BOO-999
[boo.git] / src / Boo.Lang.Compiler / Steps / NormalizeIterationStatements.cs
blobca2e4af451b150e6a7b7ccf51f39be5964e49957
1 #region license
2 // Copyright (c) 2004, 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
29 namespace Boo.Lang.Compiler.Steps
31 using Boo.Lang.Compiler.Ast;
32 using Boo.Lang.Compiler.TypeSystem;
34 /// <summary>
35 /// AST semantic evaluation.
36 /// </summary>
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");
51 Method _current;
53 override public void Run()
55 Visit(CompileUnit);
58 override public void OnMethod(Method node)
60 _current = node;
61 Visit(node.Body);
64 override public void OnConstructor(Constructor node)
66 OnMethod(node);
69 override public void OnDestructor(Destructor node)
71 OnMethod(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(
95 _current,
96 "___iterator" + _context.AllocIndex(),
97 TypeSystemServices.IEnumeratorType);
99 if (TypeSystemServices.IEnumeratorType.IsAssignableFrom(enumeratorType))
101 body.Add(
102 CodeBuilder.CreateAssignment(
103 node.LexicalInfo,
104 CodeBuilder.CreateReference(iterator),
105 node.Iterator));
107 else
109 // ___iterator = <node.Iterator>.GetEnumerator()
110 body.Add(
111 CodeBuilder.CreateAssignment(
112 node.LexicalInfo,
113 CodeBuilder.CreateReference(iterator),
114 CodeBuilder.CreateMethodInvocation(
115 node.Iterator,
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
132 ws.Block.Add(
133 CodeBuilder.CreateAssignment(
134 node.LexicalInfo,
135 CodeBuilder.CreateReference((InternalLocal)declarations[0].Entity),
136 current));
138 else
140 UnpackExpression(ws.Block,
141 CodeBuilder.CreateCast(
142 enumeratorItemType,
143 current),
144 node.Declarations);
147 ws.Block.Add(node.Block);
148 ws.OrBlock = node.OrBlock;
149 ws.ThenBlock = node.ThenBlock;
151 // try:
152 // while...
153 // ensure:
154 // d = iterator as IDisposable
155 // d.Dispose() unless d is null
157 InternalLocal iteratorDisposable = CodeBuilder.DeclareLocal(
158 _current,
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),
172 tryCastExpression
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);
196 else
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))
213 block.Add(
214 codeBuilder.CreateAssignment(
215 codeBuilder.CreateReference(local),
216 expression));
218 else
220 if (!expressionType.IsSubclassOf(codeBuilder.TypeSystemServices.IEnumerableType))
222 expression = codeBuilder.CreateMethodInvocation(
223 RuntimeServices_GetEnumerable, expression);
226 block.Add(
227 codeBuilder.CreateAssignment(
228 block.LexicalInfo,
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];
238 block.Add(
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;
249 if (null == local)
251 local = codeBuilder.DeclareTempLocal(method,
252 expression.ExpressionType);
253 block.Add(
254 codeBuilder.CreateAssignment(
255 codeBuilder.CreateReference(local),
256 expression));
258 for (int i=0; i<declarations.Count; ++i)
260 Declaration declaration = declarations[i];
261 block.Add(
262 codeBuilder.CreateAssignment(
263 codeBuilder.CreateReference(
264 declaration.Entity),
265 codeBuilder.CreateSlicing(
266 codeBuilder.CreateReference(local),
267 i)));