2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@gnome.org)
8 // (C) 2001, 2002 Ximian, Inc.
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Diagnostics;
17 namespace Mono.CSharp {
19 using System.Collections;
21 public abstract class Statement {
25 /// Resolves the statement, true means that all sub-statements
28 public virtual bool Resolve (EmitContext ec)
34 /// Return value indicates whether all code paths emitted return.
36 protected abstract bool DoEmit (EmitContext ec);
39 /// Return value indicates whether all code paths emitted return.
41 public virtual bool Emit (EmitContext ec)
48 /// Encapsulates the emission of a boolean test and jumping to a
51 /// This will emit the bool expression in `bool_expr' and if
52 /// `target_is_for_true' is true, then the code will generate a
53 /// brtrue to the target. Otherwise a brfalse.
55 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
56 Label target, bool target_is_for_true)
58 ILGenerator ig = ec.ig;
61 if (bool_expr is Unary){
62 Unary u = (Unary) bool_expr;
64 if (u.Oper == Unary.Operator.LogicalNot){
67 u.EmitLogicalNot (ec);
69 } else if (bool_expr is Binary){
70 Binary b = (Binary) bool_expr;
72 if (b.EmitBranchable (ec, target, target_is_for_true))
79 if (target_is_for_true){
81 ig.Emit (OpCodes.Brfalse, target);
83 ig.Emit (OpCodes.Brtrue, target);
86 ig.Emit (OpCodes.Brtrue, target);
88 ig.Emit (OpCodes.Brfalse, target);
92 public static void Warning_DeadCodeFound (Location loc)
94 Report.Warning (162, loc, "Unreachable code detected");
98 public class EmptyStatement : Statement {
99 public override bool Resolve (EmitContext ec)
104 protected override bool DoEmit (EmitContext ec)
110 public class If : Statement {
112 public Statement TrueStatement;
113 public Statement FalseStatement;
115 public If (Expression expr, Statement trueStatement, Location l)
118 TrueStatement = trueStatement;
122 public If (Expression expr,
123 Statement trueStatement,
124 Statement falseStatement,
128 TrueStatement = trueStatement;
129 FalseStatement = falseStatement;
133 public override bool Resolve (EmitContext ec)
135 Report.Debug (1, "START IF BLOCK", loc);
137 expr = Expression.ResolveBoolean (ec, expr, loc);
142 ec.StartFlowBranching (FlowBranchingType.BLOCK, loc);
144 if (!TrueStatement.Resolve (ec)) {
145 ec.KillFlowBranching ();
149 ec.CurrentBranching.CreateSibling ();
151 if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
152 ec.KillFlowBranching ();
156 ec.EndFlowBranching ();
158 Report.Debug (1, "END IF BLOCK", loc);
163 protected override bool DoEmit (EmitContext ec)
165 ILGenerator ig = ec.ig;
166 Label false_target = ig.DefineLabel ();
168 bool is_true_ret, is_false_ret;
171 // Dead code elimination
173 if (expr is BoolConstant){
174 bool take = ((BoolConstant) expr).Value;
177 if (FalseStatement != null){
178 Warning_DeadCodeFound (FalseStatement.loc);
180 return TrueStatement.Emit (ec);
182 Warning_DeadCodeFound (TrueStatement.loc);
183 if (FalseStatement != null)
184 return FalseStatement.Emit (ec);
188 EmitBoolExpression (ec, expr, false_target, false);
190 is_true_ret = TrueStatement.Emit (ec);
191 is_false_ret = is_true_ret;
193 if (FalseStatement != null){
194 bool branch_emitted = false;
196 end = ig.DefineLabel ();
198 ig.Emit (OpCodes.Br, end);
199 branch_emitted = true;
202 ig.MarkLabel (false_target);
203 is_false_ret = FalseStatement.Emit (ec);
208 ig.MarkLabel (false_target);
209 is_false_ret = false;
212 return is_true_ret && is_false_ret;
216 public class Do : Statement {
217 public Expression expr;
218 public readonly Statement EmbeddedStatement;
219 bool infinite, may_return;
221 public Do (Statement statement, Expression boolExpr, Location l)
224 EmbeddedStatement = statement;
228 public override bool Resolve (EmitContext ec)
232 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
234 if (!EmbeddedStatement.Resolve (ec))
237 expr = Expression.ResolveBoolean (ec, expr, loc);
240 else if (expr is BoolConstant){
241 bool res = ((BoolConstant) expr).Value;
247 ec.CurrentBranching.Infinite = infinite;
248 FlowReturns returns = ec.EndFlowBranching ();
249 may_return = returns != FlowReturns.NEVER;
254 protected override bool DoEmit (EmitContext ec)
256 ILGenerator ig = ec.ig;
257 Label loop = ig.DefineLabel ();
258 Label old_begin = ec.LoopBegin;
259 Label old_end = ec.LoopEnd;
260 bool old_inloop = ec.InLoop;
261 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
263 ec.LoopBegin = ig.DefineLabel ();
264 ec.LoopEnd = ig.DefineLabel ();
266 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
269 EmbeddedStatement.Emit (ec);
270 ig.MarkLabel (ec.LoopBegin);
273 // Dead code elimination
275 if (expr is BoolConstant){
276 bool res = ((BoolConstant) expr).Value;
279 ec.ig.Emit (OpCodes.Br, loop);
281 EmitBoolExpression (ec, expr, loop, true);
283 ig.MarkLabel (ec.LoopEnd);
285 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
286 ec.LoopBegin = old_begin;
287 ec.LoopEnd = old_end;
288 ec.InLoop = old_inloop;
291 return may_return == false;
297 public class While : Statement {
298 public Expression expr;
299 public readonly Statement Statement;
300 bool may_return, empty, infinite;
302 public While (Expression boolExpr, Statement statement, Location l)
304 this.expr = boolExpr;
305 Statement = statement;
309 public override bool Resolve (EmitContext ec)
313 expr = Expression.ResolveBoolean (ec, expr, loc);
317 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
320 // Inform whether we are infinite or not
322 if (expr is BoolConstant){
323 BoolConstant bc = (BoolConstant) expr;
325 if (bc.Value == false){
326 Warning_DeadCodeFound (Statement.loc);
332 // We are not infinite, so the loop may or may not be executed.
334 ec.CurrentBranching.CreateSibling ();
337 if (!Statement.Resolve (ec))
341 ec.KillFlowBranching ();
343 ec.CurrentBranching.Infinite = infinite;
344 FlowReturns returns = ec.EndFlowBranching ();
345 may_return = returns != FlowReturns.NEVER;
351 protected override bool DoEmit (EmitContext ec)
356 ILGenerator ig = ec.ig;
357 Label old_begin = ec.LoopBegin;
358 Label old_end = ec.LoopEnd;
359 bool old_inloop = ec.InLoop;
360 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
363 ec.LoopBegin = ig.DefineLabel ();
364 ec.LoopEnd = ig.DefineLabel ();
366 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
369 // Inform whether we are infinite or not
371 if (expr is BoolConstant){
372 BoolConstant bc = (BoolConstant) expr;
374 ig.MarkLabel (ec.LoopBegin);
376 ig.Emit (OpCodes.Br, ec.LoopBegin);
379 // Inform that we are infinite (ie, `we return'), only
380 // if we do not `break' inside the code.
382 ret = may_return == false;
383 ig.MarkLabel (ec.LoopEnd);
385 Label while_loop = ig.DefineLabel ();
387 ig.Emit (OpCodes.Br, ec.LoopBegin);
388 ig.MarkLabel (while_loop);
392 ig.MarkLabel (ec.LoopBegin);
394 EmitBoolExpression (ec, expr, while_loop, true);
395 ig.MarkLabel (ec.LoopEnd);
400 ec.LoopBegin = old_begin;
401 ec.LoopEnd = old_end;
402 ec.InLoop = old_inloop;
403 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
409 public class For : Statement {
411 readonly Statement InitStatement;
412 readonly Statement Increment;
413 readonly Statement Statement;
414 bool may_return, infinite, empty;
416 public For (Statement initStatement,
422 InitStatement = initStatement;
424 Increment = increment;
425 Statement = statement;
429 public override bool Resolve (EmitContext ec)
433 if (InitStatement != null){
434 if (!InitStatement.Resolve (ec))
439 Test = Expression.ResolveBoolean (ec, Test, loc);
442 else if (Test is BoolConstant){
443 BoolConstant bc = (BoolConstant) Test;
445 if (bc.Value == false){
446 Warning_DeadCodeFound (Statement.loc);
454 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
456 ec.CurrentBranching.CreateSibling ();
458 if (!Statement.Resolve (ec))
461 if (Increment != null){
462 if (!Increment.Resolve (ec))
467 ec.KillFlowBranching ();
469 ec.CurrentBranching.Infinite = infinite;
470 FlowReturns returns = ec.EndFlowBranching ();
471 may_return = returns != FlowReturns.NEVER;
477 protected override bool DoEmit (EmitContext ec)
482 ILGenerator ig = ec.ig;
483 Label old_begin = ec.LoopBegin;
484 Label old_end = ec.LoopEnd;
485 bool old_inloop = ec.InLoop;
486 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
487 Label loop = ig.DefineLabel ();
488 Label test = ig.DefineLabel ();
490 if (InitStatement != null)
491 if (! (InitStatement is EmptyStatement))
492 InitStatement.Emit (ec);
494 ec.LoopBegin = ig.DefineLabel ();
495 ec.LoopEnd = ig.DefineLabel ();
497 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
499 ig.Emit (OpCodes.Br, test);
503 ig.MarkLabel (ec.LoopBegin);
504 if (!(Increment is EmptyStatement))
509 // If test is null, there is no test, and we are just
514 // The Resolve code already catches the case for Test == BoolConstant (false)
515 // so we know that this is true
517 if (Test is BoolConstant)
518 ig.Emit (OpCodes.Br, loop);
520 EmitBoolExpression (ec, Test, loop, true);
522 ig.Emit (OpCodes.Br, loop);
523 ig.MarkLabel (ec.LoopEnd);
525 ec.LoopBegin = old_begin;
526 ec.LoopEnd = old_end;
527 ec.InLoop = old_inloop;
528 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
531 // Inform whether we are infinite or not
534 if (Test is BoolConstant){
535 BoolConstant bc = (BoolConstant) Test;
538 return may_return == false;
542 return may_return == false;
546 public class StatementExpression : Statement {
549 public StatementExpression (ExpressionStatement expr, Location l)
555 public override bool Resolve (EmitContext ec)
557 expr = (Expression) expr.Resolve (ec);
561 protected override bool DoEmit (EmitContext ec)
563 ILGenerator ig = ec.ig;
565 if (expr is ExpressionStatement)
566 ((ExpressionStatement) expr).EmitStatement (ec);
569 ig.Emit (OpCodes.Pop);
575 public override string ToString ()
577 return "StatementExpression (" + expr + ")";
582 /// Implements the return statement
584 public class Return : Statement {
585 public Expression Expr;
587 public Return (Expression expr, Location l)
593 public override bool Resolve (EmitContext ec)
596 Expr = Expr.Resolve (ec);
602 Report.Error (-206, loc, "Return statement not allowed inside iterators");
606 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
608 if (ec.CurrentBranching.InTryBlock ())
609 ec.CurrentBranching.AddFinallyVector (vector);
611 vector.CheckOutParameters (ec.CurrentBranching);
613 vector.Returns = FlowReturns.ALWAYS;
614 vector.Breaks = FlowReturns.ALWAYS;
618 protected override bool DoEmit (EmitContext ec)
621 Report.Error (157, loc, "Control can not leave the body of the finally block");
625 if (ec.ReturnType == null){
627 Report.Error (127, loc, "Return with a value not allowed here");
632 Report.Error (126, loc, "An object of type `" +
633 TypeManager.CSharpName (ec.ReturnType) + "' is " +
634 "expected for the return statement");
638 if (Expr.Type != ec.ReturnType)
639 Expr = Expression.ConvertImplicitRequired (
640 ec, Expr, ec.ReturnType, loc);
647 if (ec.InTry || ec.InCatch)
648 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
651 if (ec.InTry || ec.InCatch) {
652 if (!ec.HasReturnLabel) {
653 ec.ReturnLabel = ec.ig.DefineLabel ();
654 ec.HasReturnLabel = true;
656 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
658 ec.ig.Emit (OpCodes.Ret);
664 public class Goto : Statement {
667 LabeledStatement label;
669 public override bool Resolve (EmitContext ec)
671 label = block.LookupLabel (target);
675 "No such label `" + target + "' in this scope");
679 // If this is a forward goto.
680 if (!label.IsDefined)
681 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
683 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
684 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
689 public Goto (Block parent_block, string label, Location l)
691 block = parent_block;
696 public string Target {
702 protected override bool DoEmit (EmitContext ec)
704 Label l = label.LabelTarget (ec);
705 ec.ig.Emit (OpCodes.Br, l);
711 public class LabeledStatement : Statement {
712 public readonly Location Location;
720 public LabeledStatement (string label_name, Location l)
722 this.label_name = label_name;
726 public Label LabelTarget (EmitContext ec)
730 label = ec.ig.DefineLabel ();
736 public bool IsDefined {
742 public bool HasBeenReferenced {
748 public void AddUsageVector (FlowBranching.UsageVector vector)
751 vectors = new ArrayList ();
753 vectors.Add (vector.Clone ());
756 public override bool Resolve (EmitContext ec)
759 ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors);
761 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER;
762 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER;
770 protected override bool DoEmit (EmitContext ec)
773 ec.ig.MarkLabel (label);
781 /// `goto default' statement
783 public class GotoDefault : Statement {
785 public GotoDefault (Location l)
790 public override bool Resolve (EmitContext ec)
792 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
793 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
797 protected override bool DoEmit (EmitContext ec)
799 if (ec.Switch == null){
800 Report.Error (153, loc, "goto default is only valid in a switch statement");
804 if (!ec.Switch.GotDefault){
805 Report.Error (159, loc, "No default target on switch statement");
808 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
814 /// `goto case' statement
816 public class GotoCase : Statement {
820 public GotoCase (Expression e, Location l)
826 public override bool Resolve (EmitContext ec)
828 if (ec.Switch == null){
829 Report.Error (153, loc, "goto case is only valid in a switch statement");
833 expr = expr.Resolve (ec);
837 if (!(expr is Constant)){
838 Report.Error (159, loc, "Target expression for goto case is not constant");
842 object val = Expression.ConvertIntLiteral (
843 (Constant) expr, ec.Switch.SwitchType, loc);
848 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
853 "No such label 'case " + val + "': for the goto case");
856 label = sl.ILLabelCode;
858 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE;
859 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
863 protected override bool DoEmit (EmitContext ec)
865 ec.ig.Emit (OpCodes.Br, label);
870 public class Throw : Statement {
873 public Throw (Expression expr, Location l)
879 public override bool Resolve (EmitContext ec)
882 expr = expr.Resolve (ec);
886 ExprClass eclass = expr.eclass;
888 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
889 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
890 expr.Error_UnexpectedKind ("value, variable, property or indexer access ");
896 if ((t != TypeManager.exception_type) &&
897 !t.IsSubclassOf (TypeManager.exception_type) &&
898 !(expr is NullLiteral)) {
899 Report.Error (155, loc,
900 "The type caught or thrown must be derived " +
901 "from System.Exception");
906 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION;
907 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION;
911 protected override bool DoEmit (EmitContext ec)
915 ec.ig.Emit (OpCodes.Rethrow);
919 "A throw statement with no argument is only " +
920 "allowed in a catch clause");
927 ec.ig.Emit (OpCodes.Throw);
933 public class Break : Statement {
935 public Break (Location l)
940 public override bool Resolve (EmitContext ec)
942 ec.CurrentBranching.MayLeaveLoop = true;
943 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
947 protected override bool DoEmit (EmitContext ec)
949 ILGenerator ig = ec.ig;
951 if (ec.InLoop == false && ec.Switch == null){
952 Report.Error (139, loc, "No enclosing loop or switch to continue to");
956 if (ec.InTry || ec.InCatch)
957 ig.Emit (OpCodes.Leave, ec.LoopEnd);
959 ig.Emit (OpCodes.Br, ec.LoopEnd);
965 public class Continue : Statement {
967 public Continue (Location l)
972 public override bool Resolve (EmitContext ec)
974 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
978 protected override bool DoEmit (EmitContext ec)
980 Label begin = ec.LoopBegin;
983 Report.Error (139, loc, "No enclosing loop to continue to");
988 // UGH: Non trivial. This Br might cross a try/catch boundary
992 // try { ... } catch { continue; }
996 // try {} catch { while () { continue; }}
998 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
999 ec.ig.Emit (OpCodes.Leave, begin);
1000 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
1001 throw new Exception ("Should never happen");
1003 ec.ig.Emit (OpCodes.Br, begin);
1009 // This is used in the control flow analysis code to specify whether the
1010 // current code block may return to its enclosing block before reaching
1013 public enum FlowReturns {
1014 // It can never return.
1017 // This means that the block contains a conditional return statement
1021 // The code always returns, ie. there's an unconditional return / break
1025 // The code always throws an exception.
1028 // The current code block is unreachable. This happens if it's immediately
1029 // following a FlowReturns.ALWAYS block.
1034 // This is a special bit vector which can inherit from another bit vector doing a
1035 // copy-on-write strategy. The inherited vector may have a smaller size than the
1038 public class MyBitVector {
1039 public readonly int Count;
1040 public readonly MyBitVector InheritsFrom;
1045 public MyBitVector (int Count)
1046 : this (null, Count)
1049 public MyBitVector (MyBitVector InheritsFrom, int Count)
1051 this.InheritsFrom = InheritsFrom;
1056 // Checks whether this bit vector has been modified. After setting this to true,
1057 // we won't use the inherited vector anymore, but our own copy of it.
1059 public bool IsDirty {
1066 initialize_vector ();
1071 // Get/set bit `index' in the bit vector.
1073 public bool this [int index]
1077 throw new ArgumentOutOfRangeException ();
1079 // We're doing a "copy-on-write" strategy here; as long
1080 // as nobody writes to the array, we can use our parent's
1081 // copy instead of duplicating the vector.
1084 return vector [index];
1085 else if (InheritsFrom != null) {
1086 BitArray inherited = InheritsFrom.Vector;
1088 if (index < inherited.Count)
1089 return inherited [index];
1098 throw new ArgumentOutOfRangeException ();
1100 // Only copy the vector if we're actually modifying it.
1102 if (this [index] != value) {
1103 initialize_vector ();
1105 vector [index] = value;
1111 // If you explicitly convert the MyBitVector to a BitArray, you will get a deep
1112 // copy of the bit vector.
1114 public static explicit operator BitArray (MyBitVector vector)
1116 vector.initialize_vector ();
1117 return vector.Vector;
1121 // Performs an `or' operation on the bit vector. The `new_vector' may have a
1122 // different size than the current one.
1124 public void Or (MyBitVector new_vector)
1126 BitArray new_array = new_vector.Vector;
1128 initialize_vector ();
1131 if (vector.Count < new_array.Count)
1132 upper = vector.Count;
1134 upper = new_array.Count;
1136 for (int i = 0; i < upper; i++)
1137 vector [i] = vector [i] | new_array [i];
1141 // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
1142 // a different size than the current one.
1144 public void And (MyBitVector new_vector)
1146 BitArray new_array = new_vector.Vector;
1148 initialize_vector ();
1151 if (vector.Count < new_array.Count)
1152 lower = upper = vector.Count;
1154 lower = new_array.Count;
1155 upper = vector.Count;
1158 for (int i = 0; i < lower; i++)
1159 vector [i] = vector [i] & new_array [i];
1161 for (int i = lower; i < upper; i++)
1166 // This does a deep copy of the bit vector.
1168 public MyBitVector Clone ()
1170 MyBitVector retval = new MyBitVector (Count);
1172 retval.Vector = Vector;
1181 else if (!is_dirty && (InheritsFrom != null))
1182 return InheritsFrom.Vector;
1184 initialize_vector ();
1190 initialize_vector ();
1192 for (int i = 0; i < Math.Min (vector.Count, value.Count); i++)
1193 vector [i] = value [i];
1197 void initialize_vector ()
1202 vector = new BitArray (Count, false);
1203 if (InheritsFrom != null)
1204 Vector = InheritsFrom.Vector;
1209 public override string ToString ()
1211 StringBuilder sb = new StringBuilder ("MyBitVector (");
1213 BitArray vector = Vector;
1217 sb.Append ("INHERITED - ");
1218 for (int i = 0; i < vector.Count; i++) {
1221 sb.Append (vector [i]);
1225 return sb.ToString ();
1230 // The type of a FlowBranching.
1232 public enum FlowBranchingType {
1233 // Normal (conditional or toplevel) block.
1250 // A new instance of this class is created every time a new block is resolved
1251 // and if there's branching in the block's control flow.
1253 public class FlowBranching {
1255 // The type of this flow branching.
1257 public readonly FlowBranchingType Type;
1260 // The block this branching is contained in. This may be null if it's not
1261 // a top-level block and it doesn't declare any local variables.
1263 public readonly Block Block;
1266 // The parent of this branching or null if this is the top-block.
1268 public readonly FlowBranching Parent;
1271 // Start-Location of this flow branching.
1273 public readonly Location Location;
1276 // A list of UsageVectors. A new vector is added each time control flow may
1277 // take a different path.
1279 public UsageVector[] Siblings;
1282 // If this is an infinite loop.
1284 public bool Infinite;
1287 // If we may leave the current loop.
1289 public bool MayLeaveLoop;
1294 InternalParameters param_info;
1296 MyStructInfo[] struct_params;
1298 ArrayList finally_vectors;
1300 static int next_id = 0;
1304 // Performs an `And' operation on the FlowReturns status
1305 // (for instance, a block only returns ALWAYS if all its siblings
1308 public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b)
1310 if (b == FlowReturns.UNREACHABLE)
1314 case FlowReturns.NEVER:
1315 if (b == FlowReturns.NEVER)
1316 return FlowReturns.NEVER;
1318 return FlowReturns.SOMETIMES;
1320 case FlowReturns.SOMETIMES:
1321 return FlowReturns.SOMETIMES;
1323 case FlowReturns.ALWAYS:
1324 if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION))
1325 return FlowReturns.ALWAYS;
1327 return FlowReturns.SOMETIMES;
1329 case FlowReturns.EXCEPTION:
1330 if (b == FlowReturns.EXCEPTION)
1331 return FlowReturns.EXCEPTION;
1332 else if (b == FlowReturns.ALWAYS)
1333 return FlowReturns.ALWAYS;
1335 return FlowReturns.SOMETIMES;
1342 // The vector contains a BitArray with information about which local variables
1343 // and parameters are already initialized at the current code position.
1345 public class UsageVector {
1347 // If this is true, then the usage vector has been modified and must be
1348 // merged when we're done with this branching.
1350 public bool IsDirty;
1353 // The number of parameters in this block.
1355 public readonly int CountParameters;
1358 // The number of locals in this block.
1360 public readonly int CountLocals;
1363 // If not null, then we inherit our state from this vector and do a
1364 // copy-on-write. If null, then we're the first sibling in a top-level
1365 // block and inherit from the empty vector.
1367 public readonly UsageVector InheritsFrom;
1372 MyBitVector locals, parameters;
1373 FlowReturns real_returns, real_breaks;
1376 static int next_id = 0;
1380 // Normally, you should not use any of these constructors.
1382 public UsageVector (UsageVector parent, int num_params, int num_locals)
1384 this.InheritsFrom = parent;
1385 this.CountParameters = num_params;
1386 this.CountLocals = num_locals;
1387 this.real_returns = FlowReturns.NEVER;
1388 this.real_breaks = FlowReturns.NEVER;
1390 if (parent != null) {
1391 locals = new MyBitVector (parent.locals, CountLocals);
1393 parameters = new MyBitVector (parent.parameters, num_params);
1394 real_returns = parent.Returns;
1395 real_breaks = parent.Breaks;
1397 locals = new MyBitVector (null, CountLocals);
1399 parameters = new MyBitVector (null, num_params);
1405 public UsageVector (UsageVector parent)
1406 : this (parent, parent.CountParameters, parent.CountLocals)
1410 // This does a deep copy of the usage vector.
1412 public UsageVector Clone ()
1414 UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
1416 retval.locals = locals.Clone ();
1417 if (parameters != null)
1418 retval.parameters = parameters.Clone ();
1419 retval.real_returns = real_returns;
1420 retval.real_breaks = real_breaks;
1426 // State of parameter `number'.
1428 public bool this [int number]
1433 else if (number == 0)
1434 throw new ArgumentException ();
1436 return parameters [number - 1];
1442 else if (number == 0)
1443 throw new ArgumentException ();
1445 parameters [number - 1] = value;
1450 // State of the local variable `vi'.
1451 // If the local variable is a struct, use a non-zero `field_idx'
1452 // to check an individual field in it.
1454 public bool this [VariableInfo vi, int field_idx]
1457 if (vi.Number == -1)
1459 else if (vi.Number == 0)
1460 throw new ArgumentException ();
1462 return locals [vi.Number + field_idx - 1];
1466 if (vi.Number == -1)
1468 else if (vi.Number == 0)
1469 throw new ArgumentException ();
1471 locals [vi.Number + field_idx - 1] = value;
1476 // Specifies when the current block returns.
1477 // If this is FlowReturns.UNREACHABLE, then control can never reach the
1478 // end of the method (so that we don't need to emit a return statement).
1479 // The same applies for FlowReturns.EXCEPTION, but in this case the return
1480 // value will never be used.
1482 public FlowReturns Returns {
1484 return real_returns;
1488 real_returns = value;
1493 // Specifies whether control may return to our containing block
1494 // before reaching the end of this block. This happens if there
1495 // is a break/continue/goto/return in it.
1496 // This can also be used to find out whether the statement immediately
1497 // following the current block may be reached or not.
1499 public FlowReturns Breaks {
1505 real_breaks = value;
1509 public bool AlwaysBreaks {
1511 return (Breaks == FlowReturns.ALWAYS) ||
1512 (Breaks == FlowReturns.EXCEPTION) ||
1513 (Breaks == FlowReturns.UNREACHABLE);
1517 public bool MayBreak {
1519 return Breaks != FlowReturns.NEVER;
1523 public bool AlwaysReturns {
1525 return (Returns == FlowReturns.ALWAYS) ||
1526 (Returns == FlowReturns.EXCEPTION);
1530 public bool MayReturn {
1532 return (Returns == FlowReturns.SOMETIMES) ||
1533 (Returns == FlowReturns.ALWAYS);
1538 // Merge a child branching.
1540 public FlowReturns MergeChildren (FlowBranching branching, UsageVector[] children)
1542 MyBitVector new_locals = null;
1543 MyBitVector new_params = null;
1545 FlowReturns new_returns = FlowReturns.NEVER;
1546 FlowReturns new_breaks = FlowReturns.NEVER;
1547 bool new_returns_set = false, new_breaks_set = false;
1549 Report.Debug (2, "MERGING CHILDREN", branching, branching.Type,
1550 this, children.Length);
1552 foreach (UsageVector child in children) {
1553 Report.Debug (2, " MERGING CHILD", child, child.is_finally);
1555 if (!child.is_finally) {
1556 if (child.Breaks != FlowReturns.UNREACHABLE) {
1557 // If Returns is already set, perform an
1558 // `And' operation on it, otherwise just set just.
1559 if (!new_returns_set) {
1560 new_returns = child.Returns;
1561 new_returns_set = true;
1563 new_returns = AndFlowReturns (
1564 new_returns, child.Returns);
1567 // If Breaks is already set, perform an
1568 // `And' operation on it, otherwise just set just.
1569 if (!new_breaks_set) {
1570 new_breaks = child.Breaks;
1571 new_breaks_set = true;
1573 new_breaks = AndFlowReturns (
1574 new_breaks, child.Breaks);
1577 // Ignore unreachable children.
1578 if (child.Returns == FlowReturns.UNREACHABLE)
1581 // A local variable is initialized after a flow branching if it
1582 // has been initialized in all its branches which do neither
1583 // always return or always throw an exception.
1585 // If a branch may return, but does not always return, then we
1586 // can treat it like a never-returning branch here: control will
1587 // only reach the code position after the branching if we did not
1590 // It's important to distinguish between always and sometimes
1591 // returning branches here:
1594 // 2 if (something) {
1598 // 6 Console.WriteLine (a);
1600 // The if block in lines 3-4 always returns, so we must not look
1601 // at the initialization of `a' in line 4 - thus it'll still be
1602 // uninitialized in line 6.
1604 // On the other hand, the following is allowed:
1611 // 6 Console.WriteLine (a);
1613 // Here, `a' is initialized in line 3 and we must not look at
1614 // line 5 since it always returns.
1616 if (child.is_finally) {
1617 if (new_locals == null)
1618 new_locals = locals.Clone ();
1619 new_locals.Or (child.locals);
1621 if (parameters != null) {
1622 if (new_params == null)
1623 new_params = parameters.Clone ();
1624 new_params.Or (child.parameters);
1628 if (!child.AlwaysReturns && !child.AlwaysBreaks) {
1629 if (new_locals != null)
1630 new_locals.And (child.locals);
1632 new_locals = locals.Clone ();
1633 new_locals.Or (child.locals);
1635 } else if (children.Length == 1) {
1636 new_locals = locals.Clone ();
1637 new_locals.Or (child.locals);
1640 // An `out' parameter must be assigned in all branches which do
1641 // not always throw an exception.
1642 if (parameters != null) {
1643 if (child.Breaks != FlowReturns.EXCEPTION) {
1644 if (new_params != null)
1645 new_params.And (child.parameters);
1647 new_params = parameters.Clone ();
1648 new_params.Or (child.parameters);
1650 } else if (children.Length == 1) {
1651 new_params = parameters.Clone ();
1652 new_params.Or (child.parameters);
1658 Returns = new_returns;
1659 if ((branching.Type == FlowBranchingType.BLOCK) ||
1660 (branching.Type == FlowBranchingType.EXCEPTION) ||
1661 (new_breaks == FlowReturns.UNREACHABLE) ||
1662 (new_breaks == FlowReturns.EXCEPTION))
1663 Breaks = new_breaks;
1664 else if (branching.Type == FlowBranchingType.SWITCH_SECTION)
1665 Breaks = new_returns;
1666 else if (branching.Type == FlowBranchingType.SWITCH){
1667 if (new_breaks == FlowReturns.ALWAYS)
1668 Breaks = FlowReturns.ALWAYS;
1672 // We've now either reached the point after the branching or we will
1673 // never get there since we always return or always throw an exception.
1675 // If we can reach the point after the branching, mark all locals and
1676 // parameters as initialized which have been initialized in all branches
1677 // we need to look at (see above).
1680 if (((new_breaks != FlowReturns.ALWAYS) &&
1681 (new_breaks != FlowReturns.EXCEPTION) &&
1682 (new_breaks != FlowReturns.UNREACHABLE)) ||
1683 (children.Length == 1)) {
1684 if (new_locals != null)
1685 locals.Or (new_locals);
1687 if (new_params != null)
1688 parameters.Or (new_params);
1691 Report.Debug (2, "MERGING CHILDREN DONE", branching.Type,
1692 new_params, new_locals, new_returns, new_breaks,
1693 branching.Infinite, branching.MayLeaveLoop, this);
1695 if (branching.Type == FlowBranchingType.SWITCH_SECTION) {
1696 if ((new_breaks != FlowReturns.ALWAYS) &&
1697 (new_breaks != FlowReturns.EXCEPTION) &&
1698 (new_breaks != FlowReturns.UNREACHABLE))
1699 Report.Error (163, branching.Location,
1700 "Control cannot fall through from one " +
1701 "case label to another");
1704 if (branching.Infinite && !branching.MayLeaveLoop) {
1705 Report.Debug (1, "INFINITE", new_returns, new_breaks,
1706 Returns, Breaks, this);
1708 // We're actually infinite.
1709 if (new_returns == FlowReturns.NEVER) {
1710 Breaks = FlowReturns.UNREACHABLE;
1711 return FlowReturns.UNREACHABLE;
1714 // If we're an infinite loop and do not break, the code after
1715 // the loop can never be reached. However, if we may return
1716 // from the loop, then we do always return (or stay in the loop
1718 if ((new_returns == FlowReturns.SOMETIMES) ||
1719 (new_returns == FlowReturns.ALWAYS)) {
1720 Returns = FlowReturns.ALWAYS;
1721 return FlowReturns.ALWAYS;
1725 if (branching.Type == FlowBranchingType.LOOP_BLOCK) {
1726 Report.Debug (2, "MERGING LOOP BLOCK DONE", branching,
1727 branching.Infinite, branching.MayLeaveLoop,
1728 new_breaks, new_returns);
1730 // If we may leave the loop, then we do not always return.
1731 if (branching.MayLeaveLoop && (new_returns == FlowReturns.ALWAYS)) {
1732 Returns = FlowReturns.SOMETIMES;
1733 return FlowReturns.SOMETIMES;
1736 // A `break' in a loop does not "break" in the outer block.
1737 Breaks = FlowReturns.NEVER;
1744 // Tells control flow analysis that the current code position may be reached with
1745 // a forward jump from any of the origins listed in `origin_vectors' which is a
1746 // list of UsageVectors.
1748 // This is used when resolving forward gotos - in the following example, the
1749 // variable `a' is uninitialized in line 8 becase this line may be reached via
1750 // the goto in line 4:
1760 // 8 Console.WriteLine (a);
1763 public void MergeJumpOrigins (ICollection origin_vectors)
1765 Report.Debug (1, "MERGING JUMP ORIGIN", this);
1767 real_breaks = FlowReturns.NEVER;
1768 real_returns = FlowReturns.NEVER;
1770 foreach (UsageVector vector in origin_vectors) {
1771 Report.Debug (1, " MERGING JUMP ORIGIN", vector);
1773 locals.And (vector.locals);
1774 if (parameters != null)
1775 parameters.And (vector.parameters);
1776 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1777 Returns = AndFlowReturns (Returns, vector.Returns);
1780 Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
1784 // This is used at the beginning of a finally block if there were
1785 // any return statements in the try block or one of the catch blocks.
1787 public void MergeFinallyOrigins (ICollection finally_vectors)
1789 Report.Debug (1, "MERGING FINALLY ORIGIN", this);
1791 real_breaks = FlowReturns.NEVER;
1793 foreach (UsageVector vector in finally_vectors) {
1794 Report.Debug (1, " MERGING FINALLY ORIGIN", vector);
1796 if (parameters != null)
1797 parameters.And (vector.parameters);
1798 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1803 Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
1806 public void CheckOutParameters (FlowBranching branching)
1808 if (parameters != null)
1809 branching.CheckOutParameters (parameters, branching.Location);
1813 // Performs an `or' operation on the locals and the parameters.
1815 public void Or (UsageVector new_vector)
1817 locals.Or (new_vector.locals);
1818 if (parameters != null)
1819 parameters.Or (new_vector.parameters);
1823 // Performs an `and' operation on the locals.
1825 public void AndLocals (UsageVector new_vector)
1827 locals.And (new_vector.locals);
1831 // Returns a deep copy of the parameters.
1833 public MyBitVector Parameters {
1835 if (parameters != null)
1836 return parameters.Clone ();
1843 // Returns a deep copy of the locals.
1845 public MyBitVector Locals {
1847 return locals.Clone ();
1855 public override string ToString ()
1857 StringBuilder sb = new StringBuilder ();
1859 sb.Append ("Vector (");
1862 sb.Append (Returns);
1865 if (parameters != null) {
1867 sb.Append (parameters);
1873 return sb.ToString ();
1877 FlowBranching (FlowBranchingType type, Location loc)
1880 this.Location = loc;
1886 // Creates a new flow branching for `block'.
1887 // This is used from Block.Resolve to create the top-level branching of
1890 public FlowBranching (Block block, InternalParameters ip, Location loc)
1891 : this (FlowBranchingType.BLOCK, loc)
1896 int count = (ip != null) ? ip.Count : 0;
1899 param_map = new int [count];
1900 struct_params = new MyStructInfo [count];
1903 for (int i = 0; i < count; i++) {
1904 Parameter.Modifier mod = param_info.ParameterModifier (i);
1906 if ((mod & Parameter.Modifier.OUT) == 0)
1909 param_map [i] = ++num_params;
1911 Type param_type = param_info.ParameterType (i);
1913 struct_params [i] = MyStructInfo.GetStructInfo (param_type);
1914 if (struct_params [i] != null)
1915 num_params += struct_params [i].Count;
1918 AddSibling (new UsageVector (null, num_params, block.CountVariables));
1922 // Creates a new flow branching which is contained in `parent'.
1923 // You should only pass non-null for the `block' argument if this block
1924 // introduces any new variables - in this case, we need to create a new
1925 // usage vector with a different size than our parent's one.
1927 public FlowBranching (FlowBranching parent, FlowBranchingType type,
1928 Block block, Location loc)
1934 if (parent != null) {
1935 param_info = parent.param_info;
1936 param_map = parent.param_map;
1937 struct_params = parent.struct_params;
1938 num_params = parent.num_params;
1943 vector = new UsageVector (parent.CurrentUsageVector, num_params,
1944 Block.CountVariables);
1946 vector = new UsageVector (Parent.CurrentUsageVector);
1948 AddSibling (vector);
1951 case FlowBranchingType.EXCEPTION:
1952 finally_vectors = new ArrayList ();
1960 void AddSibling (UsageVector uv)
1962 if (Siblings != null) {
1963 UsageVector[] ns = new UsageVector [Siblings.Length + 1];
1964 for (int i = 0; i < Siblings.Length; ++i)
1965 ns [i] = Siblings [i];
1968 Siblings = new UsageVector [1];
1970 Siblings [Siblings.Length - 1] = uv;
1974 // Returns the branching's current usage vector.
1976 public UsageVector CurrentUsageVector
1979 return Siblings [Siblings.Length - 1];
1984 // Creates a sibling of the current usage vector.
1986 public void CreateSibling ()
1988 AddSibling (new UsageVector (Parent.CurrentUsageVector));
1990 Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
1994 // Creates a sibling for a `finally' block.
1996 public void CreateSiblingForFinally ()
1998 if (Type != FlowBranchingType.EXCEPTION)
1999 throw new NotSupportedException ();
2003 CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
2007 // Check whether all `out' parameters have been assigned.
2009 public void CheckOutParameters (MyBitVector parameters, Location loc)
2014 for (int i = 0; i < param_map.Length; i++) {
2015 int index = param_map [i];
2020 if (parameters [index - 1])
2023 // If it's a struct, we must ensure that all its fields have
2024 // been assigned. If the struct has any non-public fields, this
2025 // can only be done by assigning the whole struct.
2027 MyStructInfo struct_info = struct_params [i];
2028 if ((struct_info == null) || struct_info.HasNonPublicFields) {
2030 177, loc, "The out parameter `" +
2031 param_info.ParameterName (i) + "' must be " +
2032 "assigned before control leave the current method.");
2038 for (int j = 0; j < struct_info.Count; j++) {
2039 if (!parameters [index + j]) {
2041 177, loc, "The out parameter `" +
2042 param_info.ParameterName (i) + "' must be " +
2043 "assigned before control leaves the current method.");
2052 // Merge a child branching.
2054 public FlowReturns MergeChild (FlowBranching child)
2056 FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings);
2058 if ((child.Type != FlowBranchingType.LOOP_BLOCK) &&
2059 (child.Type != FlowBranchingType.SWITCH_SECTION))
2060 MayLeaveLoop |= child.MayLeaveLoop;
2066 // Does the toplevel merging.
2068 public FlowReturns MergeTopBlock ()
2070 if ((Type != FlowBranchingType.BLOCK) || (Block == null))
2071 throw new NotSupportedException ();
2073 UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
2075 Report.Debug (1, "MERGING TOP BLOCK", Location, vector);
2077 vector.MergeChildren (this, Siblings);
2079 if (Siblings.Length == 1)
2080 Siblings [0] = vector;
2083 AddSibling (vector);
2086 Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector);
2088 if (vector.Breaks != FlowReturns.EXCEPTION) {
2089 if (!vector.AlwaysBreaks)
2090 CheckOutParameters (CurrentUsageVector.Parameters, Location);
2091 return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns;
2093 return FlowReturns.EXCEPTION;
2096 public bool InTryBlock ()
2098 if (finally_vectors != null)
2100 else if (Parent != null)
2101 return Parent.InTryBlock ();
2106 public void AddFinallyVector (UsageVector vector)
2108 if (finally_vectors != null) {
2109 finally_vectors.Add (vector.Clone ());
2114 Parent.AddFinallyVector (vector);
2116 throw new NotSupportedException ();
2119 public bool IsVariableAssigned (VariableInfo vi)
2121 if (CurrentUsageVector.AlwaysBreaks)
2124 return CurrentUsageVector [vi, 0];
2127 public bool IsVariableAssigned (VariableInfo vi, int field_idx)
2129 if (CurrentUsageVector.AlwaysBreaks)
2132 return CurrentUsageVector [vi, field_idx];
2135 public void SetVariableAssigned (VariableInfo vi)
2137 if (CurrentUsageVector.AlwaysBreaks)
2140 CurrentUsageVector [vi, 0] = true;
2143 public void SetVariableAssigned (VariableInfo vi, int field_idx)
2145 if (CurrentUsageVector.AlwaysBreaks)
2148 CurrentUsageVector [vi, field_idx] = true;
2151 public bool IsParameterAssigned (int number)
2153 int index = param_map [number];
2158 if (CurrentUsageVector [index])
2161 // Parameter is not assigned, so check whether it's a struct.
2162 // If it's either not a struct or a struct which non-public
2163 // fields, return false.
2164 MyStructInfo struct_info = struct_params [number];
2165 if ((struct_info == null) || struct_info.HasNonPublicFields)
2168 // Ok, so each field must be assigned.
2169 for (int i = 0; i < struct_info.Count; i++)
2170 if (!CurrentUsageVector [index + i])
2176 public bool IsParameterAssigned (int number, string field_name)
2178 int index = param_map [number];
2183 MyStructInfo info = (MyStructInfo) struct_params [number];
2187 int field_idx = info [field_name];
2189 return CurrentUsageVector [index + field_idx];
2192 public void SetParameterAssigned (int number)
2194 if (param_map [number] == 0)
2197 if (!CurrentUsageVector.AlwaysBreaks)
2198 CurrentUsageVector [param_map [number]] = true;
2201 public void SetParameterAssigned (int number, string field_name)
2203 int index = param_map [number];
2208 MyStructInfo info = (MyStructInfo) struct_params [number];
2212 int field_idx = info [field_name];
2214 if (!CurrentUsageVector.AlwaysBreaks)
2215 CurrentUsageVector [index + field_idx] = true;
2218 public bool IsReachable ()
2223 case FlowBranchingType.SWITCH_SECTION:
2224 // The code following a switch block is reachable unless the switch
2225 // block always returns.
2226 reachable = !CurrentUsageVector.AlwaysReturns;
2229 case FlowBranchingType.LOOP_BLOCK:
2230 // The code following a loop is reachable unless the loop always
2231 // returns or it's an infinite loop without any `break's in it.
2232 reachable = !CurrentUsageVector.AlwaysReturns &&
2233 (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE);
2237 // The code following a block or exception is reachable unless the
2238 // block either always returns or always breaks.
2242 reachable = !CurrentUsageVector.AlwaysBreaks &&
2243 !CurrentUsageVector.AlwaysReturns;
2247 Report.Debug (1, "REACHABLE", this, Type, CurrentUsageVector.Returns,
2248 CurrentUsageVector.Breaks, CurrentUsageVector, MayLeaveLoop,
2254 public override string ToString ()
2256 StringBuilder sb = new StringBuilder ("FlowBranching (");
2261 if (Block != null) {
2263 sb.Append (Block.ID);
2265 sb.Append (Block.StartLocation);
2268 sb.Append (Siblings.Length);
2270 sb.Append (CurrentUsageVector);
2272 return sb.ToString ();
2276 public class MyStructInfo {
2277 public readonly Type Type;
2278 public readonly FieldInfo[] Fields;
2279 public readonly FieldInfo[] NonPublicFields;
2280 public readonly int Count;
2281 public readonly int CountNonPublic;
2282 public readonly bool HasNonPublicFields;
2284 private static Hashtable field_type_hash = new Hashtable ();
2285 private Hashtable field_hash;
2287 // Private constructor. To save memory usage, we only need to create one instance
2288 // of this class per struct type.
2289 private MyStructInfo (Type type)
2293 if (type is TypeBuilder) {
2294 TypeContainer tc = TypeManager.LookupTypeContainer (type);
2296 ArrayList fields = tc.Fields;
2297 if (fields != null) {
2298 foreach (Field field in fields) {
2299 if ((field.ModFlags & Modifiers.STATIC) != 0)
2301 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2308 Fields = new FieldInfo [Count];
2309 NonPublicFields = new FieldInfo [CountNonPublic];
2311 Count = CountNonPublic = 0;
2312 if (fields != null) {
2313 foreach (Field field in fields) {
2314 if ((field.ModFlags & Modifiers.STATIC) != 0)
2316 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2317 Fields [Count++] = field.FieldBuilder;
2319 NonPublicFields [CountNonPublic++] =
2325 Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public);
2326 Count = Fields.Length;
2328 NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic);
2329 CountNonPublic = NonPublicFields.Length;
2332 Count += NonPublicFields.Length;
2335 field_hash = new Hashtable ();
2336 foreach (FieldInfo field in Fields)
2337 field_hash.Add (field.Name, ++number);
2339 if (NonPublicFields.Length != 0)
2340 HasNonPublicFields = true;
2342 foreach (FieldInfo field in NonPublicFields)
2343 field_hash.Add (field.Name, ++number);
2346 public int this [string name] {
2348 if (field_hash.Contains (name))
2349 return (int) field_hash [name];
2355 public FieldInfo this [int index] {
2357 if (index >= Fields.Length)
2358 return NonPublicFields [index - Fields.Length];
2360 return Fields [index];
2364 public static MyStructInfo GetStructInfo (Type type)
2366 if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type))
2369 if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type))
2372 MyStructInfo info = (MyStructInfo) field_type_hash [type];
2376 info = new MyStructInfo (type);
2377 field_type_hash.Add (type, info);
2381 public static MyStructInfo GetStructInfo (TypeContainer tc)
2383 MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder];
2387 info = new MyStructInfo (tc.TypeBuilder);
2388 field_type_hash.Add (tc.TypeBuilder, info);
2393 public class VariableInfo : IVariable {
2394 public Expression Type;
2397 // Most of the time a variable will be stored in a LocalBuilder
2399 // But sometimes, it will be stored in a field. The context of the field will
2400 // be stored in the EmitContext
2403 public LocalBuilder LocalBuilder;
2404 public FieldBuilder FieldBuilder;
2406 public Type VariableType;
2407 public readonly string Name;
2408 public readonly Location Location;
2409 public readonly int Block;
2414 public bool Assigned;
2415 public bool ReadOnly;
2417 public VariableInfo (Expression type, string name, int block, Location l)
2422 LocalBuilder = null;
2426 public VariableInfo (TypeContainer tc, int block, Location l)
2428 VariableType = tc.TypeBuilder;
2429 struct_info = MyStructInfo.GetStructInfo (tc);
2431 LocalBuilder = null;
2435 MyStructInfo struct_info;
2436 public MyStructInfo StructInfo {
2442 public bool IsAssigned (EmitContext ec, Location loc)
2444 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
2447 MyStructInfo struct_info = StructInfo;
2448 if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
2449 Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
2450 ec.CurrentBranching.SetVariableAssigned (this);
2454 int count = struct_info.Count;
2456 for (int i = 0; i < count; i++) {
2457 if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
2459 Report.Error (165, loc,
2460 "Use of unassigned local variable `" +
2462 ec.CurrentBranching.SetVariableAssigned (this);
2466 FieldInfo field = struct_info [i];
2467 Report.Error (171, loc,
2468 "Field `" + TypeManager.CSharpName (VariableType) +
2469 "." + field.Name + "' must be fully initialized " +
2470 "before control leaves the constructor");
2478 public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
2480 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) ||
2481 (struct_info == null))
2484 int field_idx = StructInfo [name];
2488 if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) {
2489 Report.Error (170, loc,
2490 "Use of possibly unassigned field `" + name + "'");
2491 ec.CurrentBranching.SetVariableAssigned (this, field_idx);
2498 public void SetAssigned (EmitContext ec)
2500 if (ec.DoFlowAnalysis)
2501 ec.CurrentBranching.SetVariableAssigned (this);
2504 public void SetFieldAssigned (EmitContext ec, string name)
2506 if (ec.DoFlowAnalysis && (struct_info != null))
2507 ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]);
2510 public bool Resolve (DeclSpace decl)
2512 if (struct_info != null)
2515 if (VariableType == null)
2516 VariableType = decl.ResolveType (Type, false, Location);
2518 if (VariableType == null)
2521 struct_info = MyStructInfo.GetStructInfo (VariableType);
2526 public void MakePinned ()
2528 TypeManager.MakePinned (LocalBuilder);
2531 public override string ToString ()
2533 return "VariableInfo (" + Number + "," + Type + "," + Location + ")";
2538 /// Block represents a C# block.
2542 /// This class is used in a number of places: either to represent
2543 /// explicit blocks that the programmer places or implicit blocks.
2545 /// Implicit blocks are used as labels or to introduce variable
2548 public class Block : Statement {
2549 public readonly Block Parent;
2550 public readonly Location StartLocation;
2551 public Location EndLocation = Location.Null;
2554 public enum Flags : byte {
2560 public bool Implicit {
2562 return (flags & Flags.Implicit) != 0;
2566 public bool Unchecked {
2568 return (flags & Flags.Unchecked) != 0;
2571 flags |= Flags.Unchecked;
2576 // The statements in this block
2578 ArrayList statements;
2581 // An array of Blocks. We keep track of children just
2582 // to generate the local variable declarations.
2584 // Statements and child statements are handled through the
2590 // Labels. (label, block) pairs.
2595 // Keeps track of (name, type) pairs
2597 Hashtable variables;
2600 // Keeps track of constants
2601 Hashtable constants;
2609 public Block (Block parent)
2610 : this (parent, (Flags) 0, Location.Null, Location.Null)
2613 public Block (Block parent, Flags flags)
2614 : this (parent, flags, Location.Null, Location.Null)
2617 public Block (Block parent, Flags flags, Parameters parameters)
2618 : this (parent, flags, parameters, Location.Null, Location.Null)
2621 public Block (Block parent, Location start, Location end)
2622 : this (parent, (Flags) 0, start, end)
2625 public Block (Block parent, Parameters parameters, Location start, Location end)
2626 : this (parent, (Flags) 0, parameters, start, end)
2629 public Block (Block parent, Flags flags, Location start, Location end)
2630 : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
2633 public Block (Block parent, Flags flags, Parameters parameters,
2634 Location start, Location end)
2637 parent.AddChild (this);
2639 this.Parent = parent;
2641 this.parameters = parameters;
2642 this.StartLocation = start;
2643 this.EndLocation = end;
2646 statements = new ArrayList ();
2655 void AddChild (Block b)
2657 if (children == null)
2658 children = new ArrayList ();
2663 public void SetEndLocation (Location loc)
2669 /// Adds a label to the current block.
2673 /// false if the name already exists in this block. true
2677 public bool AddLabel (string name, LabeledStatement target)
2680 labels = new Hashtable ();
2681 if (labels.Contains (name))
2684 labels.Add (name, target);
2688 public LabeledStatement LookupLabel (string name)
2690 if (labels != null){
2691 if (labels.Contains (name))
2692 return ((LabeledStatement) labels [name]);
2696 return Parent.LookupLabel (name);
2701 VariableInfo this_variable = null;
2704 // Returns the "this" instance variable of this block.
2705 // See AddThisVariable() for more information.
2707 public VariableInfo ThisVariable {
2709 if (this_variable != null)
2710 return this_variable;
2711 else if (Parent != null)
2712 return Parent.ThisVariable;
2718 Hashtable child_variable_names;
2721 // Marks a variable with name @name as being used in a child block.
2722 // If a variable name has been used in a child block, it's illegal to
2723 // declare a variable with the same name in the current block.
2725 public void AddChildVariableName (string name)
2727 if (child_variable_names == null)
2728 child_variable_names = new Hashtable ();
2730 if (!child_variable_names.Contains (name))
2731 child_variable_names.Add (name, true);
2735 // Marks all variables from block @block and all its children as being
2736 // used in a child block.
2738 public void AddChildVariableNames (Block block)
2740 if (block.Variables != null) {
2741 foreach (string name in block.Variables.Keys)
2742 AddChildVariableName (name);
2745 if (block.children != null) {
2746 foreach (Block child in block.children)
2747 AddChildVariableNames (child);
2752 // Checks whether a variable name has already been used in a child block.
2754 public bool IsVariableNameUsedInChildBlock (string name)
2756 if (child_variable_names == null)
2759 return child_variable_names.Contains (name);
2763 // This is used by non-static `struct' constructors which do not have an
2764 // initializer - in this case, the constructor must initialize all of the
2765 // struct's fields. To do this, we add a "this" variable and use the flow
2766 // analysis code to ensure that it's been fully initialized before control
2767 // leaves the constructor.
2769 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
2771 if (this_variable != null)
2772 return this_variable;
2774 this_variable = new VariableInfo (tc, ID, l);
2776 if (variables == null)
2777 variables = new Hashtable ();
2778 variables.Add ("this", this_variable);
2780 return this_variable;
2783 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
2785 if (variables == null)
2786 variables = new Hashtable ();
2788 VariableInfo vi = GetVariableInfo (name);
2791 Report.Error (136, l, "A local variable named `" + name + "' " +
2792 "cannot be declared in this scope since it would " +
2793 "give a different meaning to `" + name + "', which " +
2794 "is already used in a `parent or current' scope to " +
2795 "denote something else");
2797 Report.Error (128, l, "A local variable `" + name + "' is already " +
2798 "defined in this scope");
2802 if (IsVariableNameUsedInChildBlock (name)) {
2803 Report.Error (136, l, "A local variable named `" + name + "' " +
2804 "cannot be declared in this scope since it would " +
2805 "give a different meaning to `" + name + "', which " +
2806 "is already used in a `child' scope to denote something " +
2813 Parameter p = pars.GetParameterByName (name, out idx);
2815 Report.Error (136, l, "A local variable named `" + name + "' " +
2816 "cannot be declared in this scope since it would " +
2817 "give a different meaning to `" + name + "', which " +
2818 "is already used in a `parent or current' scope to " +
2819 "denote something else");
2824 vi = new VariableInfo (type, name, ID, l);
2826 variables.Add (name, vi);
2828 if (variables_initialized)
2829 throw new Exception ();
2831 // Console.WriteLine ("Adding {0} to {1}", name, ID);
2835 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
2837 if (AddVariable (type, name, pars, l) == null)
2840 if (constants == null)
2841 constants = new Hashtable ();
2843 constants.Add (name, value);
2847 public Hashtable Variables {
2853 public VariableInfo GetVariableInfo (string name)
2855 if (variables != null) {
2857 temp = variables [name];
2860 return (VariableInfo) temp;
2865 return Parent.GetVariableInfo (name);
2870 public Expression GetVariableType (string name)
2872 VariableInfo vi = GetVariableInfo (name);
2880 public Expression GetConstantExpression (string name)
2882 if (constants != null) {
2884 temp = constants [name];
2887 return (Expression) temp;
2891 return Parent.GetConstantExpression (name);
2897 /// True if the variable named @name is a constant
2899 public bool IsConstant (string name)
2901 Expression e = null;
2903 e = GetConstantExpression (name);
2909 /// Use to fetch the statement associated with this label
2911 public Statement this [string name] {
2913 return (Statement) labels [name];
2917 Parameters parameters = null;
2918 public Parameters Parameters {
2921 return Parent.Parameters;
2928 /// A list of labels that were not used within this block
2930 public string [] GetUnreferenced ()
2932 // FIXME: Implement me
2936 public void AddStatement (Statement s)
2953 bool variables_initialized = false;
2954 int count_variables = 0, first_variable = 0;
2956 void UpdateVariableInfo (EmitContext ec)
2958 DeclSpace ds = ec.DeclSpace;
2963 first_variable += Parent.CountVariables;
2965 count_variables = first_variable;
2966 if (variables != null) {
2967 foreach (VariableInfo vi in variables.Values) {
2968 if (!vi.Resolve (ds)) {
2973 vi.Number = ++count_variables;
2975 if (vi.StructInfo != null)
2976 count_variables += vi.StructInfo.Count;
2980 variables_initialized = true;
2985 // The number of local variables in this block
2987 public int CountVariables
2990 if (!variables_initialized)
2991 throw new Exception ();
2993 return count_variables;
2998 /// Emits the variable declarations and labels.
3001 /// tc: is our typecontainer (to resolve type references)
3002 /// ig: is the code generator:
3003 /// toplevel: the toplevel block. This is used for checking
3004 /// that no two labels with the same name are used.
3006 public void EmitMeta (EmitContext ec, Block toplevel)
3008 DeclSpace ds = ec.DeclSpace;
3009 ILGenerator ig = ec.ig;
3011 if (!variables_initialized)
3012 UpdateVariableInfo (ec);
3014 bool old_check_state = ec.ConstantCheckState;
3015 ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
3016 bool remap_locals = ec.RemapToProxy;
3019 // Process this block variables
3021 if (variables != null){
3022 foreach (DictionaryEntry de in variables){
3023 string name = (string) de.Key;
3024 VariableInfo vi = (VariableInfo) de.Value;
3026 if (vi.VariableType == null)
3029 Type variable_type = vi.VariableType;
3031 if (variable_type.IsPointer){
3033 // Am not really convinced that this test is required (Microsoft does it)
3034 // but the fact is that you would not be able to use the pointer variable
3037 if (!TypeManager.VerifyUnManaged (variable_type.GetElementType (), vi.Location))
3042 vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
3044 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
3046 if (constants == null)
3049 Expression cv = (Expression) constants [name];
3053 Expression e = cv.Resolve (ec);
3057 if (!(e is Constant)){
3058 Report.Error (133, vi.Location,
3059 "The expression being assigned to `" +
3060 name + "' must be constant (" + e + ")");
3064 constants.Remove (name);
3065 constants.Add (name, e);
3068 ec.ConstantCheckState = old_check_state;
3071 // Now, handle the children
3073 if (children != null){
3074 foreach (Block b in children)
3075 b.EmitMeta (ec, toplevel);
3079 public void UsageWarning ()
3083 if (variables != null){
3084 foreach (DictionaryEntry de in variables){
3085 VariableInfo vi = (VariableInfo) de.Value;
3090 name = (string) de.Key;
3094 219, vi.Location, "The variable `" + name +
3095 "' is assigned but its value is never used");
3098 168, vi.Location, "The variable `" +
3100 "' is declared but never used");
3105 if (children != null)
3106 foreach (Block b in children)
3110 bool has_ret = false;
3112 public override bool Resolve (EmitContext ec)
3114 Block prev_block = ec.CurrentBlock;
3117 ec.CurrentBlock = this;
3118 ec.StartFlowBranching (this);
3120 Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
3122 if (!variables_initialized)
3123 UpdateVariableInfo (ec);
3125 ArrayList new_statements = new ArrayList ();
3126 bool unreachable = false, warning_shown = false;
3128 foreach (Statement s in statements){
3129 if (unreachable && !(s is LabeledStatement)) {
3130 if (!warning_shown && !(s is EmptyStatement)) {
3131 warning_shown = true;
3132 Warning_DeadCodeFound (s.loc);
3138 if (s.Resolve (ec) == false) {
3143 if (s is LabeledStatement)
3144 unreachable = false;
3146 unreachable = ! ec.CurrentBranching.IsReachable ();
3148 new_statements.Add (s);
3151 statements = new_statements;
3153 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
3155 FlowReturns returns = ec.EndFlowBranching ();
3156 ec.CurrentBlock = prev_block;
3158 // If we're a non-static `struct' constructor which doesn't have an
3159 // initializer, then we must initialize all of the struct's fields.
3160 if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
3161 !this_variable.IsAssigned (ec, loc))
3164 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
3165 foreach (LabeledStatement label in labels.Values)
3166 if (!label.HasBeenReferenced)
3167 Report.Warning (164, label.Location,
3168 "This label has not been referenced");
3171 if ((returns == FlowReturns.ALWAYS) ||
3172 (returns == FlowReturns.EXCEPTION) ||
3173 (returns == FlowReturns.UNREACHABLE))
3179 protected override bool DoEmit (EmitContext ec)
3181 foreach (Statement s in statements)
3187 public override bool Emit (EmitContext ec)
3189 Block prev_block = ec.CurrentBlock;
3191 ec.CurrentBlock = this;
3193 bool emit_debug_info = (CodeGen.SymbolWriter != null);
3194 bool is_lexical_block = !Implicit && (Parent != null);
3196 if (emit_debug_info) {
3197 if (is_lexical_block)
3198 ec.ig.BeginScope ();
3200 if (variables != null) {
3201 foreach (DictionaryEntry de in variables) {
3202 string name = (string) de.Key;
3203 VariableInfo vi = (VariableInfo) de.Value;
3205 if (vi.LocalBuilder == null)
3208 vi.LocalBuilder.SetLocalSymInfo (name);
3213 ec.Mark (StartLocation, true);
3214 bool retval = DoEmit (ec);
3215 ec.Mark (EndLocation, true);
3217 if (emit_debug_info && is_lexical_block)
3220 ec.CurrentBlock = prev_block;
3226 public class SwitchLabel {
3229 public Location loc;
3230 public Label ILLabel;
3231 public Label ILLabelCode;
3234 // if expr == null, then it is the default case.
3236 public SwitchLabel (Expression expr, Location l)
3242 public Expression Label {
3248 public object Converted {
3255 // Resolves the expression, reduces it to a literal if possible
3256 // and then converts it to the requested type.
3258 public bool ResolveAndReduce (EmitContext ec, Type required_type)
3260 ILLabel = ec.ig.DefineLabel ();
3261 ILLabelCode = ec.ig.DefineLabel ();
3266 Expression e = label.Resolve (ec);
3271 if (!(e is Constant)){
3272 Report.Error (150, loc, "A constant value is expected, got: " + e);
3276 if (e is StringConstant || e is NullLiteral){
3277 if (required_type == TypeManager.string_type){
3279 ILLabel = ec.ig.DefineLabel ();
3284 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
3285 if (converted == null)
3292 public class SwitchSection {
3293 // An array of SwitchLabels.
3294 public readonly ArrayList Labels;
3295 public readonly Block Block;
3297 public SwitchSection (ArrayList labels, Block block)
3304 public class Switch : Statement {
3305 public readonly ArrayList Sections;
3306 public Expression Expr;
3309 /// Maps constants whose type type SwitchType to their SwitchLabels.
3311 public Hashtable Elements;
3314 /// The governing switch type
3316 public Type SwitchType;
3322 Label default_target;
3323 Expression new_expr;
3326 // The types allowed to be implicitly cast from
3327 // on the governing type
3329 static Type [] allowed_types;
3331 public Switch (Expression e, ArrayList sects, Location l)
3338 public bool GotDefault {
3344 public Label DefaultTarget {
3346 return default_target;
3351 // Determines the governing type for a switch. The returned
3352 // expression might be the expression from the switch, or an
3353 // expression that includes any potential conversions to the
3354 // integral types or to string.
3356 Expression SwitchGoverningType (EmitContext ec, Type t)
3358 if (t == TypeManager.int32_type ||
3359 t == TypeManager.uint32_type ||
3360 t == TypeManager.char_type ||
3361 t == TypeManager.byte_type ||
3362 t == TypeManager.sbyte_type ||
3363 t == TypeManager.ushort_type ||
3364 t == TypeManager.short_type ||
3365 t == TypeManager.uint64_type ||
3366 t == TypeManager.int64_type ||
3367 t == TypeManager.string_type ||
3368 t == TypeManager.bool_type ||
3369 t.IsSubclassOf (TypeManager.enum_type))
3372 if (allowed_types == null){
3373 allowed_types = new Type [] {
3374 TypeManager.sbyte_type,
3375 TypeManager.byte_type,
3376 TypeManager.short_type,
3377 TypeManager.ushort_type,
3378 TypeManager.int32_type,
3379 TypeManager.uint32_type,
3380 TypeManager.int64_type,
3381 TypeManager.uint64_type,
3382 TypeManager.char_type,
3383 TypeManager.bool_type,
3384 TypeManager.string_type
3389 // Try to find a *user* defined implicit conversion.
3391 // If there is no implicit conversion, or if there are multiple
3392 // conversions, we have to report an error
3394 Expression converted = null;
3395 foreach (Type tt in allowed_types){
3398 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
3402 if (converted != null){
3403 Report.Error (-12, loc, "More than one conversion to an integral " +
3404 " type exists for type `" +
3405 TypeManager.CSharpName (Expr.Type)+"'");
3413 void error152 (string n)
3416 152, "The label `" + n + ":' " +
3417 "is already present on this switch statement");
3421 // Performs the basic sanity checks on the switch statement
3422 // (looks for duplicate keys and non-constant expressions).
3424 // It also returns a hashtable with the keys that we will later
3425 // use to compute the switch tables
3427 bool CheckSwitch (EmitContext ec)
3431 Elements = new Hashtable ();
3433 got_default = false;
3435 if (TypeManager.IsEnumType (SwitchType)){
3436 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3438 compare_type = SwitchType;
3440 foreach (SwitchSection ss in Sections){
3441 foreach (SwitchLabel sl in ss.Labels){
3442 if (!sl.ResolveAndReduce (ec, SwitchType)){
3447 if (sl.Label == null){
3449 error152 ("default");
3456 object key = sl.Converted;
3458 if (key is Constant)
3459 key = ((Constant) key).GetValue ();
3462 key = NullLiteral.Null;
3464 string lname = null;
3465 if (compare_type == TypeManager.uint64_type){
3466 ulong v = (ulong) key;
3468 if (Elements.Contains (v))
3469 lname = v.ToString ();
3471 Elements.Add (v, sl);
3472 } else if (compare_type == TypeManager.int64_type){
3473 long v = (long) key;
3475 if (Elements.Contains (v))
3476 lname = v.ToString ();
3478 Elements.Add (v, sl);
3479 } else if (compare_type == TypeManager.uint32_type){
3480 uint v = (uint) key;
3482 if (Elements.Contains (v))
3483 lname = v.ToString ();
3485 Elements.Add (v, sl);
3486 } else if (compare_type == TypeManager.char_type){
3487 char v = (char) key;
3489 if (Elements.Contains (v))
3490 lname = v.ToString ();
3492 Elements.Add (v, sl);
3493 } else if (compare_type == TypeManager.byte_type){
3494 byte v = (byte) key;
3496 if (Elements.Contains (v))
3497 lname = v.ToString ();
3499 Elements.Add (v, sl);
3500 } else if (compare_type == TypeManager.sbyte_type){
3501 sbyte v = (sbyte) key;
3503 if (Elements.Contains (v))
3504 lname = v.ToString ();
3506 Elements.Add (v, sl);
3507 } else if (compare_type == TypeManager.short_type){
3508 short v = (short) key;
3510 if (Elements.Contains (v))
3511 lname = v.ToString ();
3513 Elements.Add (v, sl);
3514 } else if (compare_type == TypeManager.ushort_type){
3515 ushort v = (ushort) key;
3517 if (Elements.Contains (v))
3518 lname = v.ToString ();
3520 Elements.Add (v, sl);
3521 } else if (compare_type == TypeManager.string_type){
3522 if (key is NullLiteral){
3523 if (Elements.Contains (NullLiteral.Null))
3526 Elements.Add (NullLiteral.Null, null);
3528 string s = (string) key;
3530 if (Elements.Contains (s))
3533 Elements.Add (s, sl);
3535 } else if (compare_type == TypeManager.int32_type) {
3538 if (Elements.Contains (v))
3539 lname = v.ToString ();
3541 Elements.Add (v, sl);
3542 } else if (compare_type == TypeManager.bool_type) {
3543 bool v = (bool) key;
3545 if (Elements.Contains (v))
3546 lname = v.ToString ();
3548 Elements.Add (v, sl);
3552 throw new Exception ("Unknown switch type!" +
3553 SwitchType + " " + compare_type);
3557 error152 ("case + " + lname);
3568 void EmitObjectInteger (ILGenerator ig, object k)
3571 IntConstant.EmitInt (ig, (int) k);
3572 else if (k is Constant) {
3573 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3576 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3579 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3581 IntConstant.EmitInt (ig, (int) (long) k);
3582 ig.Emit (OpCodes.Conv_I8);
3585 LongConstant.EmitLong (ig, (long) k);
3587 else if (k is ulong)
3589 if ((ulong) k < (1L<<32))
3591 IntConstant.EmitInt (ig, (int) (long) k);
3592 ig.Emit (OpCodes.Conv_U8);
3596 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
3600 IntConstant.EmitInt (ig, (int) ((char) k));
3601 else if (k is sbyte)
3602 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3604 IntConstant.EmitInt (ig, (int) ((byte) k));
3605 else if (k is short)
3606 IntConstant.EmitInt (ig, (int) ((short) k));
3607 else if (k is ushort)
3608 IntConstant.EmitInt (ig, (int) ((ushort) k));
3610 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3612 throw new Exception ("Unhandled case");
3615 // structure used to hold blocks of keys while calculating table switch
3616 class KeyBlock : IComparable
3618 public KeyBlock (long _nFirst)
3620 nFirst = nLast = _nFirst;
3624 public ArrayList rgKeys = null;
3627 get { return (int) (nLast - nFirst + 1); }
3629 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3631 return kbLast.nLast - kbFirst.nFirst + 1;
3633 public int CompareTo (object obj)
3635 KeyBlock kb = (KeyBlock) obj;
3636 int nLength = Length;
3637 int nLengthOther = kb.Length;
3638 if (nLengthOther == nLength)
3639 return (int) (kb.nFirst - nFirst);
3640 return nLength - nLengthOther;
3645 /// This method emits code for a lookup-based switch statement (non-string)
3646 /// Basically it groups the cases into blocks that are at least half full,
3647 /// and then spits out individual lookup opcodes for each block.
3648 /// It emits the longest blocks first, and short blocks are just
3649 /// handled with direct compares.
3651 /// <param name="ec"></param>
3652 /// <param name="val"></param>
3653 /// <returns></returns>
3654 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
3656 int cElements = Elements.Count;
3657 object [] rgKeys = new object [cElements];
3658 Elements.Keys.CopyTo (rgKeys, 0);
3659 Array.Sort (rgKeys);
3661 // initialize the block list with one element per key
3662 ArrayList rgKeyBlocks = new ArrayList ();
3663 foreach (object key in rgKeys)
3664 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
3667 // iteratively merge the blocks while they are at least half full
3668 // there's probably a really cool way to do this with a tree...
3669 while (rgKeyBlocks.Count > 1)
3671 ArrayList rgKeyBlocksNew = new ArrayList ();
3672 kbCurr = (KeyBlock) rgKeyBlocks [0];
3673 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3675 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3676 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3679 kbCurr.nLast = kb.nLast;
3683 // start a new block
3684 rgKeyBlocksNew.Add (kbCurr);
3688 rgKeyBlocksNew.Add (kbCurr);
3689 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3691 rgKeyBlocks = rgKeyBlocksNew;
3694 // initialize the key lists
3695 foreach (KeyBlock kb in rgKeyBlocks)
3696 kb.rgKeys = new ArrayList ();
3698 // fill the key lists
3700 if (rgKeyBlocks.Count > 0) {
3701 kbCurr = (KeyBlock) rgKeyBlocks [0];
3702 foreach (object key in rgKeys)
3704 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
3706 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3707 kbCurr.rgKeys.Add (key);
3711 // sort the blocks so we can tackle the largest ones first
3712 rgKeyBlocks.Sort ();
3714 // okay now we can start...
3715 ILGenerator ig = ec.ig;
3716 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3717 Label lblDefault = ig.DefineLabel ();
3719 Type typeKeys = null;
3720 if (rgKeys.Length > 0)
3721 typeKeys = rgKeys [0].GetType (); // used for conversions
3723 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3725 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3726 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3729 foreach (object key in kb.rgKeys)
3731 ig.Emit (OpCodes.Ldloc, val);
3732 EmitObjectInteger (ig, key);
3733 SwitchLabel sl = (SwitchLabel) Elements [key];
3734 ig.Emit (OpCodes.Beq, sl.ILLabel);
3739 // TODO: if all the keys in the block are the same and there are
3740 // no gaps/defaults then just use a range-check.
3741 if (SwitchType == TypeManager.int64_type ||
3742 SwitchType == TypeManager.uint64_type)
3744 // TODO: optimize constant/I4 cases
3746 // check block range (could be > 2^31)
3747 ig.Emit (OpCodes.Ldloc, val);
3748 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3749 ig.Emit (OpCodes.Blt, lblDefault);
3750 ig.Emit (OpCodes.Ldloc, val);
3751 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3752 ig.Emit (OpCodes.Bgt, lblDefault);
3755 ig.Emit (OpCodes.Ldloc, val);
3758 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3759 ig.Emit (OpCodes.Sub);
3761 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3766 ig.Emit (OpCodes.Ldloc, val);
3767 int nFirst = (int) kb.nFirst;
3770 IntConstant.EmitInt (ig, nFirst);
3771 ig.Emit (OpCodes.Sub);
3773 else if (nFirst < 0)
3775 IntConstant.EmitInt (ig, -nFirst);
3776 ig.Emit (OpCodes.Add);
3780 // first, build the list of labels for the switch
3782 int cJumps = kb.Length;
3783 Label [] rgLabels = new Label [cJumps];
3784 for (int iJump = 0; iJump < cJumps; iJump++)
3786 object key = kb.rgKeys [iKey];
3787 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
3789 SwitchLabel sl = (SwitchLabel) Elements [key];
3790 rgLabels [iJump] = sl.ILLabel;
3794 rgLabels [iJump] = lblDefault;
3796 // emit the switch opcode
3797 ig.Emit (OpCodes.Switch, rgLabels);
3800 // mark the default for this block
3802 ig.MarkLabel (lblDefault);
3805 // TODO: find the default case and emit it here,
3806 // to prevent having to do the following jump.
3807 // make sure to mark other labels in the default section
3809 // the last default just goes to the end
3810 ig.Emit (OpCodes.Br, lblDefault);
3812 // now emit the code for the sections
3813 bool fFoundDefault = false;
3814 bool fAllReturn = true;
3815 foreach (SwitchSection ss in Sections)
3817 foreach (SwitchLabel sl in ss.Labels)
3819 ig.MarkLabel (sl.ILLabel);
3820 ig.MarkLabel (sl.ILLabelCode);
3821 if (sl.Label == null)
3823 ig.MarkLabel (lblDefault);
3824 fFoundDefault = true;
3827 bool returns = ss.Block.Emit (ec);
3828 fAllReturn &= returns;
3829 //ig.Emit (OpCodes.Br, lblEnd);
3832 if (!fFoundDefault) {
3833 ig.MarkLabel (lblDefault);
3836 ig.MarkLabel (lblEnd);
3841 // This simple emit switch works, but does not take advantage of the
3843 // TODO: remove non-string logic from here
3844 // TODO: binary search strings?
3846 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3848 ILGenerator ig = ec.ig;
3849 Label end_of_switch = ig.DefineLabel ();
3850 Label next_test = ig.DefineLabel ();
3851 Label null_target = ig.DefineLabel ();
3852 bool default_found = false;
3853 bool first_test = true;
3854 bool pending_goto_end = false;
3855 bool all_return = true;
3858 ig.Emit (OpCodes.Ldloc, val);
3860 if (Elements.Contains (NullLiteral.Null)){
3861 ig.Emit (OpCodes.Brfalse, null_target);
3863 ig.Emit (OpCodes.Brfalse, default_target);
3865 ig.Emit (OpCodes.Ldloc, val);
3866 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
3867 ig.Emit (OpCodes.Stloc, val);
3869 int section_count = Sections.Count;
3870 for (int section = 0; section < section_count; section++){
3871 SwitchSection ss = (SwitchSection) Sections [section];
3872 Label sec_begin = ig.DefineLabel ();
3874 if (pending_goto_end)
3875 ig.Emit (OpCodes.Br, end_of_switch);
3877 int label_count = ss.Labels.Count;
3879 for (int label = 0; label < label_count; label++){
3880 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3881 ig.MarkLabel (sl.ILLabel);
3884 ig.MarkLabel (next_test);
3885 next_test = ig.DefineLabel ();
3888 // If we are the default target
3890 if (sl.Label == null){
3891 ig.MarkLabel (default_target);
3892 default_found = true;
3894 object lit = sl.Converted;
3896 if (lit is NullLiteral){
3898 if (label_count == 1)
3899 ig.Emit (OpCodes.Br, next_test);
3903 StringConstant str = (StringConstant) lit;
3905 ig.Emit (OpCodes.Ldloc, val);
3906 ig.Emit (OpCodes.Ldstr, str.Value);
3907 if (label_count == 1)
3908 ig.Emit (OpCodes.Bne_Un, next_test);
3910 if (label+1 == label_count)
3911 ig.Emit (OpCodes.Bne_Un, next_test);
3913 ig.Emit (OpCodes.Beq, sec_begin);
3918 ig.MarkLabel (null_target);
3919 ig.MarkLabel (sec_begin);
3920 foreach (SwitchLabel sl in ss.Labels)
3921 ig.MarkLabel (sl.ILLabelCode);
3923 bool returns = ss.Block.Emit (ec);
3925 pending_goto_end = false;
3928 pending_goto_end = true;
3932 if (!default_found){
3933 ig.MarkLabel (default_target);
3936 ig.MarkLabel (next_test);
3937 ig.MarkLabel (end_of_switch);
3942 public override bool Resolve (EmitContext ec)
3944 Expr = Expr.Resolve (ec);
3948 new_expr = SwitchGoverningType (ec, Expr.Type);
3949 if (new_expr == null){
3950 Report.Error (151, loc, "An integer type or string was expected for switch");
3955 SwitchType = new_expr.Type;
3957 if (!CheckSwitch (ec))
3960 Switch old_switch = ec.Switch;
3962 ec.Switch.SwitchType = SwitchType;
3964 ec.StartFlowBranching (FlowBranchingType.SWITCH, loc);
3967 foreach (SwitchSection ss in Sections){
3969 ec.CurrentBranching.CreateSibling ();
3973 if (ss.Block.Resolve (ec) != true)
3979 ec.CurrentBranching.CreateSibling ();
3981 ec.EndFlowBranching ();
3982 ec.Switch = old_switch;
3987 protected override bool DoEmit (EmitContext ec)
3989 // Store variable for comparission purposes
3990 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
3992 ec.ig.Emit (OpCodes.Stloc, value);
3994 ILGenerator ig = ec.ig;
3996 default_target = ig.DefineLabel ();
3999 // Setup the codegen context
4001 Label old_end = ec.LoopEnd;
4002 Switch old_switch = ec.Switch;
4004 ec.LoopEnd = ig.DefineLabel ();
4009 if (SwitchType == TypeManager.string_type)
4010 all_return = SimpleSwitchEmit (ec, value);
4012 all_return = TableSwitchEmit (ec, value);
4014 // Restore context state.
4015 ig.MarkLabel (ec.LoopEnd);
4018 // Restore the previous context
4020 ec.LoopEnd = old_end;
4021 ec.Switch = old_switch;
4027 public class Lock : Statement {
4029 Statement Statement;
4031 public Lock (Expression expr, Statement stmt, Location l)
4038 public override bool Resolve (EmitContext ec)
4040 expr = expr.Resolve (ec);
4041 return Statement.Resolve (ec) && expr != null;
4044 protected override bool DoEmit (EmitContext ec)
4046 Type type = expr.Type;
4049 if (type.IsValueType){
4050 Report.Error (185, loc, "lock statement requires the expression to be " +
4051 " a reference type (type is: `" +
4052 TypeManager.CSharpName (type) + "'");
4056 ILGenerator ig = ec.ig;
4057 LocalBuilder temp = ig.DeclareLocal (type);
4060 ig.Emit (OpCodes.Dup);
4061 ig.Emit (OpCodes.Stloc, temp);
4062 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4065 Label end = ig.BeginExceptionBlock ();
4066 bool old_in_try = ec.InTry;
4068 Label finish = ig.DefineLabel ();
4069 val = Statement.Emit (ec);
4070 ec.InTry = old_in_try;
4071 // ig.Emit (OpCodes.Leave, finish);
4073 ig.MarkLabel (finish);
4076 ig.BeginFinallyBlock ();
4077 ig.Emit (OpCodes.Ldloc, temp);
4078 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4079 ig.EndExceptionBlock ();
4085 public class Unchecked : Statement {
4086 public readonly Block Block;
4088 public Unchecked (Block b)
4094 public override bool Resolve (EmitContext ec)
4096 bool previous_state = ec.CheckState;
4097 bool previous_state_const = ec.ConstantCheckState;
4099 ec.CheckState = false;
4100 ec.ConstantCheckState = false;
4101 bool ret = Block.Resolve (ec);
4102 ec.CheckState = previous_state;
4103 ec.ConstantCheckState = previous_state_const;
4108 protected override bool DoEmit (EmitContext ec)
4110 bool previous_state = ec.CheckState;
4111 bool previous_state_const = ec.ConstantCheckState;
4114 ec.CheckState = false;
4115 ec.ConstantCheckState = false;
4116 val = Block.Emit (ec);
4117 ec.CheckState = previous_state;
4118 ec.ConstantCheckState = previous_state_const;
4124 public class Checked : Statement {
4125 public readonly Block Block;
4127 public Checked (Block b)
4130 b.Unchecked = false;
4133 public override bool Resolve (EmitContext ec)
4135 bool previous_state = ec.CheckState;
4136 bool previous_state_const = ec.ConstantCheckState;
4138 ec.CheckState = true;
4139 ec.ConstantCheckState = true;
4140 bool ret = Block.Resolve (ec);
4141 ec.CheckState = previous_state;
4142 ec.ConstantCheckState = previous_state_const;
4147 protected override bool DoEmit (EmitContext ec)
4149 bool previous_state = ec.CheckState;
4150 bool previous_state_const = ec.ConstantCheckState;
4153 ec.CheckState = true;
4154 ec.ConstantCheckState = true;
4155 val = Block.Emit (ec);
4156 ec.CheckState = previous_state;
4157 ec.ConstantCheckState = previous_state_const;
4163 public class Unsafe : Statement {
4164 public readonly Block Block;
4166 public Unsafe (Block b)
4171 public override bool Resolve (EmitContext ec)
4173 bool previous_state = ec.InUnsafe;
4177 val = Block.Resolve (ec);
4178 ec.InUnsafe = previous_state;
4183 protected override bool DoEmit (EmitContext ec)
4185 bool previous_state = ec.InUnsafe;
4189 val = Block.Emit (ec);
4190 ec.InUnsafe = previous_state;
4199 public class Fixed : Statement {
4201 ArrayList declarators;
4202 Statement statement;
4207 public bool is_object;
4208 public VariableInfo vi;
4209 public Expression expr;
4210 public Expression converted;
4213 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4216 declarators = decls;
4221 public override bool Resolve (EmitContext ec)
4224 Expression.UnsafeError (loc);
4228 expr_type = ec.DeclSpace.ResolveType (type, false, loc);
4229 if (expr_type == null)
4232 if (ec.RemapToProxy){
4233 Report.Error (-210, loc, "Fixed statement not allowed in iterators");
4237 data = new FixedData [declarators.Count];
4239 if (!expr_type.IsPointer){
4240 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
4245 foreach (Pair p in declarators){
4246 VariableInfo vi = (VariableInfo) p.First;
4247 Expression e = (Expression) p.Second;
4252 // The rules for the possible declarators are pretty wise,
4253 // but the production on the grammar is more concise.
4255 // So we have to enforce these rules here.
4257 // We do not resolve before doing the case 1 test,
4258 // because the grammar is explicit in that the token &
4259 // is present, so we need to test for this particular case.
4263 // Case 1: & object.
4265 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4266 Expression child = ((Unary) e).Expr;
4269 if (child is ParameterReference || child is LocalVariableReference){
4272 "No need to use fixed statement for parameters or " +
4273 "local variable declarations (address is already " +
4282 child = ((Unary) e).Expr;
4284 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4287 data [i].is_object = true;
4289 data [i].converted = null;
4303 if (e.Type.IsArray){
4304 Type array_type = e.Type.GetElementType ();
4308 // Provided that array_type is unmanaged,
4310 if (!TypeManager.VerifyUnManaged (array_type, loc))
4314 // and T* is implicitly convertible to the
4315 // pointer type given in the fixed statement.
4317 ArrayPtr array_ptr = new ArrayPtr (e, loc);
4319 Expression converted = Expression.ConvertImplicitRequired (
4320 ec, array_ptr, vi.VariableType, loc);
4321 if (converted == null)
4324 data [i].is_object = false;
4326 data [i].converted = converted;
4336 if (e.Type == TypeManager.string_type){
4337 data [i].is_object = false;
4339 data [i].converted = null;
4345 return statement.Resolve (ec);
4348 protected override bool DoEmit (EmitContext ec)
4350 ILGenerator ig = ec.ig;
4352 bool is_ret = false;
4353 LocalBuilder [] clear_list = new LocalBuilder [data.Length];
4355 for (int i = 0; i < data.Length; i++) {
4356 VariableInfo vi = data [i].vi;
4359 // Case 1: & object.
4361 if (data [i].is_object) {
4363 // Store pointer in pinned location
4365 data [i].expr.Emit (ec);
4366 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4367 clear_list [i] = vi.LocalBuilder;
4374 if (data [i].expr.Type.IsArray){
4376 // Store pointer in pinned location
4378 data [i].converted.Emit (ec);
4380 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4381 clear_list [i] = vi.LocalBuilder;
4388 if (data [i].expr.Type == TypeManager.string_type){
4389 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
4390 TypeManager.MakePinned (pinned_string);
4391 clear_list [i] = pinned_string;
4393 data [i].expr.Emit (ec);
4394 ig.Emit (OpCodes.Stloc, pinned_string);
4396 Expression sptr = new StringPtr (pinned_string, loc);
4397 Expression converted = Expression.ConvertImplicitRequired (
4398 ec, sptr, vi.VariableType, loc);
4400 if (converted == null)
4403 converted.Emit (ec);
4404 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4408 is_ret = statement.Emit (ec);
4413 // Clear the pinned variable
4415 for (int i = 0; i < data.Length; i++) {
4416 VariableInfo vi = data [i].vi;
4418 if (data [i].is_object || data [i].expr.Type.IsArray) {
4419 ig.Emit (OpCodes.Ldc_I4_0);
4420 ig.Emit (OpCodes.Conv_U);
4421 ig.Emit (OpCodes.Stloc, clear_list [i]);
4422 } else if (data [i].expr.Type == TypeManager.string_type){
4423 ig.Emit (OpCodes.Ldnull);
4424 ig.Emit (OpCodes.Stloc, clear_list [i]);
4432 public class Catch {
4433 public readonly string Name;
4434 public readonly Block Block;
4435 public readonly Location Location;
4437 Expression type_expr;
4440 public Catch (Expression type, string name, Block block, Location l)
4448 public Type CatchType {
4454 public bool IsGeneral {
4456 return type_expr == null;
4460 public bool Resolve (EmitContext ec)
4462 if (type_expr != null) {
4463 type = ec.DeclSpace.ResolveType (type_expr, false, Location);
4467 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4468 Report.Error (155, Location,
4469 "The type caught or thrown must be derived " +
4470 "from System.Exception");
4476 if (!Block.Resolve (ec))
4483 public class Try : Statement {
4484 public readonly Block Fini, Block;
4485 public readonly ArrayList Specific;
4486 public readonly Catch General;
4489 // specific, general and fini might all be null.
4491 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4493 if (specific == null && general == null){
4494 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4498 this.Specific = specific;
4499 this.General = general;
4504 public override bool Resolve (EmitContext ec)
4508 ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation);
4510 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4512 bool old_in_try = ec.InTry;
4515 if (!Block.Resolve (ec))
4518 ec.InTry = old_in_try;
4520 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4522 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4524 foreach (Catch c in Specific){
4525 ec.CurrentBranching.CreateSibling ();
4526 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4528 if (c.Name != null) {
4529 VariableInfo vi = c.Block.GetVariableInfo (c.Name);
4531 throw new Exception ();
4536 bool old_in_catch = ec.InCatch;
4539 if (!c.Resolve (ec))
4542 ec.InCatch = old_in_catch;
4544 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4546 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4547 vector.AndLocals (current);
4549 vector.Or (current);
4552 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4554 if (General != null){
4555 ec.CurrentBranching.CreateSibling ();
4556 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4558 bool old_in_catch = ec.InCatch;
4561 if (!General.Resolve (ec))
4564 ec.InCatch = old_in_catch;
4566 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4568 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4569 vector.AndLocals (current);
4571 vector.Or (current);
4574 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4577 ec.CurrentBranching.CreateSiblingForFinally ();
4578 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4580 bool old_in_finally = ec.InFinally;
4581 ec.InFinally = true;
4583 if (!Fini.Resolve (ec))
4586 ec.InFinally = old_in_finally;
4589 FlowReturns returns = ec.EndFlowBranching ();
4591 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4593 Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector);
4595 if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) {
4596 ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc);
4599 ec.CurrentBranching.CurrentUsageVector.Or (vector);
4601 Report.Debug (1, "END OF TRY", ec.CurrentBranching);
4606 protected override bool DoEmit (EmitContext ec)
4608 ILGenerator ig = ec.ig;
4610 Label finish = ig.DefineLabel ();;
4614 end = ig.BeginExceptionBlock ();
4615 bool old_in_try = ec.InTry;
4617 returns = Block.Emit (ec);
4618 ec.InTry = old_in_try;
4621 // System.Reflection.Emit provides this automatically:
4622 // ig.Emit (OpCodes.Leave, finish);
4624 bool old_in_catch = ec.InCatch;
4626 DeclSpace ds = ec.DeclSpace;
4628 foreach (Catch c in Specific){
4631 ig.BeginCatchBlock (c.CatchType);
4633 if (c.Name != null){
4634 vi = c.Block.GetVariableInfo (c.Name);
4636 throw new Exception ("Variable does not exist in this block");
4638 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4640 ig.Emit (OpCodes.Pop);
4642 if (!c.Block.Emit (ec))
4646 if (General != null){
4647 ig.BeginCatchBlock (TypeManager.object_type);
4648 ig.Emit (OpCodes.Pop);
4649 if (!General.Block.Emit (ec))
4652 ec.InCatch = old_in_catch;
4654 ig.MarkLabel (finish);
4656 ig.BeginFinallyBlock ();
4657 bool old_in_finally = ec.InFinally;
4658 ec.InFinally = true;
4660 ec.InFinally = old_in_finally;
4663 ig.EndExceptionBlock ();
4666 if (!returns || ec.InTry || ec.InCatch)
4669 // Unfortunately, System.Reflection.Emit automatically emits a leave
4670 // to the end of the finally block. This is a problem if `returns'
4671 // is true since we may jump to a point after the end of the method.
4672 // As a workaround, emit an explicit ret here.
4674 if (ec.ReturnType != null)
4675 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4676 ec.ig.Emit (OpCodes.Ret);
4682 public class Using : Statement {
4683 object expression_or_block;
4684 Statement Statement;
4689 Expression [] converted_vars;
4690 ExpressionStatement [] assign;
4692 public Using (object expression_or_block, Statement stmt, Location l)
4694 this.expression_or_block = expression_or_block;
4700 // Resolves for the case of using using a local variable declaration.
4702 bool ResolveLocalVariableDecls (EmitContext ec)
4704 bool need_conv = false;
4705 expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
4708 if (expr_type == null)
4712 // The type must be an IDisposable or an implicit conversion
4715 converted_vars = new Expression [var_list.Count];
4716 assign = new ExpressionStatement [var_list.Count];
4717 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4718 foreach (DictionaryEntry e in var_list){
4719 Expression var = (Expression) e.Key;
4721 var = var.ResolveLValue (ec, new EmptyExpression ());
4725 converted_vars [i] = Expression.ConvertImplicitRequired (
4726 ec, var, TypeManager.idisposable_type, loc);
4728 if (converted_vars [i] == null)
4736 foreach (DictionaryEntry e in var_list){
4737 LocalVariableReference var = (LocalVariableReference) e.Key;
4738 Expression new_expr = (Expression) e.Value;
4741 a = new Assign (var, new_expr, loc);
4747 converted_vars [i] = var;
4748 assign [i] = (ExpressionStatement) a;
4755 bool ResolveExpression (EmitContext ec)
4757 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4758 conv = Expression.ConvertImplicitRequired (
4759 ec, expr, TypeManager.idisposable_type, loc);
4769 // Emits the code for the case of using using a local variable declaration.
4771 bool EmitLocalVariableDecls (EmitContext ec)
4773 ILGenerator ig = ec.ig;
4776 bool old_in_try = ec.InTry;
4778 for (i = 0; i < assign.Length; i++) {
4779 assign [i].EmitStatement (ec);
4781 ig.BeginExceptionBlock ();
4783 Statement.Emit (ec);
4784 ec.InTry = old_in_try;
4786 bool old_in_finally = ec.InFinally;
4787 ec.InFinally = true;
4788 var_list.Reverse ();
4789 foreach (DictionaryEntry e in var_list){
4790 LocalVariableReference var = (LocalVariableReference) e.Key;
4791 Label skip = ig.DefineLabel ();
4794 ig.BeginFinallyBlock ();
4797 ig.Emit (OpCodes.Brfalse, skip);
4798 converted_vars [i].Emit (ec);
4799 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4800 ig.MarkLabel (skip);
4801 ig.EndExceptionBlock ();
4803 ec.InFinally = old_in_finally;
4808 bool EmitExpression (EmitContext ec)
4811 // Make a copy of the expression and operate on that.
4813 ILGenerator ig = ec.ig;
4814 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
4819 ig.Emit (OpCodes.Stloc, local_copy);
4821 bool old_in_try = ec.InTry;
4823 ig.BeginExceptionBlock ();
4824 Statement.Emit (ec);
4825 ec.InTry = old_in_try;
4827 Label skip = ig.DefineLabel ();
4828 bool old_in_finally = ec.InFinally;
4829 ig.BeginFinallyBlock ();
4830 ig.Emit (OpCodes.Ldloc, local_copy);
4831 ig.Emit (OpCodes.Brfalse, skip);
4832 ig.Emit (OpCodes.Ldloc, local_copy);
4833 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4834 ig.MarkLabel (skip);
4835 ec.InFinally = old_in_finally;
4836 ig.EndExceptionBlock ();
4841 public override bool Resolve (EmitContext ec)
4843 if (expression_or_block is DictionaryEntry){
4844 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4845 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4847 if (!ResolveLocalVariableDecls (ec))
4850 } else if (expression_or_block is Expression){
4851 expr = (Expression) expression_or_block;
4853 expr = expr.Resolve (ec);
4857 expr_type = expr.Type;
4859 if (!ResolveExpression (ec))
4863 return Statement.Resolve (ec);
4866 protected override bool DoEmit (EmitContext ec)
4868 if (expression_or_block is DictionaryEntry)
4869 return EmitLocalVariableDecls (ec);
4870 else if (expression_or_block is Expression)
4871 return EmitExpression (ec);
4878 /// Implementation of the foreach C# statement
4880 public class Foreach : Statement {
4882 LocalVariableReference variable;
4884 Statement statement;
4885 ForeachHelperMethods hm;
4886 Expression empty, conv;
4887 Type array_type, element_type;
4890 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4891 Statement stmt, Location l)
4894 this.variable = var;
4900 public override bool Resolve (EmitContext ec)
4902 expr = expr.Resolve (ec);
4906 var_type = ec.DeclSpace.ResolveType (type, false, loc);
4907 if (var_type == null)
4911 // We need an instance variable. Not sure this is the best
4912 // way of doing this.
4914 // FIXME: When we implement propertyaccess, will those turn
4915 // out to return values in ExprClass? I think they should.
4917 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4918 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4919 error1579 (expr.Type);
4923 if (expr.Type.IsArray) {
4924 array_type = expr.Type;
4925 element_type = array_type.GetElementType ();
4927 empty = new EmptyExpression (element_type);
4929 hm = ProbeCollectionType (ec, expr.Type);
4931 error1579 (expr.Type);
4935 array_type = expr.Type;
4936 element_type = hm.element_type;
4938 empty = new EmptyExpression (hm.element_type);
4941 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
4942 ec.CurrentBranching.CreateSibling ();
4946 // FIXME: maybe we can apply the same trick we do in the
4947 // array handling to avoid creating empty and conv in some cases.
4949 // Although it is not as important in this case, as the type
4950 // will not likely be object (what the enumerator will return).
4952 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
4956 if (variable.ResolveLValue (ec, empty) == null)
4959 if (!statement.Resolve (ec))
4962 FlowReturns returns = ec.EndFlowBranching ();
4968 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4970 static MethodInfo FetchMethodMoveNext (Type t)
4972 MemberList move_next_list;
4974 move_next_list = TypeContainer.FindMembers (
4975 t, MemberTypes.Method,
4976 BindingFlags.Public | BindingFlags.Instance,
4977 Type.FilterName, "MoveNext");
4978 if (move_next_list.Count == 0)
4981 foreach (MemberInfo m in move_next_list){
4982 MethodInfo mi = (MethodInfo) m;
4985 args = TypeManager.GetArgumentTypes (mi);
4986 if (args != null && args.Length == 0){
4987 if (mi.ReturnType == TypeManager.bool_type)
4995 // Retrieves a `public T get_Current ()' method from the Type `t'
4997 static MethodInfo FetchMethodGetCurrent (Type t)
4999 MemberList move_next_list;
5001 move_next_list = TypeContainer.FindMembers (
5002 t, MemberTypes.Method,
5003 BindingFlags.Public | BindingFlags.Instance,
5004 Type.FilterName, "get_Current");
5005 if (move_next_list.Count == 0)
5008 foreach (MemberInfo m in move_next_list){
5009 MethodInfo mi = (MethodInfo) m;
5012 args = TypeManager.GetArgumentTypes (mi);
5013 if (args != null && args.Length == 0)
5020 // This struct records the helper methods used by the Foreach construct
5022 class ForeachHelperMethods {
5023 public EmitContext ec;
5024 public MethodInfo get_enumerator;
5025 public MethodInfo move_next;
5026 public MethodInfo get_current;
5027 public Type element_type;
5028 public Type enumerator_type;
5029 public bool is_disposable;
5031 public ForeachHelperMethods (EmitContext ec)
5034 this.element_type = TypeManager.object_type;
5035 this.enumerator_type = TypeManager.ienumerator_type;
5036 this.is_disposable = true;
5040 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
5045 if (!(m is MethodInfo))
5048 if (m.Name != "GetEnumerator")
5051 MethodInfo mi = (MethodInfo) m;
5052 Type [] args = TypeManager.GetArgumentTypes (mi);
5054 if (args.Length != 0)
5057 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
5058 EmitContext ec = hm.ec;
5061 // Check whether GetEnumerator is accessible to us
5063 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
5065 Type declaring = mi.DeclaringType;
5066 if (prot == MethodAttributes.Private){
5067 if (declaring != ec.ContainerType)
5069 } else if (prot == MethodAttributes.FamANDAssem){
5070 // If from a different assembly, false
5071 if (!(mi is MethodBuilder))
5074 // Are we being invoked from the same class, or from a derived method?
5076 if (ec.ContainerType != declaring){
5077 if (!ec.ContainerType.IsSubclassOf (declaring))
5080 } else if (prot == MethodAttributes.FamORAssem){
5081 if (!(mi is MethodBuilder ||
5082 ec.ContainerType == declaring ||
5083 ec.ContainerType.IsSubclassOf (declaring)))
5085 } if (prot == MethodAttributes.Family){
5086 if (!(ec.ContainerType == declaring ||
5087 ec.ContainerType.IsSubclassOf (declaring)))
5091 if ((mi.ReturnType == TypeManager.ienumerator_type) && (declaring == TypeManager.string_type))
5093 // Apply the same optimization as MS: skip the GetEnumerator
5094 // returning an IEnumerator, and use the one returning a
5095 // CharEnumerator instead. This allows us to avoid the
5096 // try-finally block and the boxing.
5101 // Ok, we can access it, now make sure that we can do something
5102 // with this `GetEnumerator'
5105 if (mi.ReturnType == TypeManager.ienumerator_type ||
5106 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
5107 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
5108 if (declaring != TypeManager.string_type) {
5109 hm.move_next = TypeManager.bool_movenext_void;
5110 hm.get_current = TypeManager.object_getcurrent_void;
5116 // Ok, so they dont return an IEnumerable, we will have to
5117 // find if they support the GetEnumerator pattern.
5119 Type return_type = mi.ReturnType;
5121 hm.move_next = FetchMethodMoveNext (return_type);
5122 if (hm.move_next == null)
5124 hm.get_current = FetchMethodGetCurrent (return_type);
5125 if (hm.get_current == null)
5128 hm.element_type = hm.get_current.ReturnType;
5129 hm.enumerator_type = return_type;
5130 hm.is_disposable = !hm.enumerator_type.IsSealed ||
5131 TypeManager.ImplementsInterface (
5132 hm.enumerator_type, TypeManager.idisposable_type);
5138 /// This filter is used to find the GetEnumerator method
5139 /// on which IEnumerator operates
5141 static MemberFilter FilterEnumerator;
5145 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
5148 void error1579 (Type t)
5150 Report.Error (1579, loc,
5151 "foreach statement cannot operate on variables of type `" +
5152 t.FullName + "' because that class does not provide a " +
5153 " GetEnumerator method or it is inaccessible");
5156 static bool TryType (Type t, ForeachHelperMethods hm)
5160 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
5161 BindingFlags.Public | BindingFlags.NonPublic |
5162 BindingFlags.Instance,
5163 FilterEnumerator, hm);
5168 hm.get_enumerator = (MethodInfo) mi [0];
5173 // Looks for a usable GetEnumerator in the Type, and if found returns
5174 // the three methods that participate: GetEnumerator, MoveNext and get_Current
5176 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
5178 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
5180 if (TryType (t, hm))
5184 // Now try to find the method in the interfaces
5187 Type [] ifaces = t.GetInterfaces ();
5189 foreach (Type i in ifaces){
5190 if (TryType (i, hm))
5195 // Since TypeBuilder.GetInterfaces only returns the interface
5196 // types for this type, we have to keep looping, but once
5197 // we hit a non-TypeBuilder (ie, a Type), then we know we are
5198 // done, because it returns all the types
5200 if ((t is TypeBuilder))
5210 // FIXME: possible optimization.
5211 // We might be able to avoid creating `empty' if the type is the sam
5213 bool EmitCollectionForeach (EmitContext ec)
5215 ILGenerator ig = ec.ig;
5216 LocalBuilder enumerator, disposable;
5218 enumerator = ig.DeclareLocal (hm.enumerator_type);
5219 if (hm.is_disposable)
5220 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
5225 // Instantiate the enumerator
5227 if (expr.Type.IsValueType){
5228 if (expr is IMemoryLocation){
5229 IMemoryLocation ml = (IMemoryLocation) expr;
5231 ml.AddressOf (ec, AddressOp.Load);
5233 throw new Exception ("Expr " + expr + " of type " + expr.Type +
5234 " does not implement IMemoryLocation");
5235 ig.Emit (OpCodes.Call, hm.get_enumerator);
5238 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
5240 ig.Emit (OpCodes.Stloc, enumerator);
5243 // Protect the code in a try/finalize block, so that
5244 // if the beast implement IDisposable, we get rid of it
5247 bool old_in_try = ec.InTry;
5249 if (hm.is_disposable) {
5250 l = ig.BeginExceptionBlock ();
5254 Label end_try = ig.DefineLabel ();
5256 ig.MarkLabel (ec.LoopBegin);
5257 ig.Emit (OpCodes.Ldloc, enumerator);
5258 ig.Emit (OpCodes.Callvirt, hm.move_next);
5259 ig.Emit (OpCodes.Brfalse, end_try);
5260 ig.Emit (OpCodes.Ldloc, enumerator);
5261 ig.Emit (OpCodes.Callvirt, hm.get_current);
5263 variable.EmitAssign (ec, conv);
5264 statement.Emit (ec);
5265 ig.Emit (OpCodes.Br, ec.LoopBegin);
5266 ig.MarkLabel (end_try);
5267 ec.InTry = old_in_try;
5269 // The runtime provides this for us.
5270 // ig.Emit (OpCodes.Leave, end);
5273 // Now the finally block
5275 if (hm.is_disposable) {
5276 Label end_finally = ig.DefineLabel ();
5277 bool old_in_finally = ec.InFinally;
5278 ec.InFinally = true;
5279 ig.BeginFinallyBlock ();
5281 ig.Emit (OpCodes.Ldloc, enumerator);
5282 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5283 ig.Emit (OpCodes.Stloc, disposable);
5284 ig.Emit (OpCodes.Ldloc, disposable);
5285 ig.Emit (OpCodes.Brfalse, end_finally);
5286 ig.Emit (OpCodes.Ldloc, disposable);
5287 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5288 ig.MarkLabel (end_finally);
5289 ec.InFinally = old_in_finally;
5291 // The runtime generates this anyways.
5292 // ig.Emit (OpCodes.Endfinally);
5294 ig.EndExceptionBlock ();
5297 ig.MarkLabel (ec.LoopEnd);
5302 // FIXME: possible optimization.
5303 // We might be able to avoid creating `empty' if the type is the sam
5305 bool EmitArrayForeach (EmitContext ec)
5307 int rank = array_type.GetArrayRank ();
5308 ILGenerator ig = ec.ig;
5310 LocalBuilder copy = ig.DeclareLocal (array_type);
5313 // Make our copy of the array
5316 ig.Emit (OpCodes.Stloc, copy);
5319 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
5323 ig.Emit (OpCodes.Ldc_I4_0);
5324 ig.Emit (OpCodes.Stloc, counter);
5325 test = ig.DefineLabel ();
5326 ig.Emit (OpCodes.Br, test);
5328 loop = ig.DefineLabel ();
5329 ig.MarkLabel (loop);
5331 ig.Emit (OpCodes.Ldloc, copy);
5332 ig.Emit (OpCodes.Ldloc, counter);
5335 // Load the value, we load the value using the underlying type,
5336 // then we use the variable.EmitAssign to load using the proper cast.
5338 ArrayAccess.EmitLoadOpcode (ig, element_type);
5339 variable.EmitAssign (ec, conv);
5341 statement.Emit (ec);
5343 ig.MarkLabel (ec.LoopBegin);
5344 ig.Emit (OpCodes.Ldloc, counter);
5345 ig.Emit (OpCodes.Ldc_I4_1);
5346 ig.Emit (OpCodes.Add);
5347 ig.Emit (OpCodes.Stloc, counter);
5349 ig.MarkLabel (test);
5350 ig.Emit (OpCodes.Ldloc, counter);
5351 ig.Emit (OpCodes.Ldloc, copy);
5352 ig.Emit (OpCodes.Ldlen);
5353 ig.Emit (OpCodes.Conv_I4);
5354 ig.Emit (OpCodes.Blt, loop);
5356 LocalBuilder [] dim_len = new LocalBuilder [rank];
5357 LocalBuilder [] dim_count = new LocalBuilder [rank];
5358 Label [] loop = new Label [rank];
5359 Label [] test = new Label [rank];
5362 for (dim = 0; dim < rank; dim++){
5363 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
5364 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
5365 test [dim] = ig.DefineLabel ();
5366 loop [dim] = ig.DefineLabel ();
5369 for (dim = 0; dim < rank; dim++){
5370 ig.Emit (OpCodes.Ldloc, copy);
5371 IntLiteral.EmitInt (ig, dim);
5372 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
5373 ig.Emit (OpCodes.Stloc, dim_len [dim]);
5376 for (dim = 0; dim < rank; dim++){
5377 ig.Emit (OpCodes.Ldc_I4_0);
5378 ig.Emit (OpCodes.Stloc, dim_count [dim]);
5379 ig.Emit (OpCodes.Br, test [dim]);
5380 ig.MarkLabel (loop [dim]);
5383 ig.Emit (OpCodes.Ldloc, copy);
5384 for (dim = 0; dim < rank; dim++)
5385 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5388 // FIXME: Maybe we can cache the computation of `get'?
5390 Type [] args = new Type [rank];
5393 for (int i = 0; i < rank; i++)
5394 args [i] = TypeManager.int32_type;
5396 ModuleBuilder mb = CodeGen.ModuleBuilder;
5397 get = mb.GetArrayMethod (
5399 CallingConventions.HasThis| CallingConventions.Standard,
5401 ig.Emit (OpCodes.Call, get);
5402 variable.EmitAssign (ec, conv);
5403 statement.Emit (ec);
5404 ig.MarkLabel (ec.LoopBegin);
5405 for (dim = rank - 1; dim >= 0; dim--){
5406 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5407 ig.Emit (OpCodes.Ldc_I4_1);
5408 ig.Emit (OpCodes.Add);
5409 ig.Emit (OpCodes.Stloc, dim_count [dim]);
5411 ig.MarkLabel (test [dim]);
5412 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5413 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
5414 ig.Emit (OpCodes.Blt, loop [dim]);
5417 ig.MarkLabel (ec.LoopEnd);
5422 protected override bool DoEmit (EmitContext ec)
5426 ILGenerator ig = ec.ig;
5428 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5429 bool old_inloop = ec.InLoop;
5430 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
5431 ec.LoopBegin = ig.DefineLabel ();
5432 ec.LoopEnd = ig.DefineLabel ();
5434 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
5437 ret_val = EmitCollectionForeach (ec);
5439 ret_val = EmitArrayForeach (ec);
5441 ec.LoopBegin = old_begin;
5442 ec.LoopEnd = old_end;
5443 ec.InLoop = old_inloop;
5444 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;