BOO-503: Some love for compiler-generated callable definition names so that they...
[boo.git] / src / Boo.Lang.Compiler / Steps / GeneratorExpressionProcessor.cs
blob66734819822cec883ce19f49b3c7c804443af8b0
1 #region license
2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
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.
16 //
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.
27 #endregion
30 namespace Boo.Lang.Compiler.Steps
32 using System;
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;
46 Field _current;
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;
64 _generator = node;
65 Initialize(context);
68 public void Run()
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);
104 else
106 _sourceItemType = TypeSystemServices.ObjectType;
109 // Add base types
110 _enumerator.AddBaseType(_resultEnumeratorType);
111 _enumerator.AddBaseType(TypeSystemServices.Map(typeof(ICloneable)));
112 _enumerator.AddBaseType(TypeSystemServices.Map(typeof(IDisposable)));
114 // Add fields
115 _enumeratorField = _enumerator.AddField("$$enumerator", _sourceEnumeratorType);
116 _current = _enumerator.AddField("$$current", _resultItemType);
118 // Add methods
119 CreateReset();
120 CreateCurrent();
121 CreateMoveNext();
122 CreateClone();
123 CreateDispose();
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(
138 _enumerable.Entity);
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),
159 method);
162 void CreateCurrent()
164 Property property = _enumerator.AddReadOnlyProperty("Current", TypeSystemServices.ObjectType);
165 property.Getter.Modifiers |= TypeMemberModifiers.Virtual;
166 property.Getter.Body.Add(
167 new ReturnStatement(
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(
183 new ReturnStatement(
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));
204 void CreateClone()
206 BooMethodBuilder method = _enumerator.AddVirtualMethod("Clone", TypeSystemServices.ObjectType);
207 method.Body.Add(
208 new ReturnStatement(
209 CodeBuilder.CreateMethodInvocation(
210 CodeBuilder.CreateSelfReference(_enumerator.Entity),
211 GetMemberwiseCloneMethod())));
214 void CreateReset()
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);
221 method.Body.Add(
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;
249 stmt = istmt;
251 else
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;
260 filter = ifstmt;
262 else
264 UnlessStatement ustmt = new UnlessStatement(_generator.Filter.Condition);
265 innerBlock = ustmt.Block;
266 filter = ustmt;
269 stmt = wstmt;
272 DeclarationCollection declarations = _generator.Declarations;
273 if (declarations.Count > 1)
275 NormalizeIterationStatements.UnpackExpression(CodeBuilder,
276 method.Method,
277 outerBlock,
278 current,
279 declarations);
281 foreach (Declaration declaration in declarations)
283 method.Locals.Add(((InternalLocal)declaration.Entity).Local);
286 else
288 InternalLocal local = (InternalLocal)declarations[0].Entity;
289 method.Locals.Add(local.Local);
290 outerBlock.Add(CodeBuilder.CreateAssignment(
291 CodeBuilder.CreateReference(local),
292 current));
295 if (null != filter)
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")));
320 /// <summary>
321 /// Gets the member of the specified type with the specified name, assuming there is only one.
322 /// </summary>
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>(
353 type.GetMembers(),
354 delegate(IEntity e) {
355 return entityType == e.EntityType && e.Name == name;