2 // Copyright (c) 2004, 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.
29 namespace Boo
.Lang
.Compiler
.Steps
31 using Boo
.Lang
.Compiler
.Ast
;
32 using Boo
.Lang
.Compiler
.TypeSystem
;
35 /// AST semantic evaluation.
37 public class NormalizeIterationStatements
: AbstractTransformerCompilerStep
39 static System
.Reflection
.MethodInfo RuntimeServices_MoveNext
= Types
.RuntimeServices
.GetMethod("MoveNext");
41 static System
.Reflection
.MethodInfo RuntimeServices_GetEnumerable
= Types
.RuntimeServices
.GetMethod("GetEnumerable");
43 static System
.Reflection
.MethodInfo IEnumerable_GetEnumerator
= Types
.IEnumerable
.GetMethod("GetEnumerator");
45 static System
.Reflection
.MethodInfo IEnumerator_MoveNext
= Types
.IEnumerator
.GetMethod("MoveNext");
47 static System
.Reflection
.MethodInfo IEnumerator_get_Current
= Types
.IEnumerator
.GetProperty("Current").GetGetMethod();
49 static System
.Reflection
.MethodInfo IDisposable_Dispose
= typeof(System
.IDisposable
).GetMethod("Dispose");
53 override public void Run()
58 override public void OnMethod(Method node
)
64 override public void OnConstructor(Constructor node
)
69 override public void OnDestructor(Destructor node
)
74 override public void OnBlockExpression(BlockExpression node
)
76 // ignore closure's body since it will be visited
77 // through the closure's newly created method
80 override public void LeaveUnpackStatement(UnpackStatement node
)
82 Block body
= new Block(node
.LexicalInfo
);
83 UnpackExpression(body
, node
.Expression
, node
.Declarations
);
84 ReplaceCurrentNode(body
);
87 override public void LeaveForStatement(ForStatement node
)
89 IType enumeratorType
= GetExpressionType(node
.Iterator
);
90 IType enumeratorItemType
= TypeSystemServices
.GetEnumeratorItemType(enumeratorType
);
91 DeclarationCollection declarations
= node
.Declarations
;
92 Block body
= new Block(node
.LexicalInfo
);
94 InternalLocal iterator
= CodeBuilder
.DeclareLocal(
96 "___iterator" + _context
.AllocIndex(),
97 TypeSystemServices
.IEnumeratorType
);
99 if (TypeSystemServices
.IEnumeratorType
.IsAssignableFrom(enumeratorType
))
102 CodeBuilder
.CreateAssignment(
104 CodeBuilder
.CreateReference(iterator
),
109 // ___iterator = <node.Iterator>.GetEnumerator()
111 CodeBuilder
.CreateAssignment(
113 CodeBuilder
.CreateReference(iterator
),
114 CodeBuilder
.CreateMethodInvocation(
116 IEnumerable_GetEnumerator
)));
119 // while __iterator.MoveNext():
120 WhileStatement ws
= new WhileStatement(node
.LexicalInfo
);
121 ws
.Condition
= CodeBuilder
.CreateMethodInvocation(
122 CodeBuilder
.CreateReference(iterator
),
123 IEnumerator_MoveNext
);
125 Expression current
= CodeBuilder
.CreateMethodInvocation(
126 CodeBuilder
.CreateReference(iterator
),
127 IEnumerator_get_Current
);
129 if (1 == declarations
.Count
)
131 // item = __iterator.Current
133 CodeBuilder
.CreateAssignment(
135 CodeBuilder
.CreateReference((InternalLocal
)declarations
[0].Entity
),
140 UnpackExpression(ws
.Block
,
141 CodeBuilder
.CreateCast(
147 ws
.Block
.Add(node
.Block
);
148 ws
.OrBlock
= node
.OrBlock
;
149 ws
.ThenBlock
= node
.ThenBlock
;
154 // d = iterator as IDisposable
155 // d.Dispose() unless d is null
157 InternalLocal iteratorDisposable
= CodeBuilder
.DeclareLocal(
159 "___disposable" + _context
.AllocIndex(),
160 TypeSystemServices
.Map(typeof(System
.IDisposable
)));
161 TryStatement tryStatement
= new TryStatement();
162 tryStatement
.ProtectedBlock
.Add(ws
);
163 tryStatement
.EnsureBlock
= new Block();
165 TryCastExpression tryCastExpression
= new TryCastExpression();
166 tryCastExpression
.Type
= CodeBuilder
.CreateTypeReference(iteratorDisposable
.Type
);
167 tryCastExpression
.Target
= CodeBuilder
.CreateReference(iterator
);
168 tryCastExpression
.ExpressionType
= iteratorDisposable
.Type
;
170 tryStatement
.EnsureBlock
.Add(CodeBuilder
.CreateAssignment(
171 CodeBuilder
.CreateReference(iteratorDisposable
),
174 IfStatement ifStatement
= new IfStatement();
175 ifStatement
.Condition
= CodeBuilder
.CreateNotNullTest(CodeBuilder
.CreateReference(iteratorDisposable
));
176 ifStatement
.TrueBlock
= new Block();
177 ifStatement
.TrueBlock
.Add(CodeBuilder
.CreateMethodInvocation(CodeBuilder
.CreateReference(iteratorDisposable
), IDisposable_Dispose
));
178 tryStatement
.EnsureBlock
.Add(ifStatement
);
180 body
.Add(tryStatement
);
182 ReplaceCurrentNode(body
);
185 void UnpackExpression(Block block
, Expression expression
, DeclarationCollection declarations
)
187 UnpackExpression(CodeBuilder
, _current
, block
, expression
, declarations
);
190 public static void UnpackExpression(BooCodeBuilder codeBuilder
, Method method
, Block block
, Expression expression
, DeclarationCollection declarations
)
192 if (expression
.ExpressionType
.IsArray
)
194 UnpackArray(codeBuilder
, method
, block
, expression
, declarations
);
198 UnpackEnumerable(codeBuilder
, method
, block
, expression
, declarations
);
202 public static void UnpackEnumerable(BooCodeBuilder codeBuilder
, Method method
, Block block
, Expression expression
, DeclarationCollection declarations
)
204 TypeSystemServices tss
= codeBuilder
.TypeSystemServices
;
206 InternalLocal local
= codeBuilder
.DeclareTempLocal(method
,
207 tss
.IEnumeratorType
);
209 IType expressionType
= expression
.ExpressionType
;
211 if (expressionType
.IsSubclassOf(codeBuilder
.TypeSystemServices
.IEnumeratorType
))
214 codeBuilder
.CreateAssignment(
215 codeBuilder
.CreateReference(local
),
220 if (!expressionType
.IsSubclassOf(codeBuilder
.TypeSystemServices
.IEnumerableType
))
222 expression
= codeBuilder
.CreateMethodInvocation(
223 RuntimeServices_GetEnumerable
, expression
);
227 codeBuilder
.CreateAssignment(
229 codeBuilder
.CreateReference(local
),
230 codeBuilder
.CreateMethodInvocation(
231 expression
, IEnumerable_GetEnumerator
)));
234 for (int i
=0; i
<declarations
.Count
; ++i
)
236 Declaration declaration
= declarations
[i
];
239 codeBuilder
.CreateAssignment(
240 codeBuilder
.CreateReference(declaration
.Entity
),
241 codeBuilder
.CreateMethodInvocation(RuntimeServices_MoveNext
,
242 codeBuilder
.CreateReference(local
))));
246 public static void UnpackArray(BooCodeBuilder codeBuilder
, Method method
, Block block
, Expression expression
, DeclarationCollection declarations
)
248 ILocalEntity local
= expression
.Entity
as ILocalEntity
;
251 local
= codeBuilder
.DeclareTempLocal(method
,
252 expression
.ExpressionType
);
254 codeBuilder
.CreateAssignment(
255 codeBuilder
.CreateReference(local
),
258 for (int i
=0; i
<declarations
.Count
; ++i
)
260 Declaration declaration
= declarations
[i
];
262 codeBuilder
.CreateAssignment(
263 codeBuilder
.CreateReference(
265 codeBuilder
.CreateSlicing(
266 codeBuilder
.CreateReference(local
),