2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001, 2002 Ximian, Inc.
12 using System.Reflection;
13 using System.Reflection.Emit;
14 using System.Diagnostics;
16 namespace Mono.CSharp {
18 using System.Collections;
20 public abstract class Statement {
24 /// Resolves the statement, true means that all sub-statements
27 public virtual bool Resolve (EmitContext ec)
33 /// Return value indicates whether all code paths emitted return.
35 public abstract bool Emit (EmitContext ec);
37 public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
43 if (e.Type != TypeManager.bool_type){
44 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
50 31, loc, "Can not convert the expression to a boolean");
53 if (CodeGen.SymbolWriter != null)
60 /// Encapsulates the emission of a boolean test and jumping to a
63 /// This will emit the bool expression in `bool_expr' and if
64 /// `target_is_for_true' is true, then the code will generate a
65 /// brtrue to the target. Otherwise a brfalse.
67 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
68 Label target, bool target_is_for_true)
70 ILGenerator ig = ec.ig;
73 if (bool_expr is Unary){
74 Unary u = (Unary) bool_expr;
76 if (u.Oper == Unary.Operator.LogicalNot){
79 u.EmitLogicalNot (ec);
81 } else if (bool_expr is Binary){
82 Binary b = (Binary) bool_expr;
84 if (b.EmitBranchable (ec, target, target_is_for_true))
91 if (target_is_for_true){
93 ig.Emit (OpCodes.Brfalse, target);
95 ig.Emit (OpCodes.Brtrue, target);
98 ig.Emit (OpCodes.Brtrue, target);
100 ig.Emit (OpCodes.Brfalse, target);
104 public static void Warning_DeadCodeFound (Location loc)
106 Report.Warning (162, loc, "Unreachable code detected");
110 public class EmptyStatement : Statement {
111 public override bool Resolve (EmitContext ec)
116 public override bool Emit (EmitContext ec)
122 public class If : Statement {
124 public Statement TrueStatement;
125 public Statement FalseStatement;
127 public If (Expression expr, Statement trueStatement, Location l)
130 TrueStatement = trueStatement;
134 public If (Expression expr,
135 Statement trueStatement,
136 Statement falseStatement,
140 TrueStatement = trueStatement;
141 FalseStatement = falseStatement;
145 public override bool Resolve (EmitContext ec)
147 Report.Debug (1, "START IF BLOCK");
149 expr = ResolveBoolean (ec, expr, loc);
154 ec.StartFlowBranching (FlowBranchingType.BLOCK, loc);
156 if (!TrueStatement.Resolve (ec)) {
157 ec.KillFlowBranching ();
161 ec.CurrentBranching.CreateSibling ();
163 if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
164 ec.KillFlowBranching ();
168 ec.EndFlowBranching ();
170 Report.Debug (1, "END IF BLOCK");
175 public override bool Emit (EmitContext ec)
177 ILGenerator ig = ec.ig;
178 Label false_target = ig.DefineLabel ();
180 bool is_true_ret, is_false_ret;
183 // Dead code elimination
185 if (expr is BoolConstant){
186 bool take = ((BoolConstant) expr).Value;
189 if (FalseStatement != null){
190 Warning_DeadCodeFound (FalseStatement.loc);
192 return TrueStatement.Emit (ec);
194 Warning_DeadCodeFound (TrueStatement.loc);
195 if (FalseStatement != null)
196 return FalseStatement.Emit (ec);
200 EmitBoolExpression (ec, expr, false_target, false);
202 is_true_ret = TrueStatement.Emit (ec);
203 is_false_ret = is_true_ret;
205 if (FalseStatement != null){
206 bool branch_emitted = false;
208 end = ig.DefineLabel ();
210 ig.Emit (OpCodes.Br, end);
211 branch_emitted = true;
214 ig.MarkLabel (false_target);
215 is_false_ret = FalseStatement.Emit (ec);
220 ig.MarkLabel (false_target);
221 is_false_ret = false;
224 return is_true_ret && is_false_ret;
228 public class Do : Statement {
229 public Expression expr;
230 public readonly Statement EmbeddedStatement;
232 public Do (Statement statement, Expression boolExpr, Location l)
235 EmbeddedStatement = statement;
239 public override bool Resolve (EmitContext ec)
241 if (!EmbeddedStatement.Resolve (ec))
244 expr = ResolveBoolean (ec, expr, loc);
251 public override bool Emit (EmitContext ec)
253 ILGenerator ig = ec.ig;
254 Label loop = ig.DefineLabel ();
255 Label old_begin = ec.LoopBegin;
256 Label old_end = ec.LoopEnd;
257 bool old_inloop = ec.InLoop;
258 bool old_breaks = ec.Breaks;
259 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
261 ec.LoopBegin = ig.DefineLabel ();
262 ec.LoopEnd = ig.DefineLabel ();
264 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
268 EmbeddedStatement.Emit (ec);
269 bool breaks = ec.Breaks;
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;
289 ec.Breaks = old_breaks;
292 // Inform whether we are infinite or not
294 if (expr is BoolConstant){
295 BoolConstant bc = (BoolConstant) expr;
297 if (bc.Value == true)
298 return breaks == false;
305 public class While : Statement {
306 public Expression expr;
307 public readonly Statement Statement;
309 public While (Expression boolExpr, Statement statement, Location l)
311 this.expr = boolExpr;
312 Statement = statement;
316 public override bool Resolve (EmitContext ec)
318 expr = ResolveBoolean (ec, expr, loc);
322 return Statement.Resolve (ec);
325 public override bool Emit (EmitContext ec)
327 ILGenerator ig = ec.ig;
328 Label old_begin = ec.LoopBegin;
329 Label old_end = ec.LoopEnd;
330 bool old_inloop = ec.InLoop;
331 bool old_breaks = ec.Breaks;
332 Label while_loop = ig.DefineLabel ();
333 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
336 ec.LoopBegin = ig.DefineLabel ();
337 ec.LoopEnd = ig.DefineLabel ();
339 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
341 ig.Emit (OpCodes.Br, ec.LoopBegin);
342 ig.MarkLabel (while_loop);
345 // Inform whether we are infinite or not
347 if (expr is BoolConstant){
348 BoolConstant bc = (BoolConstant) expr;
350 ig.MarkLabel (ec.LoopBegin);
351 if (bc.Value == false){
352 Warning_DeadCodeFound (Statement.loc);
360 ig.Emit (OpCodes.Br, ec.LoopBegin);
363 // Inform that we are infinite (ie, `we return'), only
364 // if we do not `break' inside the code.
366 ret = breaks == false;
368 ig.MarkLabel (ec.LoopEnd);
372 ig.MarkLabel (ec.LoopBegin);
374 EmitBoolExpression (ec, expr, while_loop, true);
375 ig.MarkLabel (ec.LoopEnd);
380 ec.LoopBegin = old_begin;
381 ec.LoopEnd = old_end;
382 ec.InLoop = old_inloop;
383 ec.Breaks = old_breaks;
384 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
390 public class For : Statement {
392 readonly Statement InitStatement;
393 readonly Statement Increment;
394 readonly Statement Statement;
396 public For (Statement initStatement,
402 InitStatement = initStatement;
404 Increment = increment;
405 Statement = statement;
409 public override bool Resolve (EmitContext ec)
413 if (InitStatement != null){
414 if (!InitStatement.Resolve (ec))
419 Test = ResolveBoolean (ec, Test, loc);
424 if (Increment != null){
425 if (!Increment.Resolve (ec))
429 return Statement.Resolve (ec) && ok;
432 public override bool Emit (EmitContext ec)
434 ILGenerator ig = ec.ig;
435 Label old_begin = ec.LoopBegin;
436 Label old_end = ec.LoopEnd;
437 bool old_inloop = ec.InLoop;
438 bool old_breaks = ec.Breaks;
439 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
440 Label loop = ig.DefineLabel ();
441 Label test = ig.DefineLabel ();
443 if (InitStatement != null)
444 if (! (InitStatement is EmptyStatement))
445 InitStatement.Emit (ec);
447 ec.LoopBegin = ig.DefineLabel ();
448 ec.LoopEnd = ig.DefineLabel ();
450 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
452 ig.Emit (OpCodes.Br, test);
456 bool breaks = ec.Breaks;
458 ig.MarkLabel (ec.LoopBegin);
459 if (!(Increment is EmptyStatement))
464 // If test is null, there is no test, and we are just
468 EmitBoolExpression (ec, Test, loop, true);
470 ig.Emit (OpCodes.Br, loop);
471 ig.MarkLabel (ec.LoopEnd);
473 ec.LoopBegin = old_begin;
474 ec.LoopEnd = old_end;
475 ec.InLoop = old_inloop;
476 ec.Breaks = old_breaks;
477 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
480 // Inform whether we are infinite or not
483 if (Test is BoolConstant){
484 BoolConstant bc = (BoolConstant) Test;
487 return breaks == false;
495 public class StatementExpression : Statement {
498 public StatementExpression (ExpressionStatement expr, Location l)
504 public override bool Resolve (EmitContext ec)
506 expr = (Expression) expr.Resolve (ec);
510 public override bool Emit (EmitContext ec)
512 ILGenerator ig = ec.ig;
514 if (expr is ExpressionStatement)
515 ((ExpressionStatement) expr).EmitStatement (ec);
518 ig.Emit (OpCodes.Pop);
524 public override string ToString ()
526 return "StatementExpression (" + expr + ")";
531 /// Implements the return statement
533 public class Return : Statement {
534 public Expression Expr;
536 public Return (Expression expr, Location l)
542 public override bool Resolve (EmitContext ec)
545 Expr = Expr.Resolve (ec);
550 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
552 if (ec.CurrentBranching.InTryBlock ())
553 ec.CurrentBranching.AddFinallyVector (vector);
555 vector.Returns = FlowReturns.ALWAYS;
556 vector.Breaks = FlowReturns.ALWAYS;
561 public override bool Emit (EmitContext ec)
564 Report.Error (157,loc,"Control can not leave the body of the finally block");
568 if (ec.ReturnType == null){
570 Report.Error (127, loc, "Return with a value not allowed here");
575 Report.Error (126, loc, "An object of type `" +
576 TypeManager.CSharpName (ec.ReturnType) + "' is " +
577 "expected for the return statement");
581 if (Expr.Type != ec.ReturnType)
582 Expr = Expression.ConvertImplicitRequired (
583 ec, Expr, ec.ReturnType, loc);
590 if (ec.InTry || ec.InCatch)
591 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
594 if (ec.InTry || ec.InCatch) {
595 if (!ec.HasReturnLabel) {
596 ec.ReturnLabel = ec.ig.DefineLabel ();
597 ec.HasReturnLabel = true;
599 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
601 ec.ig.Emit (OpCodes.Ret);
607 public class Goto : Statement {
610 LabeledStatement label;
612 public override bool Resolve (EmitContext ec)
614 label = block.LookupLabel (target);
618 "No such label `" + target + "' in this scope");
622 // If this is a forward goto.
623 if (!label.IsDefined)
624 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
626 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
631 public Goto (Block parent_block, string label, Location l)
633 block = parent_block;
638 public string Target {
644 public override bool Emit (EmitContext ec)
646 Label l = label.LabelTarget (ec);
647 ec.ig.Emit (OpCodes.Br, l);
653 public class LabeledStatement : Statement {
660 public LabeledStatement (string label_name)
662 this.label_name = label_name;
665 public Label LabelTarget (EmitContext ec)
669 label = ec.ig.DefineLabel ();
675 public bool IsDefined {
681 public void AddUsageVector (FlowBranching.UsageVector vector)
684 vectors = new ArrayList ();
686 vectors.Add (vector.Clone ());
689 public override bool Resolve (EmitContext ec)
692 ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors);
697 public override bool Emit (EmitContext ec)
700 ec.ig.MarkLabel (label);
708 /// `goto default' statement
710 public class GotoDefault : Statement {
712 public GotoDefault (Location l)
717 public override bool Resolve (EmitContext ec)
719 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.UNREACHABLE;
720 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
724 public override bool Emit (EmitContext ec)
726 if (ec.Switch == null){
727 Report.Error (153, loc, "goto default is only valid in a switch statement");
731 if (!ec.Switch.GotDefault){
732 Report.Error (159, loc, "No default target on switch statement");
735 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
741 /// `goto case' statement
743 public class GotoCase : Statement {
747 public GotoCase (Expression e, Location l)
753 public override bool Resolve (EmitContext ec)
755 if (ec.Switch == null){
756 Report.Error (153, loc, "goto case is only valid in a switch statement");
760 expr = expr.Resolve (ec);
764 if (!(expr is Constant)){
765 Report.Error (159, loc, "Target expression for goto case is not constant");
769 object val = Expression.ConvertIntLiteral (
770 (Constant) expr, ec.Switch.SwitchType, loc);
775 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
780 "No such label 'case " + val + "': for the goto case");
783 label = sl.ILLabelCode;
785 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.UNREACHABLE;
786 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
790 public override bool Emit (EmitContext ec)
792 ec.ig.Emit (OpCodes.Br, label);
797 public class Throw : Statement {
800 public Throw (Expression expr, Location l)
806 public override bool Resolve (EmitContext ec)
809 expr = expr.Resolve (ec);
814 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION;
815 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION;
819 public override bool Emit (EmitContext ec)
823 ec.ig.Emit (OpCodes.Rethrow);
827 "A throw statement with no argument is only " +
828 "allowed in a catch clause");
835 ec.ig.Emit (OpCodes.Throw);
841 public class Break : Statement {
843 public Break (Location l)
848 public override bool Resolve (EmitContext ec)
850 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
854 public override bool Emit (EmitContext ec)
856 ILGenerator ig = ec.ig;
858 if (ec.InLoop == false && ec.Switch == null){
859 Report.Error (139, loc, "No enclosing loop or switch to continue to");
864 if (ec.InTry || ec.InCatch)
865 ig.Emit (OpCodes.Leave, ec.LoopEnd);
867 ig.Emit (OpCodes.Br, ec.LoopEnd);
873 public class Continue : Statement {
875 public Continue (Location l)
880 public override bool Resolve (EmitContext ec)
882 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
886 public override bool Emit (EmitContext ec)
888 Label begin = ec.LoopBegin;
891 Report.Error (139, loc, "No enclosing loop to continue to");
896 // UGH: Non trivial. This Br might cross a try/catch boundary
900 // try { ... } catch { continue; }
904 // try {} catch { while () { continue; }}
906 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
907 ec.ig.Emit (OpCodes.Leave, begin);
908 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
909 throw new Exception ("Should never happen");
911 ec.ig.Emit (OpCodes.Br, begin);
917 // This is used in the control flow analysis code to specify whether the
918 // current code block may return to its enclosing block before reaching
921 public enum FlowReturns {
922 // It can never return.
925 // This means that the block contains a conditional return statement
929 // The code always returns, ie. there's an unconditional return / break
933 // The code always throws an exception.
936 // The current code block is unreachable. This happens if it's immediately
937 // following a FlowReturns.ALWAYS block.
942 // This is a special bit vector which can inherit from another bit vector doing a
943 // copy-on-write strategy. The inherited vector may have a smaller size than the
946 public class MyBitVector {
947 public readonly int Count;
948 public readonly MyBitVector InheritsFrom;
953 public MyBitVector (int Count)
957 public MyBitVector (MyBitVector InheritsFrom, int Count)
959 this.InheritsFrom = InheritsFrom;
964 // Checks whether this bit vector has been modified. After setting this to true,
965 // we won't use the inherited vector anymore, but our own copy of it.
967 public bool IsDirty {
974 initialize_vector ();
979 // Get/set bit `index' in the bit vector.
981 public bool this [int index]
985 throw new ArgumentOutOfRangeException ();
987 // We're doing a "copy-on-write" strategy here; as long
988 // as nobody writes to the array, we can use our parent's
989 // copy instead of duplicating the vector.
992 return vector [index];
993 else if (InheritsFrom != null) {
994 BitArray inherited = InheritsFrom.Vector;
996 if (index < inherited.Count)
997 return inherited [index];
1006 throw new ArgumentOutOfRangeException ();
1008 // Only copy the vector if we're actually modifying it.
1010 if (this [index] != value) {
1011 initialize_vector ();
1013 vector [index] = value;
1019 // If you explicitly convert the MyBitVector to a BitArray, you will get a deep
1020 // copy of the bit vector.
1022 public static explicit operator BitArray (MyBitVector vector)
1024 vector.initialize_vector ();
1025 return vector.Vector;
1029 // Performs an `or' operation on the bit vector. The `new_vector' may have a
1030 // different size than the current one.
1032 public void Or (MyBitVector new_vector)
1034 BitArray new_array = new_vector.Vector;
1036 initialize_vector ();
1039 if (vector.Count < new_array.Count)
1040 upper = vector.Count;
1042 upper = new_array.Count;
1044 for (int i = 0; i < upper; i++)
1045 vector [i] = vector [i] | new_array [i];
1049 // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
1050 // a different size than the current one.
1052 public void And (MyBitVector new_vector)
1054 BitArray new_array = new_vector.Vector;
1056 initialize_vector ();
1059 if (vector.Count < new_array.Count)
1060 lower = upper = vector.Count;
1062 lower = new_array.Count;
1063 upper = vector.Count;
1066 for (int i = 0; i < lower; i++)
1067 vector [i] = vector [i] & new_array [i];
1069 for (int i = lower; i < upper; i++)
1074 // This does a deep copy of the bit vector.
1076 public MyBitVector Clone ()
1078 MyBitVector retval = new MyBitVector (Count);
1080 retval.Vector = Vector;
1089 else if (!is_dirty && (InheritsFrom != null))
1090 return InheritsFrom.Vector;
1092 initialize_vector ();
1098 initialize_vector ();
1100 for (int i = 0; i < Math.Min (vector.Count, value.Count); i++)
1101 vector [i] = value [i];
1105 void initialize_vector ()
1110 vector = new BitArray (Count, false);
1111 if (InheritsFrom != null)
1112 Vector = InheritsFrom.Vector;
1117 public override string ToString ()
1119 StringBuilder sb = new StringBuilder ("MyBitVector (");
1121 BitArray vector = Vector;
1125 sb.Append ("INHERITED - ");
1126 for (int i = 0; i < vector.Count; i++) {
1129 sb.Append (vector [i]);
1133 return sb.ToString ();
1138 // The type of a FlowBranching.
1140 public enum FlowBranchingType {
1141 // Normal (conditional or toplevel) block.
1155 // A new instance of this class is created every time a new block is resolved
1156 // and if there's branching in the block's control flow.
1158 public class FlowBranching {
1160 // The type of this flow branching.
1162 public readonly FlowBranchingType Type;
1165 // The block this branching is contained in. This may be null if it's not
1166 // a top-level block and it doesn't declare any local variables.
1168 public readonly Block Block;
1171 // The parent of this branching or null if this is the top-block.
1173 public readonly FlowBranching Parent;
1176 // Start-Location of this flow branching.
1178 public readonly Location Location;
1181 // A list of UsageVectors. A new vector is added each time control flow may
1182 // take a different path.
1184 public ArrayList Siblings;
1189 InternalParameters param_info;
1192 ArrayList finally_vectors;
1194 static int next_id = 0;
1198 // Performs an `And' operation on the FlowReturns status
1199 // (for instance, a block only returns ALWAYS if all its siblings
1202 public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b)
1204 if (b == FlowReturns.UNREACHABLE)
1208 case FlowReturns.NEVER:
1209 if (b == FlowReturns.NEVER)
1210 return FlowReturns.NEVER;
1212 return FlowReturns.SOMETIMES;
1215 case FlowReturns.SOMETIMES:
1216 return FlowReturns.SOMETIMES;
1218 case FlowReturns.ALWAYS:
1219 if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION))
1220 return FlowReturns.ALWAYS;
1222 return FlowReturns.SOMETIMES;
1225 case FlowReturns.EXCEPTION:
1226 if (b == FlowReturns.EXCEPTION)
1227 return FlowReturns.EXCEPTION;
1228 else if (b == FlowReturns.ALWAYS)
1229 return FlowReturns.ALWAYS;
1231 return FlowReturns.SOMETIMES;
1239 // The vector contains a BitArray with information about which local variables
1240 // and parameters are already initialized at the current code position.
1242 public class UsageVector {
1244 // If this is true, then the usage vector has been modified and must be
1245 // merged when we're done with this branching.
1247 public bool IsDirty;
1250 // The number of parameters in this block.
1252 public readonly int CountParameters;
1255 // The number of locals in this block.
1257 public readonly int CountLocals;
1260 // If not null, then we inherit our state from this vector and do a
1261 // copy-on-write. If null, then we're the first sibling in a top-level
1262 // block and inherit from the empty vector.
1264 public readonly UsageVector InheritsFrom;
1269 MyBitVector locals, parameters;
1270 FlowReturns real_returns, real_breaks;
1271 bool returns_set, breaks_set, is_finally;
1273 static int next_id = 0;
1277 // Normally, you should not use any of these constructors.
1279 public UsageVector (UsageVector parent, int num_params, int num_locals)
1281 this.InheritsFrom = parent;
1282 this.CountParameters = num_params;
1283 this.CountLocals = num_locals;
1284 this.real_returns = FlowReturns.NEVER;
1285 this.real_breaks = FlowReturns.NEVER;
1287 if (parent != null) {
1288 locals = new MyBitVector (parent.locals, CountLocals);
1289 parameters = new MyBitVector (parent.parameters, num_params);
1291 locals = new MyBitVector (null, CountLocals);
1292 parameters = new MyBitVector (null, num_params);
1298 public UsageVector (UsageVector parent)
1299 : this (parent, parent.CountParameters, parent.CountLocals)
1303 // This does a deep copy of the usage vector.
1305 public UsageVector Clone ()
1307 UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
1309 retval.locals = locals.Clone ();
1310 if (parameters != null)
1311 retval.parameters = parameters.Clone ();
1312 retval.real_returns = real_returns;
1313 retval.real_breaks = real_breaks;
1319 // State of parameter `number'.
1321 public bool this [int number]
1326 else if (number == 0)
1327 throw new ArgumentException ();
1329 return parameters [number - 1];
1335 else if (number == 0)
1336 throw new ArgumentException ();
1338 parameters [number - 1] = value;
1343 // State of the local variable `vi'.
1345 public bool this [VariableInfo vi]
1348 if (vi.Number == -1)
1350 else if (vi.Number == 0)
1351 throw new ArgumentException ();
1353 return locals [vi.Number - 1];
1357 if (vi.Number == -1)
1359 else if (vi.Number == 0)
1360 throw new ArgumentException ();
1362 locals [vi.Number - 1] = value;
1367 // Specifies when the current block returns.
1369 public FlowReturns Returns {
1371 return real_returns;
1375 real_returns = value;
1381 // Specifies whether control may return to our containing block
1382 // before reaching the end of this block. This happens if there
1383 // is a break/continue/goto/return in it.
1385 public FlowReturns Breaks {
1391 real_breaks = value;
1397 // Merge a child branching.
1399 public FlowReturns MergeChildren (FlowBranching branching, ICollection children)
1401 MyBitVector new_locals = null;
1402 MyBitVector new_params = null;
1404 FlowReturns new_returns = FlowReturns.NEVER;
1405 FlowReturns new_breaks = FlowReturns.NEVER;
1406 bool new_returns_set = false, new_breaks_set = false;
1409 Report.Debug (1, "MERGING CHILDREN", branching, this);
1411 foreach (UsageVector child in children) {
1412 Report.Debug (1, " MERGING CHILD", child);
1414 // If Returns is already set, perform an `And' operation on it,
1415 // otherwise just set just.
1416 if (!new_returns_set) {
1417 new_returns = child.Returns;
1418 new_returns_set = true;
1420 new_returns = AndFlowReturns (new_returns, child.Returns);
1422 // If Breaks is already set, perform an `And' operation on it,
1423 // otherwise just set just.
1424 if (!new_breaks_set) {
1425 new_breaks = child.Breaks;
1426 new_breaks_set = true;
1428 new_breaks = AndFlowReturns (new_breaks, child.Breaks);
1430 // Ignore unreachable children.
1431 if (child.Returns == FlowReturns.UNREACHABLE)
1434 // If we're a switch section, `break' won't leave the current
1435 // branching (NOTE: the type check here means that we're "a"
1436 // switch section, not that we're "in" a switch section!).
1437 breaks = (branching.Type == FlowBranchingType.SWITCH_SECTION) ?
1438 child.Returns : child.Breaks;
1440 // A local variable is initialized after a flow branching if it
1441 // has been initialized in all its branches which do neither
1442 // always return or always throw an exception.
1444 // If a branch may return, but does not always return, then we
1445 // can treat it like a never-returning branch here: control will
1446 // only reach the code position after the branching if we did not
1449 // It's important to distinguish between always and sometimes
1450 // returning branches here:
1453 // 2 if (something) {
1457 // 6 Console.WriteLine (a);
1459 // The if block in lines 3-4 always returns, so we must not look
1460 // at the initialization of `a' in line 4 - thus it'll still be
1461 // uninitialized in line 6.
1463 // On the other hand, the following is allowed:
1470 // 6 Console.WriteLine (a);
1472 // Here, `a' is initialized in line 3 and we must not look at
1473 // line 5 since it always returns.
1475 if ((breaks != FlowReturns.EXCEPTION) &&
1476 (breaks != FlowReturns.ALWAYS)) {
1477 if (new_locals != null)
1478 new_locals.And (child.locals);
1480 new_locals = locals.Clone ();
1481 new_locals.Or (child.locals);
1485 // An `out' parameter must be assigned in all branches which do
1486 // not always throw an exception.
1487 if (!child.is_finally && (child.Returns != FlowReturns.EXCEPTION)) {
1488 if (new_params != null)
1489 new_params.And (child.parameters);
1491 new_params = parameters.Clone ();
1492 new_params.Or (child.parameters);
1496 // If we always return, check whether all `out' parameters have
1498 if (child.Returns == FlowReturns.ALWAYS) {
1499 branching.CheckOutParameters (
1500 child.parameters, branching.Location);
1504 // Set new `Returns' status.
1506 Returns = new_returns;
1509 Returns = AndFlowReturns (Returns, new_returns);
1512 // We've now either reached the point after the branching or we will
1513 // never get there since we always return or always throw an exception.
1515 // If we can reach the point after the branching, mark all locals and
1516 // parameters as initialized which have been initialized in all branches
1517 // we need to look at (see above).
1520 breaks = (branching.Type == FlowBranchingType.SWITCH_SECTION) ?
1523 if ((new_locals != null) &&
1524 ((breaks == FlowReturns.NEVER) || (breaks == FlowReturns.SOMETIMES))) {
1525 locals.Or (new_locals);
1528 if ((new_params != null) && (Breaks == FlowReturns.NEVER))
1529 parameters.Or (new_params);
1532 // If we may have returned (this only happens if there was a reachable
1533 // `return' statement in one of the branches), then we may return to our
1534 // parent block before reaching the end of the block, so set `Breaks'.
1537 if ((Returns != FlowReturns.NEVER) && (Returns != FlowReturns.SOMETIMES)) {
1538 real_breaks = Returns;
1542 Report.Debug (1, "MERGING CHILDREN DONE", new_params, new_locals,
1543 new_returns, new_breaks, this);
1549 // Tells control flow analysis that the current code position may be reached with
1550 // a forward jump from any of the origins listed in `origin_vectors' which is a
1551 // list of UsageVectors.
1553 // This is used when resolving forward gotos - in the following example, the
1554 // variable `a' is uninitialized in line 8 becase this line may be reached via
1555 // the goto in line 4:
1565 // 8 Console.WriteLine (a);
1568 public void MergeJumpOrigins (ICollection origin_vectors)
1570 Report.Debug (1, "MERGING JUMP ORIGIN", this);
1572 real_breaks = FlowReturns.NEVER;
1575 foreach (UsageVector vector in origin_vectors) {
1576 Report.Debug (1, " MERGING JUMP ORIGIN", vector);
1578 locals.And (vector.locals);
1579 parameters.And (vector.parameters);
1580 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1583 Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
1587 // This is used at the beginning of a finally block if there were
1588 // any return statements in the try block or one of the catch blocks.
1590 public void MergeFinallyOrigins (ICollection finally_vectors)
1592 Report.Debug (1, "MERGING FINALLY ORIGIN", this);
1594 real_breaks = FlowReturns.NEVER;
1597 foreach (UsageVector vector in finally_vectors) {
1598 Report.Debug (1, " MERGING FINALLY ORIGIN", vector);
1600 parameters.And (vector.parameters);
1601 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1606 Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
1610 // Performs an `or' operation on the locals and the parameters.
1612 public void Or (UsageVector new_vector)
1614 locals.Or (new_vector.locals);
1615 parameters.Or (new_vector.parameters);
1619 // Performs an `and' operation on the locals.
1621 public void AndLocals (UsageVector new_vector)
1623 locals.And (new_vector.locals);
1627 // Returns a deep copy of the parameters.
1629 public MyBitVector Parameters {
1631 return parameters.Clone ();
1636 // Returns a deep copy of the locals.
1638 public MyBitVector Locals {
1640 return locals.Clone ();
1648 public override string ToString ()
1650 StringBuilder sb = new StringBuilder ();
1652 sb.Append ("Vector (");
1655 sb.Append (Returns);
1659 sb.Append (parameters);
1664 return sb.ToString ();
1668 FlowBranching (FlowBranchingType type, Location loc)
1670 this.Siblings = new ArrayList ();
1672 this.Location = loc;
1678 // Creates a new flow branching for `block'.
1679 // This is used from Block.Resolve to create the top-level branching of
1682 public FlowBranching (Block block, InternalParameters ip, Location loc)
1683 : this (FlowBranchingType.BLOCK, loc)
1689 param_map = new int [(param_info != null) ? param_info.Count : 0];
1692 for (int i = 0; i < param_map.Length; i++) {
1693 Parameter.Modifier mod = param_info.ParameterModifier (i);
1695 if ((mod & Parameter.Modifier.OUT) == 0)
1698 param_map [i] = ++num_params;
1701 Siblings = new ArrayList ();
1702 Siblings.Add (new UsageVector (null, num_params, block.CountVariables));
1706 // Creates a new flow branching which is contained in `parent'.
1707 // You should only pass non-null for the `block' argument if this block
1708 // introduces any new variables - in this case, we need to create a new
1709 // usage vector with a different size than our parent's one.
1711 public FlowBranching (FlowBranching parent, FlowBranchingType type,
1712 Block block, Location loc)
1718 if (parent != null) {
1719 param_info = parent.param_info;
1720 param_map = parent.param_map;
1721 num_params = parent.num_params;
1726 vector = new UsageVector (parent.CurrentUsageVector, num_params,
1727 Block.CountVariables);
1729 vector = new UsageVector (Parent.CurrentUsageVector);
1731 Siblings.Add (vector);
1734 case FlowBranchingType.EXCEPTION:
1735 finally_vectors = new ArrayList ();
1744 // Returns the branching's current usage vector.
1746 public UsageVector CurrentUsageVector
1749 return (UsageVector) Siblings [Siblings.Count - 1];
1754 // Creates a sibling of the current usage vector.
1756 public void CreateSibling ()
1758 Siblings.Add (new UsageVector (Parent.CurrentUsageVector));
1760 Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
1764 // Creates a sibling for a `finally' block.
1766 public void CreateSiblingForFinally ()
1768 if (Type != FlowBranchingType.EXCEPTION)
1769 throw new NotSupportedException ();
1773 CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
1777 // Check whether all `out' parameters have been assigned.
1779 public void CheckOutParameters (MyBitVector parameters, Location loc)
1784 for (int i = 0; i < param_map.Length; i++) {
1785 if (param_map [i] == 0)
1788 if (!parameters [param_map [i] - 1]) {
1790 177, loc, "The out parameter `" +
1791 param_info.ParameterName (i) + "` must be " +
1792 "assigned before control leave the current method.");
1799 // Merge a child branching.
1801 public FlowReturns MergeChild (FlowBranching child)
1803 return CurrentUsageVector.MergeChildren (child, child.Siblings);
1807 // Does the toplevel merging.
1809 public FlowReturns MergeTopBlock ()
1811 if ((Type != FlowBranchingType.BLOCK) || (Block == null))
1812 throw new NotSupportedException ();
1814 UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
1816 vector.MergeChildren (this, Siblings);
1819 Siblings.Add (vector);
1821 Report.Debug (1, "MERGING TOP BLOCK", vector);
1823 if (vector.Returns != FlowReturns.EXCEPTION)
1824 CheckOutParameters (CurrentUsageVector.Parameters, Location);
1826 return vector.Returns;
1829 public bool InTryBlock ()
1831 if (finally_vectors != null)
1833 else if (Parent != null)
1834 return Parent.InTryBlock ();
1839 public void AddFinallyVector (UsageVector vector)
1841 if (finally_vectors != null) {
1842 finally_vectors.Add (vector.Clone ());
1847 Parent.AddFinallyVector (vector);
1849 throw new NotSupportedException ();
1852 public bool IsVariableAssigned (VariableInfo vi)
1854 Report.Debug (2, "CHECK VARIABLE ACCESS", this, vi);
1856 if (CurrentUsageVector.Breaks == FlowReturns.UNREACHABLE)
1859 return CurrentUsageVector [vi];
1862 public void SetVariableAssigned (VariableInfo vi)
1864 Report.Debug (2, "SET VARIABLE ACCESS", this, vi, CurrentUsageVector);
1866 if (CurrentUsageVector.Breaks == FlowReturns.UNREACHABLE)
1869 CurrentUsageVector [vi] = true;
1872 public bool IsParameterAssigned (int number)
1874 Report.Debug (2, "IS PARAMETER ASSIGNED", this, number);
1876 if (param_map [number] == 0)
1879 return CurrentUsageVector [param_map [number]];
1882 public void SetParameterAssigned (int number)
1884 Report.Debug (2, "SET PARAMETER ACCESS", this, number, param_map [number],
1885 CurrentUsageVector);
1887 if (param_map [number] == 0)
1890 if (CurrentUsageVector.Breaks == FlowReturns.NEVER)
1891 CurrentUsageVector [param_map [number]] = true;
1894 public override string ToString ()
1896 StringBuilder sb = new StringBuilder ("FlowBranching (");
1901 if (Block != null) {
1903 sb.Append (Block.ID);
1905 sb.Append (Block.StartLocation);
1908 sb.Append (Siblings.Count);
1910 sb.Append (CurrentUsageVector);
1912 return sb.ToString ();
1916 public class VariableInfo {
1917 public Expression Type;
1918 public LocalBuilder LocalBuilder;
1919 public Type VariableType;
1920 public readonly Location Location;
1925 public bool Assigned;
1926 public bool ReadOnly;
1928 public VariableInfo (Expression type, Location l)
1931 LocalBuilder = null;
1935 public void MakePinned ()
1937 TypeManager.MakePinned (LocalBuilder);
1940 public override string ToString ()
1942 return "VariableInfo (" + Number + "," + Type + "," + Location + ")";
1947 /// Block represents a C# block.
1951 /// This class is used in a number of places: either to represent
1952 /// explicit blocks that the programmer places or implicit blocks.
1954 /// Implicit blocks are used as labels or to introduce variable
1957 public class Block : Statement {
1958 public readonly Block Parent;
1959 public readonly bool Implicit;
1960 public readonly Location StartLocation;
1961 public Location EndLocation;
1964 // The statements in this block
1966 ArrayList statements;
1969 // An array of Blocks. We keep track of children just
1970 // to generate the local variable declarations.
1972 // Statements and child statements are handled through the
1978 // Labels. (label, block) pairs.
1983 // Keeps track of (name, type) pairs
1985 Hashtable variables;
1988 // Keeps track of constants
1989 Hashtable constants;
1992 // Maps variable names to ILGenerator.LocalBuilders
1994 Hashtable local_builders;
2002 public Block (Block parent)
2003 : this (parent, false, Location.Null, Location.Null)
2006 public Block (Block parent, bool implicit_block)
2007 : this (parent, implicit_block, Location.Null, Location.Null)
2010 public Block (Block parent, Location start, Location end)
2011 : this (parent, false, start, end)
2014 public Block (Block parent, bool implicit_block, Location start, Location end)
2017 parent.AddChild (this);
2019 this.Parent = parent;
2020 this.Implicit = implicit_block;
2021 this.StartLocation = start;
2022 this.EndLocation = end;
2025 statements = new ArrayList ();
2034 void AddChild (Block b)
2036 if (children == null)
2037 children = new ArrayList ();
2042 public void SetEndLocation (Location loc)
2048 /// Adds a label to the current block.
2052 /// false if the name already exists in this block. true
2056 public bool AddLabel (string name, LabeledStatement target)
2059 labels = new Hashtable ();
2060 if (labels.Contains (name))
2063 labels.Add (name, target);
2067 public LabeledStatement LookupLabel (string name)
2069 if (labels != null){
2070 if (labels.Contains (name))
2071 return ((LabeledStatement) labels [name]);
2075 return Parent.LookupLabel (name);
2080 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
2082 if (variables == null)
2083 variables = new Hashtable ();
2085 if (GetVariableType (name) != null)
2090 Parameter p = pars.GetParameterByName (name, out idx);
2095 VariableInfo vi = new VariableInfo (type, l);
2097 variables.Add (name, vi);
2099 if (variables_initialized)
2100 throw new Exception ();
2102 // Console.WriteLine ("Adding {0} to {1}", name, ID);
2106 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
2108 if (AddVariable (type, name, pars, l) == null)
2111 if (constants == null)
2112 constants = new Hashtable ();
2114 constants.Add (name, value);
2118 public Hashtable Variables {
2124 public VariableInfo GetVariableInfo (string name)
2126 if (variables != null) {
2128 temp = variables [name];
2131 return (VariableInfo) temp;
2136 return Parent.GetVariableInfo (name);
2141 public Expression GetVariableType (string name)
2143 VariableInfo vi = GetVariableInfo (name);
2151 public Expression GetConstantExpression (string name)
2153 if (constants != null) {
2155 temp = constants [name];
2158 return (Expression) temp;
2162 return Parent.GetConstantExpression (name);
2168 /// True if the variable named @name has been defined
2171 public bool IsVariableDefined (string name)
2173 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
2174 if (variables != null) {
2175 if (variables.Contains (name))
2180 return Parent.IsVariableDefined (name);
2186 /// True if the variable named @name is a constant
2188 public bool IsConstant (string name)
2190 Expression e = null;
2192 e = GetConstantExpression (name);
2198 /// Use to fetch the statement associated with this label
2200 public Statement this [string name] {
2202 return (Statement) labels [name];
2207 /// A list of labels that were not used within this block
2209 public string [] GetUnreferenced ()
2211 // FIXME: Implement me
2215 public void AddStatement (Statement s)
2232 bool variables_initialized = false;
2233 int count_variables = 0, first_variable = 0;
2235 void UpdateVariableInfo (EmitContext ec)
2237 DeclSpace ds = ec.DeclSpace;
2242 first_variable += Parent.CountVariables;
2244 count_variables = first_variable;
2245 if (variables != null) {
2246 foreach (VariableInfo vi in variables.Values) {
2247 Report.Debug (2, "VARIABLE", vi);
2249 Type type = ds.ResolveType (vi.Type, false, vi.Location);
2255 vi.VariableType = type;
2257 Report.Debug (2, "VARIABLE", vi, type, type.IsValueType,
2258 TypeManager.IsValueType (type),
2259 TypeManager.IsBuiltinType (type));
2261 // FIXME: we don't have support for structs yet.
2262 if (TypeManager.IsValueType (type) && !TypeManager.IsBuiltinType (type))
2265 vi.Number = ++count_variables;
2269 variables_initialized = true;
2274 // The number of local variables in this block
2276 public int CountVariables
2279 if (!variables_initialized)
2280 throw new Exception ();
2282 return count_variables;
2287 /// Emits the variable declarations and labels.
2290 /// tc: is our typecontainer (to resolve type references)
2291 /// ig: is the code generator:
2292 /// toplevel: the toplevel block. This is used for checking
2293 /// that no two labels with the same name are used.
2295 public void EmitMeta (EmitContext ec, Block toplevel)
2297 DeclSpace ds = ec.DeclSpace;
2298 ILGenerator ig = ec.ig;
2300 if (!variables_initialized)
2301 UpdateVariableInfo (ec);
2304 // Process this block variables
2306 if (variables != null){
2307 local_builders = new Hashtable ();
2309 foreach (DictionaryEntry de in variables){
2310 string name = (string) de.Key;
2311 VariableInfo vi = (VariableInfo) de.Value;
2313 if (vi.VariableType == null)
2316 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
2318 if (CodeGen.SymbolWriter != null)
2319 vi.LocalBuilder.SetLocalSymInfo (name);
2321 if (constants == null)
2324 Expression cv = (Expression) constants [name];
2328 Expression e = cv.Resolve (ec);
2332 if (!(e is Constant)){
2333 Report.Error (133, vi.Location,
2334 "The expression being assigned to `" +
2335 name + "' must be constant (" + e + ")");
2339 constants.Remove (name);
2340 constants.Add (name, e);
2345 // Now, handle the children
2347 if (children != null){
2348 foreach (Block b in children)
2349 b.EmitMeta (ec, toplevel);
2353 public void UsageWarning ()
2357 if (variables != null){
2358 foreach (DictionaryEntry de in variables){
2359 VariableInfo vi = (VariableInfo) de.Value;
2364 name = (string) de.Key;
2368 219, vi.Location, "The variable `" + name +
2369 "' is assigned but its value is never used");
2372 168, vi.Location, "The variable `" +
2374 "' is declared but never used");
2379 if (children != null)
2380 foreach (Block b in children)
2384 public override bool Resolve (EmitContext ec)
2386 Block prev_block = ec.CurrentBlock;
2389 ec.CurrentBlock = this;
2390 ec.StartFlowBranching (this);
2392 Report.Debug (1, "RESOLVE BLOCK", StartLocation);
2394 if (!variables_initialized)
2395 UpdateVariableInfo (ec);
2397 foreach (Statement s in statements){
2398 if (s.Resolve (ec) == false)
2402 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation);
2404 ec.EndFlowBranching ();
2405 ec.CurrentBlock = prev_block;
2409 public override bool Emit (EmitContext ec)
2411 bool is_ret = false, this_ret = false;
2412 Block prev_block = ec.CurrentBlock;
2413 bool warning_shown = false;
2415 ec.CurrentBlock = this;
2417 if (CodeGen.SymbolWriter != null) {
2418 ec.Mark (StartLocation);
2420 foreach (Statement s in statements) {
2423 if (is_ret && !warning_shown){
2424 warning_shown = true;
2425 Warning_DeadCodeFound (s.loc);
2427 this_ret = s.Emit (ec);
2432 ec.Mark (EndLocation);
2434 foreach (Statement s in statements){
2435 if (is_ret && !warning_shown){
2436 warning_shown = true;
2437 Warning_DeadCodeFound (s.loc);
2439 this_ret = s.Emit (ec);
2445 ec.CurrentBlock = prev_block;
2450 public class SwitchLabel {
2453 public Location loc;
2454 public Label ILLabel;
2455 public Label ILLabelCode;
2458 // if expr == null, then it is the default case.
2460 public SwitchLabel (Expression expr, Location l)
2466 public Expression Label {
2472 public object Converted {
2479 // Resolves the expression, reduces it to a literal if possible
2480 // and then converts it to the requested type.
2482 public bool ResolveAndReduce (EmitContext ec, Type required_type)
2484 ILLabel = ec.ig.DefineLabel ();
2485 ILLabelCode = ec.ig.DefineLabel ();
2490 Expression e = label.Resolve (ec);
2495 if (!(e is Constant)){
2496 Console.WriteLine ("Value is: " + label);
2497 Report.Error (150, loc, "A constant value is expected");
2501 if (e is StringConstant || e is NullLiteral){
2502 if (required_type == TypeManager.string_type){
2504 ILLabel = ec.ig.DefineLabel ();
2509 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
2510 if (converted == null)
2517 public class SwitchSection {
2518 // An array of SwitchLabels.
2519 public readonly ArrayList Labels;
2520 public readonly Block Block;
2522 public SwitchSection (ArrayList labels, Block block)
2529 public class Switch : Statement {
2530 public readonly ArrayList Sections;
2531 public Expression Expr;
2534 /// Maps constants whose type type SwitchType to their SwitchLabels.
2536 public Hashtable Elements;
2539 /// The governing switch type
2541 public Type SwitchType;
2547 Label default_target;
2548 Expression new_expr;
2551 // The types allowed to be implicitly cast from
2552 // on the governing type
2554 static Type [] allowed_types;
2556 public Switch (Expression e, ArrayList sects, Location l)
2563 public bool GotDefault {
2569 public Label DefaultTarget {
2571 return default_target;
2576 // Determines the governing type for a switch. The returned
2577 // expression might be the expression from the switch, or an
2578 // expression that includes any potential conversions to the
2579 // integral types or to string.
2581 Expression SwitchGoverningType (EmitContext ec, Type t)
2583 if (t == TypeManager.int32_type ||
2584 t == TypeManager.uint32_type ||
2585 t == TypeManager.char_type ||
2586 t == TypeManager.byte_type ||
2587 t == TypeManager.sbyte_type ||
2588 t == TypeManager.ushort_type ||
2589 t == TypeManager.short_type ||
2590 t == TypeManager.uint64_type ||
2591 t == TypeManager.int64_type ||
2592 t == TypeManager.string_type ||
2593 t == TypeManager.bool_type ||
2594 t.IsSubclassOf (TypeManager.enum_type))
2597 if (allowed_types == null){
2598 allowed_types = new Type [] {
2599 TypeManager.sbyte_type,
2600 TypeManager.byte_type,
2601 TypeManager.short_type,
2602 TypeManager.ushort_type,
2603 TypeManager.int32_type,
2604 TypeManager.uint32_type,
2605 TypeManager.int64_type,
2606 TypeManager.uint64_type,
2607 TypeManager.char_type,
2608 TypeManager.bool_type,
2609 TypeManager.string_type
2614 // Try to find a *user* defined implicit conversion.
2616 // If there is no implicit conversion, or if there are multiple
2617 // conversions, we have to report an error
2619 Expression converted = null;
2620 foreach (Type tt in allowed_types){
2623 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
2627 if (converted != null){
2628 Report.Error (-12, loc, "More than one conversion to an integral " +
2629 " type exists for type `" +
2630 TypeManager.CSharpName (Expr.Type)+"'");
2638 void error152 (string n)
2641 152, "The label `" + n + ":' " +
2642 "is already present on this switch statement");
2646 // Performs the basic sanity checks on the switch statement
2647 // (looks for duplicate keys and non-constant expressions).
2649 // It also returns a hashtable with the keys that we will later
2650 // use to compute the switch tables
2652 bool CheckSwitch (EmitContext ec)
2656 Elements = new Hashtable ();
2658 got_default = false;
2660 if (TypeManager.IsEnumType (SwitchType)){
2661 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2663 compare_type = SwitchType;
2665 foreach (SwitchSection ss in Sections){
2666 foreach (SwitchLabel sl in ss.Labels){
2667 if (!sl.ResolveAndReduce (ec, SwitchType)){
2672 if (sl.Label == null){
2674 error152 ("default");
2681 object key = sl.Converted;
2683 if (key is Constant)
2684 key = ((Constant) key).GetValue ();
2687 key = NullLiteral.Null;
2689 string lname = null;
2690 if (compare_type == TypeManager.uint64_type){
2691 ulong v = (ulong) key;
2693 if (Elements.Contains (v))
2694 lname = v.ToString ();
2696 Elements.Add (v, sl);
2697 } else if (compare_type == TypeManager.int64_type){
2698 long v = (long) key;
2700 if (Elements.Contains (v))
2701 lname = v.ToString ();
2703 Elements.Add (v, sl);
2704 } else if (compare_type == TypeManager.uint32_type){
2705 uint v = (uint) key;
2707 if (Elements.Contains (v))
2708 lname = v.ToString ();
2710 Elements.Add (v, sl);
2711 } else if (compare_type == TypeManager.char_type){
2712 char v = (char) key;
2714 if (Elements.Contains (v))
2715 lname = v.ToString ();
2717 Elements.Add (v, sl);
2718 } else if (compare_type == TypeManager.byte_type){
2719 byte v = (byte) key;
2721 if (Elements.Contains (v))
2722 lname = v.ToString ();
2724 Elements.Add (v, sl);
2725 } else if (compare_type == TypeManager.sbyte_type){
2726 sbyte v = (sbyte) key;
2728 if (Elements.Contains (v))
2729 lname = v.ToString ();
2731 Elements.Add (v, sl);
2732 } else if (compare_type == TypeManager.short_type){
2733 short v = (short) key;
2735 if (Elements.Contains (v))
2736 lname = v.ToString ();
2738 Elements.Add (v, sl);
2739 } else if (compare_type == TypeManager.ushort_type){
2740 ushort v = (ushort) key;
2742 if (Elements.Contains (v))
2743 lname = v.ToString ();
2745 Elements.Add (v, sl);
2746 } else if (compare_type == TypeManager.string_type){
2747 if (key is NullLiteral){
2748 if (Elements.Contains (NullLiteral.Null))
2751 Elements.Add (NullLiteral.Null, null);
2753 string s = (string) key;
2755 if (Elements.Contains (s))
2758 Elements.Add (s, sl);
2760 } else if (compare_type == TypeManager.int32_type) {
2763 if (Elements.Contains (v))
2764 lname = v.ToString ();
2766 Elements.Add (v, sl);
2767 } else if (compare_type == TypeManager.bool_type) {
2768 bool v = (bool) key;
2770 if (Elements.Contains (v))
2771 lname = v.ToString ();
2773 Elements.Add (v, sl);
2777 throw new Exception ("Unknown switch type!" +
2778 SwitchType + " " + compare_type);
2782 error152 ("case + " + lname);
2793 void EmitObjectInteger (ILGenerator ig, object k)
2796 IntConstant.EmitInt (ig, (int) k);
2797 else if (k is Constant) {
2798 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2801 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2804 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2806 IntConstant.EmitInt (ig, (int) (long) k);
2807 ig.Emit (OpCodes.Conv_I8);
2810 LongConstant.EmitLong (ig, (long) k);
2812 else if (k is ulong)
2814 if ((ulong) k < (1L<<32))
2816 IntConstant.EmitInt (ig, (int) (long) k);
2817 ig.Emit (OpCodes.Conv_U8);
2821 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2825 IntConstant.EmitInt (ig, (int) ((char) k));
2826 else if (k is sbyte)
2827 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2829 IntConstant.EmitInt (ig, (int) ((byte) k));
2830 else if (k is short)
2831 IntConstant.EmitInt (ig, (int) ((short) k));
2832 else if (k is ushort)
2833 IntConstant.EmitInt (ig, (int) ((ushort) k));
2835 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2837 throw new Exception ("Unhandled case");
2840 // structure used to hold blocks of keys while calculating table switch
2841 class KeyBlock : IComparable
2843 public KeyBlock (long _nFirst)
2845 nFirst = nLast = _nFirst;
2849 public ArrayList rgKeys = null;
2852 get { return (int) (nLast - nFirst + 1); }
2854 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2856 return kbLast.nLast - kbFirst.nFirst + 1;
2858 public int CompareTo (object obj)
2860 KeyBlock kb = (KeyBlock) obj;
2861 int nLength = Length;
2862 int nLengthOther = kb.Length;
2863 if (nLengthOther == nLength)
2864 return (int) (kb.nFirst - nFirst);
2865 return nLength - nLengthOther;
2870 /// This method emits code for a lookup-based switch statement (non-string)
2871 /// Basically it groups the cases into blocks that are at least half full,
2872 /// and then spits out individual lookup opcodes for each block.
2873 /// It emits the longest blocks first, and short blocks are just
2874 /// handled with direct compares.
2876 /// <param name="ec"></param>
2877 /// <param name="val"></param>
2878 /// <returns></returns>
2879 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
2881 int cElements = Elements.Count;
2882 object [] rgKeys = new object [cElements];
2883 Elements.Keys.CopyTo (rgKeys, 0);
2884 Array.Sort (rgKeys);
2886 // initialize the block list with one element per key
2887 ArrayList rgKeyBlocks = new ArrayList ();
2888 foreach (object key in rgKeys)
2889 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
2892 // iteratively merge the blocks while they are at least half full
2893 // there's probably a really cool way to do this with a tree...
2894 while (rgKeyBlocks.Count > 1)
2896 ArrayList rgKeyBlocksNew = new ArrayList ();
2897 kbCurr = (KeyBlock) rgKeyBlocks [0];
2898 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2900 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2901 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
2904 kbCurr.nLast = kb.nLast;
2908 // start a new block
2909 rgKeyBlocksNew.Add (kbCurr);
2913 rgKeyBlocksNew.Add (kbCurr);
2914 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2916 rgKeyBlocks = rgKeyBlocksNew;
2919 // initialize the key lists
2920 foreach (KeyBlock kb in rgKeyBlocks)
2921 kb.rgKeys = new ArrayList ();
2923 // fill the key lists
2925 if (rgKeyBlocks.Count > 0) {
2926 kbCurr = (KeyBlock) rgKeyBlocks [0];
2927 foreach (object key in rgKeys)
2929 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
2931 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2932 kbCurr.rgKeys.Add (key);
2936 // sort the blocks so we can tackle the largest ones first
2937 rgKeyBlocks.Sort ();
2939 // okay now we can start...
2940 ILGenerator ig = ec.ig;
2941 Label lblEnd = ig.DefineLabel (); // at the end ;-)
2942 Label lblDefault = ig.DefineLabel ();
2944 Type typeKeys = null;
2945 if (rgKeys.Length > 0)
2946 typeKeys = rgKeys [0].GetType (); // used for conversions
2948 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2950 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2951 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2954 foreach (object key in kb.rgKeys)
2956 ig.Emit (OpCodes.Ldloc, val);
2957 EmitObjectInteger (ig, key);
2958 SwitchLabel sl = (SwitchLabel) Elements [key];
2959 ig.Emit (OpCodes.Beq, sl.ILLabel);
2964 // TODO: if all the keys in the block are the same and there are
2965 // no gaps/defaults then just use a range-check.
2966 if (SwitchType == TypeManager.int64_type ||
2967 SwitchType == TypeManager.uint64_type)
2969 // TODO: optimize constant/I4 cases
2971 // check block range (could be > 2^31)
2972 ig.Emit (OpCodes.Ldloc, val);
2973 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
2974 ig.Emit (OpCodes.Blt, lblDefault);
2975 ig.Emit (OpCodes.Ldloc, val);
2976 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
2977 ig.Emit (OpCodes.Bgt, lblDefault);
2980 ig.Emit (OpCodes.Ldloc, val);
2983 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
2984 ig.Emit (OpCodes.Sub);
2986 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
2991 ig.Emit (OpCodes.Ldloc, val);
2992 int nFirst = (int) kb.nFirst;
2995 IntConstant.EmitInt (ig, nFirst);
2996 ig.Emit (OpCodes.Sub);
2998 else if (nFirst < 0)
3000 IntConstant.EmitInt (ig, -nFirst);
3001 ig.Emit (OpCodes.Add);
3005 // first, build the list of labels for the switch
3007 int cJumps = kb.Length;
3008 Label [] rgLabels = new Label [cJumps];
3009 for (int iJump = 0; iJump < cJumps; iJump++)
3011 object key = kb.rgKeys [iKey];
3012 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
3014 SwitchLabel sl = (SwitchLabel) Elements [key];
3015 rgLabels [iJump] = sl.ILLabel;
3019 rgLabels [iJump] = lblDefault;
3021 // emit the switch opcode
3022 ig.Emit (OpCodes.Switch, rgLabels);
3025 // mark the default for this block
3027 ig.MarkLabel (lblDefault);
3030 // TODO: find the default case and emit it here,
3031 // to prevent having to do the following jump.
3032 // make sure to mark other labels in the default section
3034 // the last default just goes to the end
3035 ig.Emit (OpCodes.Br, lblDefault);
3037 // now emit the code for the sections
3038 bool fFoundDefault = false;
3039 bool fAllReturn = true;
3040 foreach (SwitchSection ss in Sections)
3042 foreach (SwitchLabel sl in ss.Labels)
3044 ig.MarkLabel (sl.ILLabel);
3045 ig.MarkLabel (sl.ILLabelCode);
3046 if (sl.Label == null)
3048 ig.MarkLabel (lblDefault);
3049 fFoundDefault = true;
3052 fAllReturn &= ss.Block.Emit (ec);
3053 //ig.Emit (OpCodes.Br, lblEnd);
3056 if (!fFoundDefault) {
3057 ig.MarkLabel (lblDefault);
3060 ig.MarkLabel (lblEnd);
3065 // This simple emit switch works, but does not take advantage of the
3067 // TODO: remove non-string logic from here
3068 // TODO: binary search strings?
3070 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3072 ILGenerator ig = ec.ig;
3073 Label end_of_switch = ig.DefineLabel ();
3074 Label next_test = ig.DefineLabel ();
3075 Label null_target = ig.DefineLabel ();
3076 bool default_found = false;
3077 bool first_test = true;
3078 bool pending_goto_end = false;
3079 bool all_return = true;
3080 bool is_string = false;
3084 // Special processing for strings: we cant compare
3087 if (SwitchType == TypeManager.string_type){
3088 ig.Emit (OpCodes.Ldloc, val);
3091 if (Elements.Contains (NullLiteral.Null)){
3092 ig.Emit (OpCodes.Brfalse, null_target);
3094 ig.Emit (OpCodes.Brfalse, default_target);
3096 ig.Emit (OpCodes.Ldloc, val);
3097 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
3098 ig.Emit (OpCodes.Stloc, val);
3101 SwitchSection last_section;
3102 last_section = (SwitchSection) Sections [Sections.Count-1];
3104 foreach (SwitchSection ss in Sections){
3105 Label sec_begin = ig.DefineLabel ();
3107 if (pending_goto_end)
3108 ig.Emit (OpCodes.Br, end_of_switch);
3110 int label_count = ss.Labels.Count;
3112 foreach (SwitchLabel sl in ss.Labels){
3113 ig.MarkLabel (sl.ILLabel);
3116 ig.MarkLabel (next_test);
3117 next_test = ig.DefineLabel ();
3120 // If we are the default target
3122 if (sl.Label == null){
3123 ig.MarkLabel (default_target);
3124 default_found = true;
3126 object lit = sl.Converted;
3128 if (lit is NullLiteral){
3130 if (label_count == 1)
3131 ig.Emit (OpCodes.Br, next_test);
3136 StringConstant str = (StringConstant) lit;
3138 ig.Emit (OpCodes.Ldloc, val);
3139 ig.Emit (OpCodes.Ldstr, str.Value);
3140 if (label_count == 1)
3141 ig.Emit (OpCodes.Bne_Un, next_test);
3143 ig.Emit (OpCodes.Beq, sec_begin);
3145 ig.Emit (OpCodes.Ldloc, val);
3146 EmitObjectInteger (ig, lit);
3147 ig.Emit (OpCodes.Ceq);
3148 if (label_count == 1)
3149 ig.Emit (OpCodes.Brfalse, next_test);
3151 ig.Emit (OpCodes.Brtrue, sec_begin);
3155 if (label_count != 1 && ss != last_section)
3156 ig.Emit (OpCodes.Br, next_test);
3159 ig.MarkLabel (null_target);
3160 ig.MarkLabel (sec_begin);
3161 foreach (SwitchLabel sl in ss.Labels)
\r
3162 ig.MarkLabel (sl.ILLabelCode);
3163 if (ss.Block.Emit (ec))
3164 pending_goto_end = false;
3167 pending_goto_end = true;
3171 if (!default_found){
3172 ig.MarkLabel (default_target);
3175 ig.MarkLabel (next_test);
3176 ig.MarkLabel (end_of_switch);
3181 public override bool Resolve (EmitContext ec)
3183 Expr = Expr.Resolve (ec);
3187 new_expr = SwitchGoverningType (ec, Expr.Type);
3188 if (new_expr == null){
3189 Report.Error (151, loc, "An integer type or string was expected for switch");
3194 SwitchType = new_expr.Type;
3196 if (!CheckSwitch (ec))
3199 Switch old_switch = ec.Switch;
3201 ec.Switch.SwitchType = SwitchType;
3203 ec.StartFlowBranching (FlowBranchingType.SWITCH, loc);
3206 foreach (SwitchSection ss in Sections){
3208 ec.CurrentBranching.CreateSibling ();
3212 if (ss.Block.Resolve (ec) != true)
3216 ec.EndFlowBranching ();
3217 ec.Switch = old_switch;
3222 public override bool Emit (EmitContext ec)
3224 // Store variable for comparission purposes
3225 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
3227 ec.ig.Emit (OpCodes.Stloc, value);
3229 ILGenerator ig = ec.ig;
3231 default_target = ig.DefineLabel ();
3234 // Setup the codegen context
3236 Label old_end = ec.LoopEnd;
3237 Switch old_switch = ec.Switch;
3239 ec.LoopEnd = ig.DefineLabel ();
3244 if (SwitchType == TypeManager.string_type)
3245 all_return = SimpleSwitchEmit (ec, value);
3247 all_return = TableSwitchEmit (ec, value);
3249 // Restore context state.
3250 ig.MarkLabel (ec.LoopEnd);
3253 // Restore the previous context
3255 ec.LoopEnd = old_end;
3256 ec.Switch = old_switch;
3262 public class Lock : Statement {
3264 Statement Statement;
3266 public Lock (Expression expr, Statement stmt, Location l)
3273 public override bool Resolve (EmitContext ec)
3275 expr = expr.Resolve (ec);
3276 return Statement.Resolve (ec) && expr != null;
3279 public override bool Emit (EmitContext ec)
3281 Type type = expr.Type;
3284 if (type.IsValueType){
3285 Report.Error (185, loc, "lock statement requires the expression to be " +
3286 " a reference type (type is: `" +
3287 TypeManager.CSharpName (type) + "'");
3291 ILGenerator ig = ec.ig;
3292 LocalBuilder temp = ig.DeclareLocal (type);
3295 ig.Emit (OpCodes.Dup);
3296 ig.Emit (OpCodes.Stloc, temp);
3297 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3300 Label end = ig.BeginExceptionBlock ();
3301 bool old_in_try = ec.InTry;
3303 Label finish = ig.DefineLabel ();
3304 val = Statement.Emit (ec);
3305 ec.InTry = old_in_try;
3306 // ig.Emit (OpCodes.Leave, finish);
3308 ig.MarkLabel (finish);
3311 ig.BeginFinallyBlock ();
3312 ig.Emit (OpCodes.Ldloc, temp);
3313 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3314 ig.EndExceptionBlock ();
3320 public class Unchecked : Statement {
3321 public readonly Block Block;
3323 public Unchecked (Block b)
3328 public override bool Resolve (EmitContext ec)
3330 return Block.Resolve (ec);
3333 public override bool Emit (EmitContext ec)
3335 bool previous_state = ec.CheckState;
3336 bool previous_state_const = ec.ConstantCheckState;
3339 ec.CheckState = false;
3340 ec.ConstantCheckState = false;
3341 val = Block.Emit (ec);
3342 ec.CheckState = previous_state;
3343 ec.ConstantCheckState = previous_state_const;
3349 public class Checked : Statement {
3350 public readonly Block Block;
3352 public Checked (Block b)
3357 public override bool Resolve (EmitContext ec)
3359 bool previous_state = ec.CheckState;
3360 bool previous_state_const = ec.ConstantCheckState;
3362 ec.CheckState = true;
3363 ec.ConstantCheckState = true;
3364 bool ret = Block.Resolve (ec);
3365 ec.CheckState = previous_state;
3366 ec.ConstantCheckState = previous_state_const;
3371 public override bool Emit (EmitContext ec)
3373 bool previous_state = ec.CheckState;
3374 bool previous_state_const = ec.ConstantCheckState;
3377 ec.CheckState = true;
3378 ec.ConstantCheckState = true;
3379 val = Block.Emit (ec);
3380 ec.CheckState = previous_state;
3381 ec.ConstantCheckState = previous_state_const;
3387 public class Unsafe : Statement {
3388 public readonly Block Block;
3390 public Unsafe (Block b)
3395 public override bool Resolve (EmitContext ec)
3397 bool previous_state = ec.InUnsafe;
3401 val = Block.Resolve (ec);
3402 ec.InUnsafe = previous_state;
3407 public override bool Emit (EmitContext ec)
3409 bool previous_state = ec.InUnsafe;
3413 val = Block.Emit (ec);
3414 ec.InUnsafe = previous_state;
3423 public class Fixed : Statement {
3425 ArrayList declarators;
3426 Statement statement;
3431 public bool is_object;
3432 public VariableInfo vi;
3433 public Expression expr;
3434 public Expression converted;
3437 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3440 declarators = decls;
3445 public override bool Resolve (EmitContext ec)
3447 expr_type = ec.DeclSpace.ResolveType (type, false, loc);
3448 if (expr_type == null)
3451 data = new FixedData [declarators.Count];
3454 foreach (Pair p in declarators){
3455 VariableInfo vi = (VariableInfo) p.First;
3456 Expression e = (Expression) p.Second;
3461 // The rules for the possible declarators are pretty wise,
3462 // but the production on the grammar is more concise.
3464 // So we have to enforce these rules here.
3466 // We do not resolve before doing the case 1 test,
3467 // because the grammar is explicit in that the token &
3468 // is present, so we need to test for this particular case.
3472 // Case 1: & object.
3474 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3475 Expression child = ((Unary) e).Expr;
3478 if (child is ParameterReference || child is LocalVariableReference){
3481 "No need to use fixed statement for parameters or " +
3482 "local variable declarations (address is already " +
3491 child = ((Unary) e).Expr;
3493 if (!TypeManager.VerifyUnManaged (child.Type, loc))
3496 data [i].is_object = true;
3498 data [i].converted = null;
3512 if (e.Type.IsArray){
3513 Type array_type = e.Type.GetElementType ();
3517 // Provided that array_type is unmanaged,
3519 if (!TypeManager.VerifyUnManaged (array_type, loc))
3523 // and T* is implicitly convertible to the
3524 // pointer type given in the fixed statement.
3526 ArrayPtr array_ptr = new ArrayPtr (e);
3528 Expression converted = Expression.ConvertImplicitRequired (
3529 ec, array_ptr, vi.VariableType, loc);
3530 if (converted == null)
3533 data [i].is_object = false;
3535 data [i].converted = converted;
3545 if (e.Type == TypeManager.string_type){
3546 data [i].is_object = false;
3548 data [i].converted = null;
3554 return statement.Resolve (ec);
3557 public override bool Emit (EmitContext ec)
3559 ILGenerator ig = ec.ig;
3561 bool is_ret = false;
3563 for (int i = 0; i < data.Length; i++) {
3564 VariableInfo vi = data [i].vi;
3567 // Case 1: & object.
3569 if (data [i].is_object) {
3571 // Store pointer in pinned location
3573 data [i].expr.Emit (ec);
3574 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3576 is_ret = statement.Emit (ec);
3578 // Clear the pinned variable.
3579 ig.Emit (OpCodes.Ldc_I4_0);
3580 ig.Emit (OpCodes.Conv_U);
3581 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3589 if (data [i].expr.Type.IsArray){
3591 // Store pointer in pinned location
3593 data [i].converted.Emit (ec);
3595 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3597 is_ret = statement.Emit (ec);
3599 // Clear the pinned variable.
3600 ig.Emit (OpCodes.Ldc_I4_0);
3601 ig.Emit (OpCodes.Conv_U);
3602 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3610 if (data [i].expr.Type == TypeManager.string_type){
3611 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
3612 TypeManager.MakePinned (pinned_string);
3614 data [i].expr.Emit (ec);
3615 ig.Emit (OpCodes.Stloc, pinned_string);
3617 Expression sptr = new StringPtr (pinned_string);
3618 Expression converted = Expression.ConvertImplicitRequired (
3619 ec, sptr, vi.VariableType, loc);
3621 if (converted == null)
3624 converted.Emit (ec);
3625 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3627 is_ret = statement.Emit (ec);
3629 // Clear the pinned variable
3630 ig.Emit (OpCodes.Ldnull);
3631 ig.Emit (OpCodes.Stloc, pinned_string);
3639 public class Catch {
3640 public readonly Expression Type;
3641 public readonly string Name;
3642 public readonly Block Block;
3643 public readonly Location Location;
3645 public Catch (Expression type, string name, Block block, Location l)
3654 public class Try : Statement {
3655 public readonly Block Fini, Block;
3656 public readonly ArrayList Specific;
3657 public readonly Catch General;
3660 // specific, general and fini might all be null.
3662 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3664 if (specific == null && general == null){
3665 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3669 this.Specific = specific;
3670 this.General = general;
3675 public override bool Resolve (EmitContext ec)
3679 ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation);
3681 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3683 if (!Block.Resolve (ec))
3686 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3688 Report.Debug (1, "START OF CATCH BLOCKS", vector);
3690 foreach (Catch c in Specific){
3691 ec.CurrentBranching.CreateSibling ();
3692 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3694 if (c.Name != null) {
3695 VariableInfo vi = c.Block.GetVariableInfo (c.Name);
3697 throw new Exception ();
3702 if (!c.Block.Resolve (ec))
3705 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
3707 if ((current.Returns == FlowReturns.NEVER) ||
3708 (current.Returns == FlowReturns.SOMETIMES)) {
3709 vector.AndLocals (current);
3713 if (General != null){
3714 ec.CurrentBranching.CreateSibling ();
3715 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3717 if (!General.Block.Resolve (ec))
3720 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
3722 if ((current.Returns == FlowReturns.NEVER) ||
3723 (current.Returns == FlowReturns.SOMETIMES)) {
3724 vector.AndLocals (current);
3728 ec.CurrentBranching.CreateSiblingForFinally ();
3729 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3732 if (!Fini.Resolve (ec))
3735 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3737 FlowReturns returns = ec.EndFlowBranching ();
3739 Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector);
3741 if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) {
3742 ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc);
3745 ec.CurrentBranching.CurrentUsageVector.Or (vector);
3747 Report.Debug (1, "END OF TRY", ec.CurrentBranching);
3752 public override bool Emit (EmitContext ec)
3754 ILGenerator ig = ec.ig;
3756 Label finish = ig.DefineLabel ();;
3760 end = ig.BeginExceptionBlock ();
3761 bool old_in_try = ec.InTry;
3763 returns = Block.Emit (ec);
3764 ec.InTry = old_in_try;
3767 // System.Reflection.Emit provides this automatically:
3768 // ig.Emit (OpCodes.Leave, finish);
3770 bool old_in_catch = ec.InCatch;
3772 DeclSpace ds = ec.DeclSpace;
3774 foreach (Catch c in Specific){
3775 Type catch_type = ds.ResolveType (c.Type, false, c.Location);
3778 if (catch_type == null)
3781 ig.BeginCatchBlock (catch_type);
3783 if (c.Name != null){
3784 vi = c.Block.GetVariableInfo (c.Name);
3786 throw new Exception ("Variable does not exist in this block");
3788 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3790 ig.Emit (OpCodes.Pop);
3792 if (!c.Block.Emit (ec))
3796 if (General != null){
3797 ig.BeginCatchBlock (TypeManager.object_type);
3798 ig.Emit (OpCodes.Pop);
3799 if (!General.Block.Emit (ec))
3802 ec.InCatch = old_in_catch;
3804 ig.MarkLabel (finish);
3806 ig.BeginFinallyBlock ();
3807 bool old_in_finally = ec.InFinally;
3808 ec.InFinally = true;
3810 ec.InFinally = old_in_finally;
3813 ig.EndExceptionBlock ();
3816 if (!returns || ec.InTry || ec.InCatch)
3819 // Unfortunately, System.Reflection.Emit automatically emits a leave
3820 // to the end of the finally block. This is a problem if `returns'
3821 // is true since we may jump to a point after the end of the method.
3822 // As a workaround, emit an explicit ret here.
3824 if (ec.ReturnType != null)
3825 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3826 ec.ig.Emit (OpCodes.Ret);
3833 // FIXME: We still do not support the expression variant of the using
3836 public class Using : Statement {
3837 object expression_or_block;
3838 Statement Statement;
3843 Expression [] converted_vars;
3844 ExpressionStatement [] assign;
3846 public Using (object expression_or_block, Statement stmt, Location l)
3848 this.expression_or_block = expression_or_block;
3854 // Resolves for the case of using using a local variable declaration.
3856 bool ResolveLocalVariableDecls (EmitContext ec)
3858 bool need_conv = false;
3859 expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
3862 if (expr_type == null)
3866 // The type must be an IDisposable or an implicit conversion
3869 converted_vars = new Expression [var_list.Count];
3870 assign = new ExpressionStatement [var_list.Count];
3871 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3872 foreach (DictionaryEntry e in var_list){
3873 Expression var = (Expression) e.Key;
3875 var = var.ResolveLValue (ec, new EmptyExpression ());
3879 converted_vars [i] = Expression.ConvertImplicit (
3880 ec, var, TypeManager.idisposable_type, loc);
3882 if (converted_vars [i] == null)
3890 foreach (DictionaryEntry e in var_list){
3891 LocalVariableReference var = (LocalVariableReference) e.Key;
3892 Expression new_expr = (Expression) e.Value;
3895 a = new Assign (var, new_expr, loc);
3901 converted_vars [i] = var;
3902 assign [i] = (ExpressionStatement) a;
3909 bool ResolveExpression (EmitContext ec)
3911 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3912 conv = Expression.ConvertImplicit (
3913 ec, expr, TypeManager.idisposable_type, loc);
3923 // Emits the code for the case of using using a local variable declaration.
3925 bool EmitLocalVariableDecls (EmitContext ec)
3927 ILGenerator ig = ec.ig;
3930 bool old_in_try = ec.InTry;
3932 for (i = 0; i < assign.Length; i++) {
3933 assign [i].EmitStatement (ec);
3935 ig.BeginExceptionBlock ();
3937 Statement.Emit (ec);
3938 ec.InTry = old_in_try;
3940 bool old_in_finally = ec.InFinally;
3941 ec.InFinally = true;
3942 var_list.Reverse ();
3943 foreach (DictionaryEntry e in var_list){
3944 LocalVariableReference var = (LocalVariableReference) e.Key;
3945 Label skip = ig.DefineLabel ();
3948 ig.BeginFinallyBlock ();
3951 ig.Emit (OpCodes.Brfalse, skip);
3952 converted_vars [i].Emit (ec);
3953 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3954 ig.MarkLabel (skip);
3955 ig.EndExceptionBlock ();
3957 ec.InFinally = old_in_finally;
3962 bool EmitExpression (EmitContext ec)
3965 // Make a copy of the expression and operate on that.
3967 ILGenerator ig = ec.ig;
3968 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
3973 ig.Emit (OpCodes.Stloc, local_copy);
3975 bool old_in_try = ec.InTry;
3977 ig.BeginExceptionBlock ();
3978 Statement.Emit (ec);
3979 ec.InTry = old_in_try;
3981 Label skip = ig.DefineLabel ();
3982 bool old_in_finally = ec.InFinally;
3983 ig.BeginFinallyBlock ();
3984 ig.Emit (OpCodes.Ldloc, local_copy);
3985 ig.Emit (OpCodes.Brfalse, skip);
3986 ig.Emit (OpCodes.Ldloc, local_copy);
3987 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3988 ig.MarkLabel (skip);
3989 ec.InFinally = old_in_finally;
3990 ig.EndExceptionBlock ();
3995 public override bool Resolve (EmitContext ec)
3997 if (expression_or_block is DictionaryEntry){
3998 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
3999 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4001 if (!ResolveLocalVariableDecls (ec))
4004 } else if (expression_or_block is Expression){
4005 expr = (Expression) expression_or_block;
4007 expr = expr.Resolve (ec);
4011 expr_type = expr.Type;
4013 if (!ResolveExpression (ec))
4017 return Statement.Resolve (ec);
4020 public override bool Emit (EmitContext ec)
4022 if (expression_or_block is DictionaryEntry)
4023 return EmitLocalVariableDecls (ec);
4024 else if (expression_or_block is Expression)
4025 return EmitExpression (ec);
4032 /// Implementation of the foreach C# statement
4034 public class Foreach : Statement {
4036 LocalVariableReference variable;
4038 Statement statement;
4039 ForeachHelperMethods hm;
4040 Expression empty, conv;
4041 Type array_type, element_type;
4044 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4045 Statement stmt, Location l)
4048 this.variable = var;
4054 public override bool Resolve (EmitContext ec)
4056 expr = expr.Resolve (ec);
4060 var_type = ec.DeclSpace.ResolveType (type, false, loc);
4061 if (var_type == null)
4065 // We need an instance variable. Not sure this is the best
4066 // way of doing this.
4068 // FIXME: When we implement propertyaccess, will those turn
4069 // out to return values in ExprClass? I think they should.
4071 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4072 expr.eclass == ExprClass.PropertyAccess)){
4073 error1579 (expr.Type);
4077 if (expr.Type.IsArray) {
4078 array_type = expr.Type;
4079 element_type = array_type.GetElementType ();
4081 empty = new EmptyExpression (element_type);
4083 hm = ProbeCollectionType (ec, expr.Type);
4085 error1579 (expr.Type);
4089 array_type = expr.Type;
4090 element_type = hm.element_type;
4092 empty = new EmptyExpression (hm.element_type);
4096 // FIXME: maybe we can apply the same trick we do in the
4097 // array handling to avoid creating empty and conv in some cases.
4099 // Although it is not as important in this case, as the type
4100 // will not likely be object (what the enumerator will return).
4102 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
4106 if (variable.ResolveLValue (ec, empty) == null)
4109 if (!statement.Resolve (ec))
4116 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4118 static MethodInfo FetchMethodMoveNext (Type t)
4120 MemberInfo [] move_next_list;
4122 move_next_list = TypeContainer.FindMembers (
4123 t, MemberTypes.Method,
4124 BindingFlags.Public | BindingFlags.Instance,
4125 Type.FilterName, "MoveNext");
4126 if (move_next_list == null || move_next_list.Length == 0)
4129 foreach (MemberInfo m in move_next_list){
4130 MethodInfo mi = (MethodInfo) m;
4133 args = TypeManager.GetArgumentTypes (mi);
4134 if (args != null && args.Length == 0){
4135 if (mi.ReturnType == TypeManager.bool_type)
4143 // Retrieves a `public T get_Current ()' method from the Type `t'
4145 static MethodInfo FetchMethodGetCurrent (Type t)
4147 MemberInfo [] move_next_list;
4149 move_next_list = TypeContainer.FindMembers (
4150 t, MemberTypes.Method,
4151 BindingFlags.Public | BindingFlags.Instance,
4152 Type.FilterName, "get_Current");
4153 if (move_next_list == null || move_next_list.Length == 0)
4156 foreach (MemberInfo m in move_next_list){
4157 MethodInfo mi = (MethodInfo) m;
4160 args = TypeManager.GetArgumentTypes (mi);
4161 if (args != null && args.Length == 0)
4168 // This struct records the helper methods used by the Foreach construct
4170 class ForeachHelperMethods {
4171 public EmitContext ec;
4172 public MethodInfo get_enumerator;
4173 public MethodInfo move_next;
4174 public MethodInfo get_current;
4175 public Type element_type;
4177 public ForeachHelperMethods (EmitContext ec)
4180 this.element_type = TypeManager.object_type;
4184 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
4189 if (!(m is MethodInfo))
4192 if (m.Name != "GetEnumerator")
4195 MethodInfo mi = (MethodInfo) m;
4196 Type [] args = TypeManager.GetArgumentTypes (mi);
4198 if (args.Length != 0)
4201 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
4202 EmitContext ec = hm.ec;
4205 // Check whether GetEnumerator is accessible to us
4207 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
4209 Type declaring = mi.DeclaringType;
4210 if (prot == MethodAttributes.Private){
4211 if (declaring != ec.ContainerType)
4213 } else if (prot == MethodAttributes.FamANDAssem){
4214 // If from a different assembly, false
4215 if (!(mi is MethodBuilder))
4218 // Are we being invoked from the same class, or from a derived method?
4220 if (ec.ContainerType != declaring){
4221 if (!ec.ContainerType.IsSubclassOf (declaring))
4224 } else if (prot == MethodAttributes.FamORAssem){
4225 if (!(mi is MethodBuilder ||
4226 ec.ContainerType == declaring ||
4227 ec.ContainerType.IsSubclassOf (declaring)))
4229 } if (prot == MethodAttributes.Family){
4230 if (!(ec.ContainerType == declaring ||
4231 ec.ContainerType.IsSubclassOf (declaring)))
4236 // Ok, we can access it, now make sure that we can do something
4237 // with this `GetEnumerator'
4240 if (mi.ReturnType == TypeManager.ienumerator_type ||
4241 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
4242 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
4243 hm.move_next = TypeManager.bool_movenext_void;
4244 hm.get_current = TypeManager.object_getcurrent_void;
4249 // Ok, so they dont return an IEnumerable, we will have to
4250 // find if they support the GetEnumerator pattern.
4252 Type return_type = mi.ReturnType;
4254 hm.move_next = FetchMethodMoveNext (return_type);
4255 if (hm.move_next == null)
4257 hm.get_current = FetchMethodGetCurrent (return_type);
4258 if (hm.get_current == null)
4261 hm.element_type = hm.get_current.ReturnType;
4267 /// This filter is used to find the GetEnumerator method
4268 /// on which IEnumerator operates
4270 static MemberFilter FilterEnumerator;
4274 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
4277 void error1579 (Type t)
4279 Report.Error (1579, loc,
4280 "foreach statement cannot operate on variables of type `" +
4281 t.FullName + "' because that class does not provide a " +
4282 " GetEnumerator method or it is inaccessible");
4285 static bool TryType (Type t, ForeachHelperMethods hm)
4289 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
4290 BindingFlags.Public | BindingFlags.NonPublic |
4291 BindingFlags.Instance,
4292 FilterEnumerator, hm);
4294 if (mi == null || mi.Length == 0)
4297 hm.get_enumerator = (MethodInfo) mi [0];
4302 // Looks for a usable GetEnumerator in the Type, and if found returns
4303 // the three methods that participate: GetEnumerator, MoveNext and get_Current
4305 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
4307 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
4309 if (TryType (t, hm))
4313 // Now try to find the method in the interfaces
4316 Type [] ifaces = t.GetInterfaces ();
4318 foreach (Type i in ifaces){
4319 if (TryType (i, hm))
4324 // Since TypeBuilder.GetInterfaces only returns the interface
4325 // types for this type, we have to keep looping, but once
4326 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4327 // done, because it returns all the types
4329 if ((t is TypeBuilder))
4339 // FIXME: possible optimization.
4340 // We might be able to avoid creating `empty' if the type is the sam
4342 bool EmitCollectionForeach (EmitContext ec)
4344 ILGenerator ig = ec.ig;
4345 LocalBuilder enumerator, disposable;
4347 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
4348 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
4351 // Instantiate the enumerator
4353 if (expr.Type.IsValueType){
4354 if (expr is IMemoryLocation){
4355 IMemoryLocation ml = (IMemoryLocation) expr;
4357 ml.AddressOf (ec, AddressOp.Load);
4359 throw new Exception ("Expr " + expr + " of type " + expr.Type +
4360 " does not implement IMemoryLocation");
4361 ig.Emit (OpCodes.Call, hm.get_enumerator);
4364 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
4366 ig.Emit (OpCodes.Stloc, enumerator);
4369 // Protect the code in a try/finalize block, so that
4370 // if the beast implement IDisposable, we get rid of it
4372 Label l = ig.BeginExceptionBlock ();
4373 bool old_in_try = ec.InTry;
4376 Label end_try = ig.DefineLabel ();
4378 ig.MarkLabel (ec.LoopBegin);
4379 ig.Emit (OpCodes.Ldloc, enumerator);
4380 ig.Emit (OpCodes.Callvirt, hm.move_next);
4381 ig.Emit (OpCodes.Brfalse, end_try);
4382 ig.Emit (OpCodes.Ldloc, enumerator);
4383 ig.Emit (OpCodes.Callvirt, hm.get_current);
4384 variable.EmitAssign (ec, conv);
4385 statement.Emit (ec);
4386 ig.Emit (OpCodes.Br, ec.LoopBegin);
4387 ig.MarkLabel (end_try);
4388 ec.InTry = old_in_try;
4390 // The runtime provides this for us.
4391 // ig.Emit (OpCodes.Leave, end);
4394 // Now the finally block
4396 Label end_finally = ig.DefineLabel ();
4397 bool old_in_finally = ec.InFinally;
4398 ec.InFinally = true;
4399 ig.BeginFinallyBlock ();
4401 ig.Emit (OpCodes.Ldloc, enumerator);
4402 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
4403 ig.Emit (OpCodes.Stloc, disposable);
4404 ig.Emit (OpCodes.Ldloc, disposable);
4405 ig.Emit (OpCodes.Brfalse, end_finally);
4406 ig.Emit (OpCodes.Ldloc, disposable);
4407 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4408 ig.MarkLabel (end_finally);
4409 ec.InFinally = old_in_finally;
4411 // The runtime generates this anyways.
4412 // ig.Emit (OpCodes.Endfinally);
4414 ig.EndExceptionBlock ();
4416 ig.MarkLabel (ec.LoopEnd);
4421 // FIXME: possible optimization.
4422 // We might be able to avoid creating `empty' if the type is the sam
4424 bool EmitArrayForeach (EmitContext ec)
4426 int rank = array_type.GetArrayRank ();
4427 ILGenerator ig = ec.ig;
4429 LocalBuilder copy = ig.DeclareLocal (array_type);
4432 // Make our copy of the array
4435 ig.Emit (OpCodes.Stloc, copy);
4438 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
4442 ig.Emit (OpCodes.Ldc_I4_0);
4443 ig.Emit (OpCodes.Stloc, counter);
4444 test = ig.DefineLabel ();
4445 ig.Emit (OpCodes.Br, test);
4447 loop = ig.DefineLabel ();
4448 ig.MarkLabel (loop);
4450 ig.Emit (OpCodes.Ldloc, copy);
4451 ig.Emit (OpCodes.Ldloc, counter);
4452 ArrayAccess.EmitLoadOpcode (ig, var_type);
4454 variable.EmitAssign (ec, conv);
4456 statement.Emit (ec);
4458 ig.MarkLabel (ec.LoopBegin);
4459 ig.Emit (OpCodes.Ldloc, counter);
4460 ig.Emit (OpCodes.Ldc_I4_1);
4461 ig.Emit (OpCodes.Add);
4462 ig.Emit (OpCodes.Stloc, counter);
4464 ig.MarkLabel (test);
4465 ig.Emit (OpCodes.Ldloc, counter);
4466 ig.Emit (OpCodes.Ldloc, copy);
4467 ig.Emit (OpCodes.Ldlen);
4468 ig.Emit (OpCodes.Conv_I4);
4469 ig.Emit (OpCodes.Blt, loop);
4471 LocalBuilder [] dim_len = new LocalBuilder [rank];
4472 LocalBuilder [] dim_count = new LocalBuilder [rank];
4473 Label [] loop = new Label [rank];
4474 Label [] test = new Label [rank];
4477 for (dim = 0; dim < rank; dim++){
4478 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
4479 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
4480 test [dim] = ig.DefineLabel ();
4481 loop [dim] = ig.DefineLabel ();
4484 for (dim = 0; dim < rank; dim++){
4485 ig.Emit (OpCodes.Ldloc, copy);
4486 IntLiteral.EmitInt (ig, dim);
4487 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
4488 ig.Emit (OpCodes.Stloc, dim_len [dim]);
4491 for (dim = 0; dim < rank; dim++){
4492 ig.Emit (OpCodes.Ldc_I4_0);
4493 ig.Emit (OpCodes.Stloc, dim_count [dim]);
4494 ig.Emit (OpCodes.Br, test [dim]);
4495 ig.MarkLabel (loop [dim]);
4498 ig.Emit (OpCodes.Ldloc, copy);
4499 for (dim = 0; dim < rank; dim++)
4500 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
4503 // FIXME: Maybe we can cache the computation of `get'?
4505 Type [] args = new Type [rank];
4508 for (int i = 0; i < rank; i++)
4509 args [i] = TypeManager.int32_type;
4511 ModuleBuilder mb = CodeGen.ModuleBuilder;
4512 get = mb.GetArrayMethod (
4514 CallingConventions.HasThis| CallingConventions.Standard,
4516 ig.Emit (OpCodes.Call, get);
4517 variable.EmitAssign (ec, conv);
4518 statement.Emit (ec);
4519 ig.MarkLabel (ec.LoopBegin);
4520 for (dim = rank - 1; dim >= 0; dim--){
4521 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
4522 ig.Emit (OpCodes.Ldc_I4_1);
4523 ig.Emit (OpCodes.Add);
4524 ig.Emit (OpCodes.Stloc, dim_count [dim]);
4526 ig.MarkLabel (test [dim]);
4527 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
4528 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
4529 ig.Emit (OpCodes.Blt, loop [dim]);
4532 ig.MarkLabel (ec.LoopEnd);
4537 public override bool Emit (EmitContext ec)
4541 ILGenerator ig = ec.ig;
4543 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4544 bool old_inloop = ec.InLoop;
4545 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
4546 ec.LoopBegin = ig.DefineLabel ();
4547 ec.LoopEnd = ig.DefineLabel ();
4549 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
4552 ret_val = EmitCollectionForeach (ec);
4554 ret_val = EmitArrayForeach (ec);
4556 ec.LoopBegin = old_begin;
4557 ec.LoopEnd = old_end;
4558 ec.InLoop = old_inloop;
4559 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;