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
32 using System
.Collections
;
33 using System
.Collections
.Generic
;
34 using System
.Diagnostics
;
35 using Boo
.Lang
.Compiler
;
36 using Boo
.Lang
.Compiler
.Ast
;
37 using Boo
.Lang
.Compiler
.TypeSystem
;
39 internal class GeneratorMethodProcessor
: AbstractTransformerCompilerStep
41 InternalMethod _generator
;
43 InternalMethod _moveNext
;
45 BooClassBuilder _enumerable
;
47 BooClassBuilder _enumerator
;
49 BooMethodBuilder _enumeratorConstructor
;
51 BooMethodBuilder _enumerableConstructor
;
57 Field _externalEnumeratorSelf
;
60 List
<TryStatementInfo
> _tryStatementInfoForLabels
= new List
<TryStatementInfo
>();
64 IType _generatorItemType
;
67 /// used for expressionless yield statements when the generator type
68 /// is a value type (and thus 'null' is not an appropriate value)
70 Field _nullValueField
;
72 public GeneratorMethodProcessor(CompilerContext context
, InternalMethod method
)
75 _mapping
= new Hashtable();
77 _generatorItemType
= (IType
)_generator
.Method
["GeneratorItemType"];
78 _enumerable
= (BooClassBuilder
)_generator
.Method
["GeneratorClassBuilder"];
79 Debug
.Assert(null != _generatorItemType
);
80 Debug
.Assert(null != _enumerable
);
84 public LexicalInfo LexicalInfo
86 get { return _generator.Method.LexicalInfo; }
89 public InternalMethod MoveNextMethod
91 get { return _moveNext; }
94 override public void Run()
96 CreateEnumerableConstructor();
98 MethodInvocationExpression enumerableConstructorInvocation
= CodeBuilder
.CreateConstructorInvocation(_enumerable
.ClassDefinition
);
99 MethodInvocationExpression enumeratorConstructorInvocation
= CodeBuilder
.CreateConstructorInvocation(_enumerator
.ClassDefinition
);
100 PropagateReferences(enumerableConstructorInvocation
, enumeratorConstructorInvocation
);
101 CreateGetEnumerator(enumeratorConstructorInvocation
);
102 FixGeneratorMethodBody(enumerableConstructorInvocation
);
105 void FixGeneratorMethodBody(MethodInvocationExpression enumerableConstructorInvocation
)
107 Block body
= _generator
.Method
.Body
;
112 _generator
.Method
.LexicalInfo
,
113 GeneratorReturnsIEnumerator()
114 ? CreateGetEnumeratorInvocation(enumerableConstructorInvocation
)
115 : enumerableConstructorInvocation
));
118 void PropagateReferences(MethodInvocationExpression enumerableConstructorInvocation
,
119 MethodInvocationExpression enumeratorConstructorInvocation
)
121 // propagate the necessary parameters from
122 // the enumerable to the enumerator
123 foreach (ParameterDeclaration parameter
in _generator
.Method
.Parameters
)
125 InternalParameter entity
= (InternalParameter
)parameter
.Entity
;
128 enumerableConstructorInvocation
.Arguments
.Add(
129 CodeBuilder
.CreateReference(parameter
));
131 PropagateFromEnumerableToEnumerator(enumeratorConstructorInvocation
,
137 // propagate the external self reference if necessary
138 if (null != _externalEnumeratorSelf
)
140 IType type
= (IType
)_externalEnumeratorSelf
.Type
.Entity
;
141 enumerableConstructorInvocation
.Arguments
.Add(CodeBuilder
.CreateSelfReference(type
));
143 PropagateFromEnumerableToEnumerator(enumeratorConstructorInvocation
,
150 private MethodInvocationExpression
CreateGetEnumeratorInvocation(MethodInvocationExpression enumerableConstructorInvocation
)
152 return CodeBuilder
.CreateMethodInvocation(
153 enumerableConstructorInvocation
,
154 GetGetEnumeratorEntity());
157 private InternalMethod
GetGetEnumeratorEntity()
159 return GetGetEnumeratorBuilder().Entity
;
162 private bool GeneratorReturnsIEnumerator()
164 bool returnsEnumerator
= _generator
.ReturnType
== TypeSystemServices
.IEnumeratorType
;
166 _generator
.ReturnType
.ConstructedInfo
!= null &&
167 _generator
.ReturnType
.ConstructedInfo
.GenericDefinition
== TypeSystemServices
.IEnumeratorGenericType
;
169 return returnsEnumerator
;
172 void CreateGetEnumerator(Expression enumeratorExpression
)
174 BooMethodBuilder method
= GetGetEnumeratorBuilder();
175 method
.Body
.Add(new ReturnStatement(enumeratorExpression
));
178 private BooMethodBuilder
GetGetEnumeratorBuilder()
180 return (BooMethodBuilder
)_generator
.Method
["GetEnumeratorBuilder"];
183 void CreateEnumerableConstructor()
185 _enumerableConstructor
= CreateConstructor(_enumerable
);
188 void CreateEnumeratorConstructor()
190 _enumeratorConstructor
= CreateConstructor(_enumerator
);
193 void CreateEnumerator()
195 IType abstractEnumeratorType
=
196 TypeSystemServices
.Map(typeof(Boo
.Lang
.GenericGeneratorEnumerator
<>)).
197 GenericInfo
.ConstructType(new IType
[] {_generatorItemType}
);
199 _state
= NameResolutionService
.ResolveField(abstractEnumeratorType
, "_state");
200 _yield
= NameResolutionService
.ResolveMethod(abstractEnumeratorType
, "Yield");
202 _enumerator
= CodeBuilder
.CreateClass("$");
203 _enumerator
.AddAttribute(CodeBuilder
.CreateAttribute(typeof(System
.Runtime
.CompilerServices
.CompilerGeneratedAttribute
)));
204 _enumerator
.Modifiers
|= TypeMemberModifiers
.Final
;
205 _enumerator
.LexicalInfo
= this.LexicalInfo
;
206 _enumerator
.AddBaseType(abstractEnumeratorType
);
207 _enumerator
.AddBaseType(TypeSystemServices
.IEnumeratorType
);
209 CreateEnumeratorConstructor();
212 _enumerable
.ClassDefinition
.Members
.Add(_enumerator
.ClassDefinition
);
213 //new Boo.Lang.Compiler.Ast.Visitors.BooPrinterVisitor(System.Console.Out).Visit(_enumerator.ClassDefinition);
216 void CreateMoveNext()
218 Method generator
= _generator
.Method
;
220 BooMethodBuilder methodBuilder
= _enumerator
.AddVirtualMethod("MoveNext", TypeSystemServices
.BoolType
);
221 methodBuilder
.Method
.LexicalInfo
= generator
.LexicalInfo
;
222 _moveNext
= methodBuilder
.Entity
;
224 foreach (Local local
in generator
.Locals
)
226 InternalLocal entity
= (InternalLocal
)local
.Entity
;
228 Field field
= _enumerator
.AddInternalField("$" + entity
.Name
+ "$" + _context
.AllocIndex(), entity
.Type
);
229 _mapping
[entity
] = field
.Entity
;
231 generator
.Locals
.Clear();
233 foreach (ParameterDeclaration parameter
in generator
.Parameters
)
235 InternalParameter entity
= (InternalParameter
)parameter
.Entity
;
238 Field field
= DeclareFieldInitializedFromConstructorParameter(_enumerator
, _enumeratorConstructor
,
241 _mapping
[entity
] = field
.Entity
;
245 methodBuilder
.Body
.Add(CreateLabel(generator
));
246 // Visit() needs to know the number of the finished state
247 _finishedStateNumber
= _labels
.Count
;
248 LabelStatement finishedLabel
= CreateLabel(generator
);
249 methodBuilder
.Body
.Add(generator
.Body
);
250 generator
.Body
.Clear();
252 Visit(methodBuilder
.Body
);
254 methodBuilder
.Body
.Add(CreateYieldInvocation(null, _finishedStateNumber
));
255 methodBuilder
.Body
.Add(finishedLabel
);
257 methodBuilder
.Body
.Insert(0,
258 CodeBuilder
.CreateSwitch(
260 CodeBuilder
.CreateMemberReference(_state
),
263 // if the method contains converted try statements, put it in a try/failure block
264 if (_convertedTryStatements
.Count
> 0)
266 IMethod dispose
= CreateDisposeMethod();
268 TryStatement tryFailure
= new TryStatement();
269 tryFailure
.ProtectedBlock
.Add(methodBuilder
.Body
);
270 tryFailure
.FailureBlock
= new Block();
271 tryFailure
.FailureBlock
.Add(CallMethodOnSelf(dispose
));
272 methodBuilder
.Body
.Clear();
273 methodBuilder
.Body
.Add(tryFailure
);
277 MethodInvocationExpression
CallMethodOnSelf(IMethod method
)
279 return CodeBuilder
.CreateMethodInvocation(
280 CodeBuilder
.CreateSelfReference(_enumerator
.Entity
),
284 IMethod
CreateDisposeMethod()
286 BooMethodBuilder mn
= _enumerator
.AddVirtualMethod("Dispose", TypeSystemServices
.VoidType
);
287 mn
.Method
.LexicalInfo
= this.LexicalInfo
;
289 LabelStatement noEnsure
= CodeBuilder
.CreateLabel(_generator
.Method
, "noEnsure").LabelStatement
;
290 mn
.Body
.Add(noEnsure
);
291 mn
.Body
.Add(SetStateTo(_finishedStateNumber
));
292 mn
.Body
.Add(new ReturnStatement());
294 // Create a section calling all ensure methods for each converted try block
295 LabelStatement
[] disposeLabels
= new LabelStatement
[_labels
.Count
];
296 for (int i
= 0; i
< _convertedTryStatements
.Count
; i
++) {
297 TryStatementInfo info
= _convertedTryStatements
[i
];
298 disposeLabels
[info
._stateNumber
] = CodeBuilder
.CreateLabel(_generator
.Method
, "$ensure_" + info
._stateNumber
).LabelStatement
;
299 mn
.Body
.Add(disposeLabels
[info
._stateNumber
]);
300 mn
.Body
.Add(SetStateTo(_finishedStateNumber
));
301 Block block
= mn
.Body
;
302 while (info
._parent
!= null) {
303 TryStatement ts
= new TryStatement();
305 ts
.ProtectedBlock
.Add(CallMethodOnSelf(info
._ensureMethod
));
306 block
= ts
.EnsureBlock
= new Block();
309 block
.Add(CallMethodOnSelf(info
._ensureMethod
));
310 mn
.Body
.Add(new ReturnStatement());
313 // now map the labels of the suspended states to the labels we just created
314 for (int i
= 0; i
< _labels
.Count
; i
++) {
315 if (_tryStatementInfoForLabels
[i
] == null)
316 disposeLabels
[i
] = noEnsure
;
318 disposeLabels
[i
] = disposeLabels
[_tryStatementInfoForLabels
[i
]._stateNumber
];
321 mn
.Body
.Insert(0, CodeBuilder
.CreateSwitch(
323 CodeBuilder
.CreateMemberReference(_state
),
328 void PropagateFromEnumerableToEnumerator(MethodInvocationExpression enumeratorConstructorInvocation
,
329 string parameterName
,
332 Field field
= DeclareFieldInitializedFromConstructorParameter(_enumerable
, _enumerableConstructor
, parameterName
, parameterType
);
333 enumeratorConstructorInvocation
.Arguments
.Add(
334 CodeBuilder
.CreateReference(field
));
337 Field
DeclareFieldInitializedFromConstructorParameter(BooClassBuilder type
,
338 BooMethodBuilder constructor
,
339 string parameterName
,
342 Field field
= type
.AddInternalField("$" + parameterName
+ _context
.AllocIndex(), parameterType
);
343 InitializeFieldFromConstructorParameter(constructor
, field
, parameterName
, parameterType
);
347 void InitializeFieldFromConstructorParameter(BooMethodBuilder constructor
,
349 string parameterName
,
352 ParameterDeclaration parameter
= constructor
.AddParameter(parameterName
, parameterType
);
353 constructor
.Body
.Add(
354 CodeBuilder
.CreateAssignment(
355 CodeBuilder
.CreateReference(field
),
356 CodeBuilder
.CreateReference(parameter
)));
359 override public void OnReferenceExpression(ReferenceExpression node
)
361 InternalField mapped
= (InternalField
)_mapping
[node
.Entity
];
365 CodeBuilder
.CreateMemberReference(
367 CodeBuilder
.CreateSelfReference(_enumerator
.Entity
),
372 override public void OnSelfLiteralExpression(SelfLiteralExpression node
)
374 if (null == _externalEnumeratorSelf
)
376 IType type
= node
.ExpressionType
;
377 _externalEnumeratorSelf
= DeclareFieldInitializedFromConstructorParameter(
379 _enumeratorConstructor
,
384 ReplaceCurrentNode(CodeBuilder
.CreateReference(node
.LexicalInfo
, _externalEnumeratorSelf
));
387 class TryStatementInfo
389 internal TryStatement _statement
;
390 internal TryStatementInfo _parent
;
392 internal bool _containsYield
;
393 internal int _stateNumber
= -1;
394 internal Block _replacement
;
396 internal IMethod _ensureMethod
;
399 List
<TryStatementInfo
> _convertedTryStatements
= new List
<TryStatementInfo
>();
400 Stack
<TryStatementInfo
> _tryStatementStack
= new Stack
<TryStatementInfo
>();
401 int _finishedStateNumber
;
403 public override bool EnterTryStatement(TryStatement node
)
405 TryStatementInfo info
= new TryStatementInfo();
406 info
._statement
= node
;
407 if (_tryStatementStack
.Count
> 0)
408 info
._parent
= _tryStatementStack
.Peek();
409 _tryStatementStack
.Push(info
);
413 BinaryExpression
SetStateTo(int num
)
415 return CodeBuilder
.CreateAssignment(CodeBuilder
.CreateMemberReference(_state
),
416 CodeBuilder
.CreateIntegerLiteral(num
));
419 public override void LeaveTryStatement(TryStatement node
)
421 TryStatementInfo info
= _tryStatementStack
.Pop();
422 if (info
._containsYield
) {
423 ReplaceCurrentNode(info
._replacement
);
424 TryStatementInfo currentTry
= (_tryStatementStack
.Count
> 0) ? _tryStatementStack
.Peek() : null;
425 info
._replacement
.Add(node
.ProtectedBlock
);
426 if (currentTry
!= null) {
427 ConvertTryStatement(currentTry
);
428 info
._replacement
.Add(SetStateTo(currentTry
._stateNumber
));
430 // leave try block, reset state to prevent ensure block from being called again
431 info
._replacement
.Add(SetStateTo(_finishedStateNumber
));
433 BooMethodBuilder ensureMethod
= _enumerator
.AddMethod("$ensure" + info
._stateNumber
, TypeSystemServices
.VoidType
, TypeMemberModifiers
.Private
);
434 ensureMethod
.Body
.Add(info
._statement
.EnsureBlock
);
435 info
._ensureMethod
= ensureMethod
.Entity
;
436 info
._replacement
.Add(CallMethodOnSelf(ensureMethod
.Entity
));
437 _convertedTryStatements
.Add(info
);
441 void ConvertTryStatement(TryStatementInfo currentTry
)
443 if (currentTry
._containsYield
)
445 currentTry
._containsYield
= true;
446 currentTry
._stateNumber
= _labels
.Count
;
447 Block tryReplacement
= new Block();
448 //tryReplacement.Add(CreateLabel(tryReplacement));
449 // when the MoveNext() is called while the enumerator is still in running state, don't jump to the
450 // try block, but handle it like MoveNext() calls when the enumerator is in the finished state.
451 _labels
.Add(_labels
[_finishedStateNumber
]);
452 _tryStatementInfoForLabels
.Add(currentTry
);
453 tryReplacement
.Add(SetStateTo(currentTry
._stateNumber
));
454 currentTry
._replacement
= tryReplacement
;
457 override public void LeaveYieldStatement(YieldStatement node
)
459 TryStatementInfo currentTry
= (_tryStatementStack
.Count
> 0) ? _tryStatementStack
.Peek() : null;
460 if (currentTry
!= null) {
461 ConvertTryStatement(currentTry
);
463 Block block
= new Block();
467 CreateYieldInvocation(node
.Expression
, _labels
.Count
),
469 block
.Add(CreateLabel(node
));
470 // setting the state back to the "running" state not required, as that state has the same ensure blocks
471 // as the state we are currently in.
472 // if (currentTry != null) {
473 // block.Add(SetStateTo(currentTry._stateNumber));
475 ReplaceCurrentNode(block
);
478 MethodInvocationExpression
CreateYieldInvocation(Expression
value, int newState
)
480 MethodInvocationExpression invocation
= CodeBuilder
.CreateMethodInvocation(
481 CodeBuilder
.CreateSelfReference(_enumerator
.Entity
),
483 CodeBuilder
.CreateIntegerLiteral(newState
),
484 value == null ? GetDefaultYieldValue() : value);
485 if (null != value) invocation
.LexicalInfo
= value.LexicalInfo
;
489 private Expression
GetDefaultYieldValue()
491 if (_generatorItemType
.IsValueType
)
493 if (null == _nullValueField
)
495 _nullValueField
= _enumerator
.AddField("$empty", _generatorItemType
);
497 return CodeBuilder
.CreateReference(_nullValueField
);
499 return new NullLiteralExpression();
502 LabelStatement
CreateLabel(Node sourceNode
)
504 InternalLabel label
= CodeBuilder
.CreateLabel(sourceNode
, "$state$" + _labels
.Count
);
505 _labels
.Add(label
.LabelStatement
);
506 _tryStatementInfoForLabels
.Add(_tryStatementStack
.Count
> 0 ? _tryStatementStack
.Peek() : null);
507 return label
.LabelStatement
;
510 BooMethodBuilder
CreateConstructor(BooClassBuilder builder
)
512 BooMethodBuilder constructor
= builder
.AddConstructor();
513 constructor
.Body
.Add(CodeBuilder
.CreateSuperConstructorInvocation(builder
.Entity
.BaseType
));