1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Reflection;
22 using System.Runtime.CompilerServices;
25 namespace Microsoft.Scripting.Ast.Compiler {
27 namespace System.Linq.Expressions.Compiler {
31 /// Expression rewriting to spill the CLR stack into temporary variables
32 /// in order to guarantee some properties of code generation, for
33 /// example that we always enter try block on empty stack.
35 internal partial class StackSpiller {
37 // Is the evaluation stack empty?
43 // Should the parent nodes be rewritten, and in what way?
44 // Designed so bitwise-or produces the correct result when merging two
45 // subtrees. In particular, SpillStack is preferred over Copy which is
46 // preferred over None.
49 // None -> no rewrite needed
50 // Copy -> copy into a new node
51 // SpillStack -> spill stack into temps
53 private enum RewriteAction {
59 // Result of a rewrite operation. Always contains an action and a node.
60 private struct Result {
61 internal readonly RewriteAction Action;
62 internal readonly Expression Node;
64 internal Result(RewriteAction action, Expression node) {
71 /// The source of temporary variables
73 private readonly TempMaker _tm = new TempMaker();
76 /// Initial stack state. Normally empty, but when inlining the lambda
77 /// we might have a non-empty starting stack state.
79 private readonly Stack _startingStack;
82 /// Lambda rewrite result. We need this for inlined lambdas to figure
83 /// out whether we need to guarentee it an empty stack.
85 private RewriteAction _lambdaRewrite;
88 /// Analyzes a lambda, producing a new one that has correct invariants
89 /// for codegen. In particular, it spills the IL stack to temps in
90 /// places where it's invalid to have a non-empty stack (for example,
91 /// entering a try statement).
93 internal static LambdaExpression AnalyzeLambda(LambdaExpression lambda) {
94 return lambda.Accept(new StackSpiller(Stack.Empty));
97 private StackSpiller(Stack stack) {
98 _startingStack = stack;
101 // called by Expression<T>.Accept
102 internal Expression<T> Rewrite<T>(Expression<T> lambda) {
105 // Lambda starts with an empty stack
106 Result body = RewriteExpressionFreeTemps(lambda.Body, _startingStack);
107 _lambdaRewrite = body.Action;
111 if (body.Action != RewriteAction.None) {
112 // Create a new scope for temps
113 // (none of these will be hoisted so there is no closure impact)
114 Expression newBody = body.Node;
115 if (_tm.Temps.Count > 0) {
116 newBody = Expression.Block(_tm.Temps, newBody);
119 // Clone the lambda, replacing the body & variables
120 return new Expression<T>(newBody, lambda.Name, lambda.TailCall, lambda.Parameters);
128 [Conditional("DEBUG")]
129 private static void VerifyRewrite(Result result, Expression node) {
130 Debug.Assert(result.Node != null);
132 // (result.Action == RewriteAction.None) if and only if (node == result.Node)
133 Debug.Assert((result.Action == RewriteAction.None) ^ (node != result.Node), "rewrite action does not match node object identity");
135 // if the original node is an extension node, it should have been rewritten
136 Debug.Assert(result.Node.NodeType != ExpressionType.Extension, "extension nodes must be rewritten");
138 // if we have Copy, then node type must match
140 result.Action != RewriteAction.Copy || node.NodeType == result.Node.NodeType || node.CanReduce,
141 "rewrite action does not match node object kind"
144 // New type must be reference assignable to the old type
145 // (our rewrites preserve type exactly, but the rules for rewriting
146 // an extension node are more lenient, see Expression.ReduceAndCheck())
148 TypeUtils.AreReferenceAssignable(node.Type, result.Node.Type),
149 "rewritten object must be reference assignable to the original type"
153 private Result RewriteExpressionFreeTemps(Expression expression, Stack stack) {
155 Result result = RewriteExpression(expression, stack);
161 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "stack")]
162 private Result RewriteDynamicExpression(Expression expr, Stack stack) {
163 var node = (DynamicExpression)expr;
165 // CallSite is on the stack
166 IArgumentProvider argNode = (IArgumentProvider)node;
167 ChildRewriter cr = new ChildRewriter(this, Stack.NonEmpty, argNode.ArgumentCount);
168 cr.AddArguments(argNode);
169 if (cr.Action == RewriteAction.SpillStack) {
170 RequireNoRefArgs(node.DelegateType.GetMethod("Invoke"));
172 return cr.Finish(cr.Rewrite ? node.Rewrite(cr[0, -1]) : expr);
175 private Result RewriteIndexAssignment(BinaryExpression node, Stack stack) {
176 IndexExpression index = (IndexExpression)node.Left;
178 ChildRewriter cr = new ChildRewriter(this, stack, 2 + index.Arguments.Count);
180 cr.Add(index.Object);
181 cr.Add(index.Arguments);
184 if (cr.Action == RewriteAction.SpillStack) {
185 RequireNotRefInstance(index.Object);
189 node = new AssignBinaryExpression(
193 cr[1, -2] // arguments
199 return cr.Finish(node);
202 // BinaryExpression: AndAlso, OrElse
203 private Result RewriteLogicalBinaryExpression(Expression expr, Stack stack) {
204 BinaryExpression node = (BinaryExpression)expr;
206 // Left expression runs on a stack as left by parent
207 Result left = RewriteExpression(node.Left, stack);
208 // ... and so does the right one
209 Result right = RewriteExpression(node.Right, stack);
210 //conversion is a lambda. stack state will be ignored.
211 Result conversion = RewriteExpression(node.Conversion, stack);
213 RewriteAction action = left.Action | right.Action | conversion.Action;
214 if (action != RewriteAction.None) {
216 // We don't have to worry about byref parameters here, because the
217 // factory doesn't allow it (it requires identical parameters and
218 // return type from the AndAlso/OrElse method)
220 expr = BinaryExpression.Create(
226 (LambdaExpression)conversion.Node
229 return new Result(action, expr);
232 private Result RewriteReducibleExpression(Expression expr, Stack stack) {
233 Result result = RewriteExpression(expr.Reduce(), stack);
234 // it's at least Copy because we reduced the node
235 return new Result(result.Action | RewriteAction.Copy, result.Node);
239 private Result RewriteBinaryExpression(Expression expr, Stack stack) {
240 BinaryExpression node = (BinaryExpression)expr;
242 ChildRewriter cr = new ChildRewriter(this, stack, 3);
243 // Left expression executes on the stack as left by parent
245 // Right expression always has non-empty stack (left is on it)
247 // conversion is a lambda, stack state will be ignored
248 cr.Add(node.Conversion);
250 if (cr.Action == RewriteAction.SpillStack) {
251 RequireNoRefArgs(node.Method);
254 return cr.Finish(cr.Rewrite ?
255 BinaryExpression.Create(
261 (LambdaExpression)cr[2]) :
265 // variable assignment
266 private Result RewriteVariableAssignment(BinaryExpression node, Stack stack) {
267 // Expression is evaluated on a stack in current state
268 Result right = RewriteExpression(node.Right, stack);
269 if (right.Action != RewriteAction.None) {
270 node = Expression.Assign(node.Left, right.Node);
272 return new Result(right.Action, node);
275 private Result RewriteAssignBinaryExpression(Expression expr, Stack stack) {
276 var node = (BinaryExpression)expr;
278 switch (node.Left.NodeType) {
279 case ExpressionType.Index:
280 return RewriteIndexAssignment(node, stack);
281 case ExpressionType.MemberAccess:
282 return RewriteMemberAssignment(node, stack);
283 case ExpressionType.Parameter:
284 return RewriteVariableAssignment(node, stack);
285 case ExpressionType.Extension:
286 return RewriteExtensionAssignment(node, stack);
288 throw Error.InvalidLvalue(node.Left.NodeType);
292 private Result RewriteExtensionAssignment(BinaryExpression node, Stack stack) {
293 node = Expression.Assign(node.Left.ReduceExtensions(), node.Right);
294 Result result = RewriteAssignBinaryExpression(node, stack);
295 // it's at least Copy because we reduced the node
296 return new Result(result.Action | RewriteAction.Copy, result.Node);
300 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "stack")]
301 private static Result RewriteLambdaExpression(Expression expr, Stack stack) {
302 LambdaExpression node = (LambdaExpression)expr;
304 // Call back into the rewriter
305 expr = AnalyzeLambda(node);
307 // If the lambda gets rewritten, we don't need to spill the stack,
308 // but we do need to rebuild the tree above us so it includes the new node.
309 RewriteAction action = (expr == node) ? RewriteAction.None : RewriteAction.Copy;
311 return new Result(action, expr);
314 // ConditionalExpression
315 private Result RewriteConditionalExpression(Expression expr, Stack stack) {
316 ConditionalExpression node = (ConditionalExpression)expr;
317 // Test executes at the stack as left by parent
318 Result test = RewriteExpression(node.Test, stack);
319 // The test is popped by conditional jump so branches execute
320 // at the stack as left by parent too.
321 Result ifTrue = RewriteExpression(node.IfTrue, stack);
322 Result ifFalse = RewriteExpression(node.IfFalse, stack);
324 RewriteAction action = test.Action | ifTrue.Action | ifFalse.Action;
325 if (action != RewriteAction.None) {
326 expr = Expression.Condition(test.Node, ifTrue.Node, ifFalse.Node, node.Type);
329 return new Result(action, expr);
333 private Result RewriteMemberAssignment(BinaryExpression node, Stack stack) {
334 MemberExpression lvalue = (MemberExpression)node.Left;
336 ChildRewriter cr = new ChildRewriter(this, stack, 2);
338 // If there's an instance, it executes on the stack in current state
339 // and rest is executed on non-empty stack.
340 // Otherwise the stack is left unchaged.
341 cr.Add(lvalue.Expression);
345 if (cr.Action == RewriteAction.SpillStack) {
346 RequireNotRefInstance(lvalue.Expression);
351 new AssignBinaryExpression(
352 MemberExpression.Make(cr[0], lvalue.Member),
357 return new Result(RewriteAction.None, node);
361 private Result RewriteMemberExpression(Expression expr, Stack stack) {
362 MemberExpression node = (MemberExpression)expr;
364 // Expression is emitted on top of the stack in current state
365 Result expression = RewriteExpression(node.Expression, stack);
366 if (expression.Action != RewriteAction.None) {
367 if (expression.Action == RewriteAction.SpillStack &&
368 node.Member.MemberType == MemberTypes.Property) {
369 // Only need to validate propreties because reading a field
370 // is always side-effect free.
371 RequireNotRefInstance(node.Expression);
373 expr = MemberExpression.Make(expression.Node, node.Member);
375 return new Result(expression.Action, expr);
378 //RewriteIndexExpression
379 private Result RewriteIndexExpression(Expression expr, Stack stack) {
380 IndexExpression node = (IndexExpression)expr;
382 ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count + 1);
384 // For instance methods, the instance executes on the
385 // stack as is, but stays on the stack, making it non-empty.
387 cr.Add(node.Arguments);
389 if (cr.Action == RewriteAction.SpillStack) {
390 RequireNotRefInstance(node.Object);
394 expr = new IndexExpression(
401 return cr.Finish(expr);
404 // MethodCallExpression
405 private Result RewriteMethodCallExpression(Expression expr, Stack stack) {
406 MethodCallExpression node = (MethodCallExpression)expr;
408 ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count + 1);
410 // For instance methods, the instance executes on the
411 // stack as is, but stays on the stack, making it non-empty.
414 cr.AddArguments(node);
416 if (cr.Action == RewriteAction.SpillStack) {
417 RequireNotRefInstance(node.Object);
418 RequireNoRefArgs(node.Method);
421 return cr.Finish(cr.Rewrite ? node.Rewrite(cr[0], cr[1, -1]) : expr);
424 // NewArrayExpression
425 private Result RewriteNewArrayExpression(Expression expr, Stack stack) {
426 NewArrayExpression node = (NewArrayExpression)expr;
428 if (node.NodeType == ExpressionType.NewArrayInit) {
429 // In a case of array construction with element initialization
430 // the element expressions are never emitted on an empty stack because
431 // the array reference and the index are on the stack.
432 stack = Stack.NonEmpty;
434 // In a case of NewArrayBounds we make no modifications to the stack
435 // before emitting bounds expressions.
438 ChildRewriter cr = new ChildRewriter(this, stack, node.Expressions.Count);
439 cr.Add(node.Expressions);
442 Type element = node.Type.GetElementType();
443 if (node.NodeType == ExpressionType.NewArrayInit) {
444 expr = Expression.NewArrayInit(element, cr[0, -1]);
446 expr = Expression.NewArrayBounds(element, cr[0, -1]);
450 return cr.Finish(expr);
453 // InvocationExpression
454 private Result RewriteInvocationExpression(Expression expr, Stack stack) {
455 InvocationExpression node = (InvocationExpression)expr;
459 // See if the lambda will be inlined
460 LambdaExpression lambda = node.LambdaOperand;
461 if (lambda != null) {
462 // Arguments execute on current stack
463 cr = new ChildRewriter(this, stack, node.Arguments.Count);
464 cr.Add(node.Arguments);
466 if (cr.Action == RewriteAction.SpillStack) {
467 RequireNoRefArgs(Expression.GetInvokeMethod(node.Expression));
470 // Lambda body also executes on current stack
471 var spiller = new StackSpiller(stack);
472 lambda = lambda.Accept(spiller);
474 if (cr.Rewrite || spiller._lambdaRewrite != RewriteAction.None) {
475 node = new InvocationExpression(lambda, cr[0, -1], node.Type);
478 Result result = cr.Finish(node);
479 return new Result(result.Action | spiller._lambdaRewrite, result.Node);
482 cr = new ChildRewriter(this, stack, node.Arguments.Count + 1);
484 // first argument starts on stack as provided
485 cr.Add(node.Expression);
487 // rest of arguments have non-empty stack (delegate instance on the stack)
488 cr.Add(node.Arguments);
490 if (cr.Action == RewriteAction.SpillStack) {
491 RequireNoRefArgs(Expression.GetInvokeMethod(node.Expression));
494 return cr.Finish(cr.Rewrite ? new InvocationExpression(cr[0], cr[1, -1], node.Type) : expr);
498 private Result RewriteNewExpression(Expression expr, Stack stack) {
499 NewExpression node = (NewExpression)expr;
501 // The first expression starts on a stack as provided by parent,
502 // rest are definitely non-emtpy (which ChildRewriter guarantees)
503 ChildRewriter cr = new ChildRewriter(this, stack, node.Arguments.Count);
504 cr.AddArguments(node);
506 if (cr.Action == RewriteAction.SpillStack) {
507 RequireNoRefArgs(node.Constructor);
510 return cr.Finish(cr.Rewrite ? new NewExpression(node.Constructor, cr[0, -1], node.Members) : expr);
513 // TypeBinaryExpression
514 private Result RewriteTypeBinaryExpression(Expression expr, Stack stack) {
515 TypeBinaryExpression node = (TypeBinaryExpression)expr;
516 // The expression is emitted on top of current stack
517 Result expression = RewriteExpression(node.Expression, stack);
518 if (expression.Action != RewriteAction.None) {
519 if (node.NodeType == ExpressionType.TypeIs) {
520 expr = Expression.TypeIs(expression.Node, node.TypeOperand);
522 expr = Expression.TypeEqual(expression.Node, node.TypeOperand);
525 return new Result(expression.Action, expr);
529 private Result RewriteThrowUnaryExpression(Expression expr, Stack stack) {
530 UnaryExpression node = (UnaryExpression)expr;
532 // Throw statement itself does not care about the stack
533 // but it will empty the stack and it may cause stack misbalance
534 // it so we need to restore stack after unconditional throw to make JIT happy
535 // this has an effect of executing Throw on an empty stack.
537 Result value = RewriteExpressionFreeTemps(node.Operand, Stack.Empty);
538 RewriteAction action = value.Action;
540 if (stack != Stack.Empty) {
541 action = RewriteAction.SpillStack;
544 if (action != RewriteAction.None) {
545 expr = Expression.Throw(value.Node, node.Type);
548 return new Result(action, expr);
552 private Result RewriteUnaryExpression(Expression expr, Stack stack) {
553 UnaryExpression node = (UnaryExpression)expr;
555 Debug.Assert(node.NodeType != ExpressionType.Quote, "unexpected Quote");
556 Debug.Assert(node.NodeType != ExpressionType.Throw, "unexpected Throw");
558 // Operand is emitted on top of the stack as is
559 Result expression = RewriteExpression(node.Operand, stack);
561 if (expression.Action == RewriteAction.SpillStack) {
562 RequireNoRefArgs(node.Method);
565 if (expression.Action != RewriteAction.None) {
566 expr = new UnaryExpression(node.NodeType, expression.Node, node.Type, node.Method);
568 return new Result(expression.Action, expr);
571 // RewriteListInitExpression
572 private Result RewriteListInitExpression(Expression expr, Stack stack) {
573 ListInitExpression node = (ListInitExpression)expr;
575 //ctor runs on initial stack
576 Result newResult = RewriteExpression(node.NewExpression, stack);
577 Expression rewrittenNew = newResult.Node;
578 RewriteAction action = newResult.Action;
580 ReadOnlyCollection<ElementInit> inits = node.Initializers;
582 ChildRewriter[] cloneCrs = new ChildRewriter[inits.Count];
584 for (int i = 0; i < inits.Count; i++) {
585 ElementInit init = inits[i];
587 //initializers all run on nonempty stack
588 ChildRewriter cr = new ChildRewriter(this, Stack.NonEmpty, init.Arguments.Count);
589 cr.Add(init.Arguments);
596 case RewriteAction.None:
598 case RewriteAction.Copy:
599 ElementInit[] newInits = new ElementInit[inits.Count];
600 for (int i = 0; i < inits.Count; i++) {
601 ChildRewriter cr = cloneCrs[i];
602 if (cr.Action == RewriteAction.None) {
603 newInits[i] = inits[i];
605 newInits[i] = Expression.ElementInit(inits[i].AddMethod, cr[0, -1]);
608 expr = Expression.ListInit((NewExpression)rewrittenNew, new TrueReadOnlyCollection<ElementInit>(newInits));
610 case RewriteAction.SpillStack:
611 RequireNotRefInstance(node.NewExpression);
613 ParameterExpression tempNew = MakeTemp(rewrittenNew.Type);
614 Expression[] comma = new Expression[inits.Count + 2];
615 comma[0] = Expression.Assign(tempNew, rewrittenNew);
617 for (int i = 0; i < inits.Count; i++) {
618 ChildRewriter cr = cloneCrs[i];
619 Result add = cr.Finish(Expression.Call(tempNew, inits[i].AddMethod, cr[0, -1]));
620 comma[i + 1] = add.Node;
622 comma[inits.Count + 1] = tempNew;
623 expr = MakeBlock(comma);
626 throw ContractUtils.Unreachable;
629 return new Result(action, expr);
632 // RewriteMemberInitExpression
633 private Result RewriteMemberInitExpression(Expression expr, Stack stack) {
634 MemberInitExpression node = (MemberInitExpression)expr;
636 //ctor runs on original stack
637 Result result = RewriteExpression(node.NewExpression, stack);
638 Expression rewrittenNew = result.Node;
639 RewriteAction action = result.Action;
641 ReadOnlyCollection<MemberBinding> bindings = node.Bindings;
642 BindingRewriter[] bindingRewriters = new BindingRewriter[bindings.Count];
643 for (int i = 0; i < bindings.Count; i++) {
644 MemberBinding binding = bindings[i];
645 //bindings run on nonempty stack
646 BindingRewriter rewriter = BindingRewriter.Create(binding, this, Stack.NonEmpty);
647 bindingRewriters[i] = rewriter;
648 action |= rewriter.Action;
652 case RewriteAction.None:
654 case RewriteAction.Copy:
655 MemberBinding[] newBindings = new MemberBinding[bindings.Count];
656 for (int i = 0; i < bindings.Count; i++) {
657 newBindings[i] = bindingRewriters[i].AsBinding();
659 expr = Expression.MemberInit((NewExpression)rewrittenNew, new TrueReadOnlyCollection<MemberBinding>(newBindings));
661 case RewriteAction.SpillStack:
662 RequireNotRefInstance(node.NewExpression);
664 ParameterExpression tempNew = MakeTemp(rewrittenNew.Type);
665 Expression[] comma = new Expression[bindings.Count + 2];
666 comma[0] = Expression.Assign(tempNew, rewrittenNew);
667 for (int i = 0; i < bindings.Count; i++) {
668 BindingRewriter cr = bindingRewriters[i];
669 Expression initExpr = cr.AsExpression(tempNew);
670 comma[i + 1] = initExpr;
672 comma[bindings.Count + 1] = tempNew;
673 expr = MakeBlock(comma);
676 throw ContractUtils.Unreachable;
678 return new Result(action, expr);
686 private Result RewriteBlockExpression(Expression expr, Stack stack) {
687 BlockExpression node = (BlockExpression)expr;
689 int count = node.ExpressionCount;
690 RewriteAction action = RewriteAction.None;
691 Expression[] clone = null;
692 for (int i = 0; i < count; i++) {
693 Expression expression = node.GetExpression(i);
694 // All statements within the block execute at the
696 Result rewritten = RewriteExpression(expression, stack);
697 action |= rewritten.Action;
699 if (clone == null && rewritten.Action != RewriteAction.None) {
700 clone = Clone(node.Expressions, i);
704 clone[i] = rewritten.Node;
708 if (action != RewriteAction.None) {
709 // okay to wrap since we know no one can mutate the clone array
710 expr = node.Rewrite(null, clone);
712 return new Result(action, expr);
716 private Result RewriteLabelExpression(Expression expr, Stack stack) {
717 LabelExpression node = (LabelExpression)expr;
719 Result expression = RewriteExpression(node.DefaultValue, stack);
720 if (expression.Action != RewriteAction.None) {
721 expr = Expression.Label(node.Target, expression.Node);
723 return new Result(expression.Action, expr);
727 private Result RewriteLoopExpression(Expression expr, Stack stack) {
728 LoopExpression node = (LoopExpression)expr;
730 // The loop statement requires empty stack for itself, so it
731 // can guarantee it to the child nodes.
732 Result body = RewriteExpression(node.Body, Stack.Empty);
734 RewriteAction action = body.Action;
736 // However, the loop itself requires that it executes on an empty stack
737 // so we need to rewrite if the stack is not empty.
738 if (stack != Stack.Empty) {
739 action = RewriteAction.SpillStack;
742 if (action != RewriteAction.None) {
743 expr = new LoopExpression(body.Node, node.BreakLabel, node.ContinueLabel);
745 return new Result(action, expr);
749 // Note: goto does not necessarily need an empty stack. We could always
750 // emit it as a "leave" which would clear the stack for us. That would
751 // prevent us from doing certain optimizations we might want to do,
752 // however, like the switch-case-goto pattern. For now, be conservative
753 private Result RewriteGotoExpression(Expression expr, Stack stack) {
754 GotoExpression node = (GotoExpression)expr;
756 // Goto requires empty stack to execute so the expression is
757 // going to execute on an empty stack.
758 Result value = RewriteExpressionFreeTemps(node.Value, Stack.Empty);
760 // However, the statement itself needs an empty stack for itself
761 // so if stack is not empty, rewrite to empty the stack.
762 RewriteAction action = value.Action;
763 if (stack != Stack.Empty) {
764 action = RewriteAction.SpillStack;
767 if (action != RewriteAction.None) {
768 expr = Expression.MakeGoto(node.Kind, node.Target, value.Node, node.Type);
770 return new Result(action, expr);
774 private Result RewriteSwitchExpression(Expression expr, Stack stack) {
775 SwitchExpression node = (SwitchExpression)expr;
777 // The switch statement test is emitted on the stack in current state
778 Result switchValue = RewriteExpressionFreeTemps(node.SwitchValue, stack);
780 RewriteAction action = switchValue.Action;
781 ReadOnlyCollection<SwitchCase> cases = node.Cases;
782 SwitchCase[] clone = null;
783 for (int i = 0; i < cases.Count; i++) {
784 SwitchCase @case = cases[i];
786 Expression[] cloneTests = null;
787 ReadOnlyCollection<Expression> testValues = @case.TestValues;
788 for (int j = 0; j < testValues.Count; j++) {
789 // All tests execute at the same stack state as the switch.
790 // This is guarenteed by the compiler (to simplify spilling)
791 Result test = RewriteExpression(testValues[j], stack);
792 action |= test.Action;
794 if (cloneTests == null && test.Action != RewriteAction.None) {
795 cloneTests = Clone(testValues, j);
798 if (cloneTests != null) {
799 cloneTests[j] = test.Node;
803 // And all the cases also run on the same stack level.
804 Result body = RewriteExpression(@case.Body, stack);
805 action |= body.Action;
807 if (body.Action != RewriteAction.None || cloneTests != null) {
808 if (cloneTests != null) {
809 testValues = new ReadOnlyCollection<Expression>(cloneTests);
811 @case = new SwitchCase(body.Node, testValues);
814 clone = Clone(cases, i);
823 // default body also runs on initial stack
824 Result defaultBody = RewriteExpression(node.DefaultBody, stack);
825 action |= defaultBody.Action;
827 if (action != RewriteAction.None) {
829 // okay to wrap because we aren't modifying the array
830 cases = new ReadOnlyCollection<SwitchCase>(clone);
833 expr = new SwitchExpression(node.Type, switchValue.Node, defaultBody.Node, node.Comparison, cases);
836 return new Result(action, expr);
840 private Result RewriteTryExpression(Expression expr, Stack stack) {
841 TryExpression node = (TryExpression)expr;
843 // Try statement definitely needs an empty stack so its
844 // child nodes execute at empty stack.
845 Result body = RewriteExpression(node.Body, Stack.Empty);
846 ReadOnlyCollection<CatchBlock> handlers = node.Handlers;
847 CatchBlock[] clone = null;
849 RewriteAction action = body.Action;
850 if (handlers != null) {
851 for (int i = 0; i < handlers.Count; i++) {
852 RewriteAction curAction = body.Action;
854 CatchBlock handler = handlers[i];
856 Expression filter = handler.Filter;
857 if (handler.Filter != null) {
858 // our code gen saves the incoming filter value and provides it as a varaible so the stack is empty
859 Result rfault = RewriteExpression(handler.Filter, Stack.Empty);
860 action |= rfault.Action;
861 curAction |= rfault.Action;
862 filter = rfault.Node;
865 // Catch block starts with an empty stack (guaranteed by TryStatement)
866 Result rbody = RewriteExpression(handler.Body, Stack.Empty);
867 action |= rbody.Action;
868 curAction |= rbody.Action;
870 if (curAction != RewriteAction.None) {
871 handler = Expression.MakeCatchBlock(handler.Test, handler.Variable, rbody.Node, filter);
874 clone = Clone(handlers, i);
884 Result fault = RewriteExpression(node.Fault, Stack.Empty);
885 action |= fault.Action;
887 Result @finally = RewriteExpression(node.Finally, Stack.Empty);
888 action |= @finally.Action;
890 // If the stack is initially not empty, rewrite to spill the stack
891 if (stack != Stack.Empty) {
892 action = RewriteAction.SpillStack;
895 if (action != RewriteAction.None) {
897 // okay to wrap because we aren't modifying the array
898 handlers = new ReadOnlyCollection<CatchBlock>(clone);
901 expr = new TryExpression(node.Type, body.Node, @finally.Node, fault.Node, handlers);
903 return new Result(action, expr);
906 private Result RewriteExtensionExpression(Expression expr, Stack stack) {
907 Result result = RewriteExpression(expr.ReduceExtensions(), stack);
908 // it's at least Copy because we reduced the node
909 return new Result(result.Action | RewriteAction.Copy, result.Node);
917 /// Will clone an IList into an array of the same size, and copy
918 /// all vaues up to (and NOT including) the max index
920 /// <returns>The cloned array.</returns>
921 private static T[] Clone<T>(ReadOnlyCollection<T> original, int max) {
922 Debug.Assert(original != null);
923 Debug.Assert(max < original.Count);
925 T[] clone = new T[original.Count];
926 for (int j = 0; j < max; j++) {
927 clone[j] = original[j];
935 /// If we are spilling, requires that there are no byref arguments to
940 /// MethodCallExpression,
941 /// InvocationExpression,
942 /// DynamicExpression,
944 /// BinaryExpression.
947 /// We could support this if spilling happened later in the compiler.
948 /// Other expressions that can emit calls with arguments (such as
949 /// ListInitExpression and IndexExpression) don't allow byref arguments.
951 private static void RequireNoRefArgs(MethodBase method) {
952 if (method != null && method.GetParametersCached().Any(p => p.ParameterType.IsByRef)) {
953 throw Error.TryNotSupportedForMethodsWithRefArgs(method);
958 /// Requires that the instance is not a value type (primitive types are
959 /// okay because they're immutable).
962 /// MethodCallExpression,
963 /// MemberExpression (for properties),
965 /// ListInitExpression,
966 /// MemberInitExpression,
967 /// assign to MemberExpression,
968 /// assign to IndexExpression.
971 /// We could support this if spilling happened later in the compiler.
973 private static void RequireNotRefInstance(Expression instance) {
974 // Primitive value types are okay because they are all readonly,
975 // but we can't rely on this for non-primitive types. So we throw
977 if (instance != null && instance.Type.IsValueType && Type.GetTypeCode(instance.Type) == TypeCode.Object) {
978 throw Error.TryNotSupportedForValueTypeInstances(instance.Type);