2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 namespace Boo
.Lang
.Compiler
.Steps
33 using System
.Collections
;
34 using Boo
.Lang
.Compiler
;
35 using Boo
.Lang
.Compiler
.Ast
;
36 using Boo
.Lang
.Compiler
.TypeSystem
;
38 class GeneratorExpressionProcessor
: AbstractCompilerComponent
40 GeneratorExpression _generator
;
42 BooClassBuilder _enumerable
;
44 BooClassBuilder _enumerator
;
48 Field _enumeratorField
;
50 ForeignReferenceCollector _collector
;
52 IType _sourceItemType
;
53 IType _sourceEnumeratorType
;
54 IType _sourceEnumerableType
;
56 IType _resultItemType
;
57 IType _resultEnumeratorType
;
59 public GeneratorExpressionProcessor(CompilerContext context
,
60 ForeignReferenceCollector collector
,
61 GeneratorExpression node
)
63 _collector
= collector
;
70 RemoveReferencedDeclarations();
71 CreateAnonymousGeneratorType();
74 void RemoveReferencedDeclarations()
76 Hash referencedEntities
= _collector
.ReferencedEntities
;
77 foreach (Declaration d
in _generator
.Declarations
)
79 referencedEntities
.Remove(d
.Entity
);
83 void CreateAnonymousGeneratorType()
85 _enumerable
= (BooClassBuilder
)_generator
["GeneratorClassBuilder"];
87 // Set up some important types
88 _sourceItemType
= TypeSystemServices
.ObjectType
;
89 _sourceEnumeratorType
= TypeSystemServices
.IEnumeratorType
;
90 _sourceEnumerableType
= TypeSystemServices
.IEnumerableType
;
92 _resultItemType
= (IType
)_generator
["GeneratorItemType"];
93 _resultEnumeratorType
= TypeSystemServices
.IEnumeratorGenericType
.GenericInfo
.ConstructType(_resultItemType
);
95 _enumerator
= _collector
.CreateSkeletonClass("Enumerator",_generator
.LexicalInfo
);
97 // use a generic enumerator for the source type if possible
98 _sourceItemType
= TypeSystemServices
.GetGenericEnumerableItemType(_generator
.Iterator
.ExpressionType
);
99 if (_sourceItemType
!= null && _sourceItemType
!= TypeSystemServices
.ObjectType
)
101 _sourceEnumerableType
= TypeSystemServices
.IEnumerableGenericType
.GenericInfo
.ConstructType(_sourceItemType
);
102 _sourceEnumeratorType
= TypeSystemServices
.IEnumeratorGenericType
.GenericInfo
.ConstructType(_sourceItemType
);
106 _sourceItemType
= TypeSystemServices
.ObjectType
;
110 _enumerator
.AddBaseType(_resultEnumeratorType
);
111 _enumerator
.AddBaseType(TypeSystemServices
.Map(typeof(ICloneable
)));
112 _enumerator
.AddBaseType(TypeSystemServices
.Map(typeof(IDisposable
)));
115 _enumeratorField
= _enumerator
.AddField("$$enumerator", _sourceEnumeratorType
);
116 _current
= _enumerator
.AddField("$$current", _resultItemType
);
125 EnumeratorConstructorMustCallReset();
127 _collector
.AdjustReferences();
129 _collector
.DeclareFieldsAndConstructor(_enumerable
);
131 CreateGetEnumerator();
132 _enumerable
.ClassDefinition
.Members
.Add(_enumerator
.ClassDefinition
);
135 public MethodInvocationExpression
CreateEnumerableConstructorInvocation()
137 return _collector
.CreateConstructorInvocationWithReferencedEntities(
141 void EnumeratorConstructorMustCallReset()
143 Constructor constructor
= _enumerator
.ClassDefinition
.GetConstructor(0);
144 constructor
.Body
.Add(CreateMethodInvocation(_enumerator
.ClassDefinition
, "Reset"));
147 IMethod
GetMemberwiseCloneMethod()
149 return TypeSystemServices
.Map(
150 typeof(object).GetMethod("MemberwiseClone",
151 System
.Reflection
.BindingFlags
.NonPublic
|System
.Reflection
.BindingFlags
.Instance
));
154 MethodInvocationExpression
CreateMethodInvocation(ClassDefinition cd
, string name
)
156 IMethod method
= (IMethod
)((Method
)cd
.Members
[name
]).Entity
;
157 return CodeBuilder
.CreateMethodInvocation(
158 CodeBuilder
.CreateSelfReference(method
.DeclaringType
),
164 Property property
= _enumerator
.AddReadOnlyProperty("Current", TypeSystemServices
.ObjectType
);
165 property
.Getter
.Modifiers
|= TypeMemberModifiers
.Virtual
;
166 property
.Getter
.Body
.Add(
168 CodeBuilder
.CreateReference(_current
)));
170 // If item type is object, we're done
171 if (_resultItemType
== TypeSystemServices
.ObjectType
) return;
173 // Since enumerator is generic, this object-typed property should be the
174 // explicit interface implementation for the non-generic IEnumerator interface
175 property
.ExplicitInfo
= new ExplicitMemberInfo();
176 property
.ExplicitInfo
.InterfaceType
=
177 (SimpleTypeReference
)CodeBuilder
.CreateTypeReference(TypeSystemServices
.IEnumeratorType
);
179 // ...and now we create a typed property for the generic IEnumerator<> interface
180 Property typedProperty
= _enumerator
.AddReadOnlyProperty("Current", _resultItemType
);
181 typedProperty
.Getter
.Modifiers
|= TypeMemberModifiers
.Virtual
;
182 typedProperty
.Getter
.Body
.Add(
184 CodeBuilder
.CreateReference(_current
)));
187 void CreateGetEnumerator()
189 BooMethodBuilder method
= (BooMethodBuilder
)_generator
["GetEnumeratorBuilder"];
191 MethodInvocationExpression mie
= CodeBuilder
.CreateConstructorInvocation(_enumerator
.ClassDefinition
);
192 foreach (TypeMember member
in _enumerable
.ClassDefinition
.Members
)
194 if (NodeType
.Field
== member
.NodeType
)
196 IField field
= (IField
)member
.Entity
;
197 mie
.Arguments
.Add(CodeBuilder
.CreateMemberReference(field
));
201 method
.Body
.Add(new ReturnStatement(mie
));
206 BooMethodBuilder method
= _enumerator
.AddVirtualMethod("Clone", TypeSystemServices
.ObjectType
);
209 CodeBuilder
.CreateMethodInvocation(
210 CodeBuilder
.CreateSelfReference(_enumerator
.Entity
),
211 GetMemberwiseCloneMethod())));
216 // Find GetEnumerator method on the source type
217 IMethod getEnumerator
= (IMethod
)GetMember(_sourceEnumerableType
, "GetEnumerator", EntityType
.Method
);
219 // Build Reset method that calls GetEnumerator on the source
220 BooMethodBuilder method
= _enumerator
.AddVirtualMethod("Reset", TypeSystemServices
.VoidType
);
222 CodeBuilder
.CreateAssignment(
223 CodeBuilder
.CreateReference((InternalField
)_enumeratorField
.Entity
),
224 CodeBuilder
.CreateMethodInvocation(_generator
.Iterator
, getEnumerator
)));
227 void CreateMoveNext()
229 BooMethodBuilder method
= _enumerator
.AddVirtualMethod("MoveNext", TypeSystemServices
.BoolType
);
231 Expression moveNext
= CodeBuilder
.CreateMethodInvocation(
232 CodeBuilder
.CreateReference((InternalField
)_enumeratorField
.Entity
),
233 TypeSystemServices
.Map(Types
.IEnumerator
.GetMethod("MoveNext")));
235 Expression current
= CodeBuilder
.CreateMethodInvocation(
236 CodeBuilder
.CreateReference((InternalField
)_enumeratorField
.Entity
),
237 ((IProperty
)GetMember(_sourceEnumeratorType
, "Current", EntityType
.Property
)).GetGetMethod());
239 Statement filter
= null;
240 Statement stmt
= null;
241 Block outerBlock
= null;
242 Block innerBlock
= null;
244 if (null == _generator
.Filter
)
246 IfStatement istmt
= new IfStatement(moveNext
, new Block(), null);
247 outerBlock
= innerBlock
= istmt
.TrueBlock
;
253 WhileStatement wstmt
= new WhileStatement(moveNext
);
254 outerBlock
= wstmt
.Block
;
256 if (StatementModifierType
.If
== _generator
.Filter
.Type
)
258 IfStatement ifstmt
= new IfStatement(_generator
.Filter
.Condition
, new Block(), null);
259 innerBlock
= ifstmt
.TrueBlock
;
264 UnlessStatement ustmt
= new UnlessStatement(_generator
.Filter
.Condition
);
265 innerBlock
= ustmt
.Block
;
272 DeclarationCollection declarations
= _generator
.Declarations
;
273 if (declarations
.Count
> 1)
275 NormalizeIterationStatements
.UnpackExpression(CodeBuilder
,
281 foreach (Declaration declaration
in declarations
)
283 method
.Locals
.Add(((InternalLocal
)declaration
.Entity
).Local
);
288 InternalLocal local
= (InternalLocal
)declarations
[0].Entity
;
289 method
.Locals
.Add(local
.Local
);
290 outerBlock
.Add(CodeBuilder
.CreateAssignment(
291 CodeBuilder
.CreateReference(local
),
297 outerBlock
.Add(filter
);
300 innerBlock
.Add(CodeBuilder
.CreateAssignment(
301 CodeBuilder
.CreateReference((InternalField
)_current
.Entity
),
302 _generator
.Expression
));
303 innerBlock
.Add(new ReturnStatement(new BoolLiteralExpression(true)));
305 method
.Body
.Add(stmt
);
306 method
.Body
.Add(new ReturnStatement(new BoolLiteralExpression(false)));
309 private void CreateDispose()
311 BooMethodBuilder dispose
= _enumerator
.AddVirtualMethod("Dispose", TypeSystemServices
.VoidType
);
312 if (TypeSystemServices
.Map(typeof(IDisposable
)).IsAssignableFrom(_sourceEnumeratorType
))
314 dispose
.Body
.Add(CodeBuilder
.CreateMethodInvocation(
315 CodeBuilder
.CreateReference(_enumeratorField
),
316 typeof(IDisposable
).GetMethod("Dispose")));
321 /// Gets the member of the specified type with the specified name, assuming there is only one.
323 private IEntity
GetMember(IType type
, string name
, EntityType entityType
)
325 // For external types we can use GetMethod or GetProperty to optimize things a little
326 ExternalType external
= type
as ExternalType
;
327 if (external
!= null)
329 if (entityType
== EntityType
.Property
)
331 return TypeSystemServices
.Map(
332 ((ExternalType
)type
).ActualType
.GetProperty(name
));
335 else if (entityType
== EntityType
.Method
)
337 return TypeSystemServices
.Map(
338 ((ExternalType
)type
).ActualType
.GetMethod(name
));
343 // For constructed types which aren't external we can use the GenericMapping to
344 // (maybe) optimize things a little
345 if (type
.ConstructedInfo
!= null)
347 return ((GenericConstructedType
)type
).GenericMapping
.Map(
348 GetMember(type
.ConstructedInfo
.GenericDefinition
, name
, entityType
));
351 // For other cases we just scan through the members collection
352 return Array
.Find
<IEntity
>(
354 delegate(IEntity e
) {
355 return entityType
== e
.EntityType
&& e
.Name
== name
;