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)
44 Report.Debug (8, "MARK", this, loc);
48 public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
54 if (e.Type != TypeManager.bool_type){
55 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
61 31, loc, "Can not convert the expression to a boolean");
70 /// Encapsulates the emission of a boolean test and jumping to a
73 /// This will emit the bool expression in `bool_expr' and if
74 /// `target_is_for_true' is true, then the code will generate a
75 /// brtrue to the target. Otherwise a brfalse.
77 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
78 Label target, bool target_is_for_true)
80 ILGenerator ig = ec.ig;
83 if (bool_expr is Unary){
84 Unary u = (Unary) bool_expr;
86 if (u.Oper == Unary.Operator.LogicalNot){
89 u.EmitLogicalNot (ec);
91 } else if (bool_expr is Binary){
92 Binary b = (Binary) bool_expr;
94 if (b.EmitBranchable (ec, target, target_is_for_true))
101 if (target_is_for_true){
103 ig.Emit (OpCodes.Brfalse, target);
105 ig.Emit (OpCodes.Brtrue, target);
108 ig.Emit (OpCodes.Brtrue, target);
110 ig.Emit (OpCodes.Brfalse, target);
114 public static void Warning_DeadCodeFound (Location loc)
116 Report.Warning (162, loc, "Unreachable code detected");
120 public class EmptyStatement : Statement {
121 public override bool Resolve (EmitContext ec)
126 protected override bool DoEmit (EmitContext ec)
132 public class If : Statement {
134 public Statement TrueStatement;
135 public Statement FalseStatement;
137 public If (Expression expr, Statement trueStatement, Location l)
140 TrueStatement = trueStatement;
144 public If (Expression expr,
145 Statement trueStatement,
146 Statement falseStatement,
150 TrueStatement = trueStatement;
151 FalseStatement = falseStatement;
155 public override bool Resolve (EmitContext ec)
157 Report.Debug (1, "START IF BLOCK", loc);
159 expr = ResolveBoolean (ec, expr, loc);
164 ec.StartFlowBranching (FlowBranchingType.BLOCK, loc);
166 if (!TrueStatement.Resolve (ec)) {
167 ec.KillFlowBranching ();
171 ec.CurrentBranching.CreateSibling ();
173 if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
174 ec.KillFlowBranching ();
178 ec.EndFlowBranching ();
180 Report.Debug (1, "END IF BLOCK", loc);
185 protected override bool DoEmit (EmitContext ec)
187 ILGenerator ig = ec.ig;
188 Label false_target = ig.DefineLabel ();
190 bool is_true_ret, is_false_ret;
193 // Dead code elimination
195 if (expr is BoolConstant){
196 bool take = ((BoolConstant) expr).Value;
199 if (FalseStatement != null){
200 Warning_DeadCodeFound (FalseStatement.loc);
202 return TrueStatement.Emit (ec);
204 Warning_DeadCodeFound (TrueStatement.loc);
205 if (FalseStatement != null)
206 return FalseStatement.Emit (ec);
210 EmitBoolExpression (ec, expr, false_target, false);
212 is_true_ret = TrueStatement.Emit (ec);
213 is_false_ret = is_true_ret;
215 if (FalseStatement != null){
216 bool branch_emitted = false;
218 end = ig.DefineLabel ();
220 ig.Emit (OpCodes.Br, end);
221 branch_emitted = true;
224 ig.MarkLabel (false_target);
225 is_false_ret = FalseStatement.Emit (ec);
230 ig.MarkLabel (false_target);
231 is_false_ret = false;
234 return is_true_ret && is_false_ret;
238 public class Do : Statement {
239 public Expression expr;
240 public readonly Statement EmbeddedStatement;
241 bool infinite, may_return;
243 public Do (Statement statement, Expression boolExpr, Location l)
246 EmbeddedStatement = statement;
250 public override bool Resolve (EmitContext ec)
254 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
256 if (!EmbeddedStatement.Resolve (ec))
259 expr = ResolveBoolean (ec, expr, loc);
262 else if (expr is BoolConstant){
263 bool res = ((BoolConstant) expr).Value;
269 ec.CurrentBranching.Infinite = infinite;
270 FlowReturns returns = ec.EndFlowBranching ();
271 may_return = returns != FlowReturns.NEVER;
276 protected override bool DoEmit (EmitContext ec)
278 ILGenerator ig = ec.ig;
279 Label loop = ig.DefineLabel ();
280 Label old_begin = ec.LoopBegin;
281 Label old_end = ec.LoopEnd;
282 bool old_inloop = ec.InLoop;
283 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
285 ec.LoopBegin = ig.DefineLabel ();
286 ec.LoopEnd = ig.DefineLabel ();
288 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
291 EmbeddedStatement.Emit (ec);
292 ig.MarkLabel (ec.LoopBegin);
295 // Dead code elimination
297 if (expr is BoolConstant){
298 bool res = ((BoolConstant) expr).Value;
301 ec.ig.Emit (OpCodes.Br, loop);
303 EmitBoolExpression (ec, expr, loop, true);
305 ig.MarkLabel (ec.LoopEnd);
307 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
308 ec.LoopBegin = old_begin;
309 ec.LoopEnd = old_end;
310 ec.InLoop = old_inloop;
313 return may_return == false;
319 public class While : Statement {
320 public Expression expr;
321 public readonly Statement Statement;
322 bool may_return, empty, infinite;
324 public While (Expression boolExpr, Statement statement, Location l)
326 this.expr = boolExpr;
327 Statement = statement;
331 public override bool Resolve (EmitContext ec)
335 expr = ResolveBoolean (ec, expr, loc);
339 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
342 // Inform whether we are infinite or not
344 if (expr is BoolConstant){
345 BoolConstant bc = (BoolConstant) expr;
347 if (bc.Value == false){
348 Warning_DeadCodeFound (Statement.loc);
354 // We are not infinite, so the loop may or may not be executed.
356 ec.CurrentBranching.CreateSibling ();
359 if (!Statement.Resolve (ec))
363 ec.KillFlowBranching ();
365 ec.CurrentBranching.Infinite = infinite;
366 FlowReturns returns = ec.EndFlowBranching ();
367 may_return = returns != FlowReturns.NEVER;
373 protected override bool DoEmit (EmitContext ec)
378 ILGenerator ig = ec.ig;
379 Label old_begin = ec.LoopBegin;
380 Label old_end = ec.LoopEnd;
381 bool old_inloop = ec.InLoop;
382 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
385 ec.LoopBegin = ig.DefineLabel ();
386 ec.LoopEnd = ig.DefineLabel ();
388 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
391 // Inform whether we are infinite or not
393 if (expr is BoolConstant){
394 BoolConstant bc = (BoolConstant) expr;
396 ig.MarkLabel (ec.LoopBegin);
398 ig.Emit (OpCodes.Br, ec.LoopBegin);
401 // Inform that we are infinite (ie, `we return'), only
402 // if we do not `break' inside the code.
404 ret = may_return == false;
405 ig.MarkLabel (ec.LoopEnd);
407 Label while_loop = ig.DefineLabel ();
409 ig.Emit (OpCodes.Br, ec.LoopBegin);
410 ig.MarkLabel (while_loop);
414 ig.MarkLabel (ec.LoopBegin);
416 EmitBoolExpression (ec, expr, while_loop, true);
417 ig.MarkLabel (ec.LoopEnd);
422 ec.LoopBegin = old_begin;
423 ec.LoopEnd = old_end;
424 ec.InLoop = old_inloop;
425 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
431 public class For : Statement {
433 readonly Statement InitStatement;
434 readonly Statement Increment;
435 readonly Statement Statement;
436 bool may_return, infinite, empty;
438 public For (Statement initStatement,
444 InitStatement = initStatement;
446 Increment = increment;
447 Statement = statement;
451 public override bool Resolve (EmitContext ec)
455 if (InitStatement != null){
456 if (!InitStatement.Resolve (ec))
461 Test = ResolveBoolean (ec, Test, loc);
464 else if (Test is BoolConstant){
465 BoolConstant bc = (BoolConstant) Test;
467 if (bc.Value == false){
468 Warning_DeadCodeFound (Statement.loc);
476 if (Increment != null){
477 if (!Increment.Resolve (ec))
481 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
483 ec.CurrentBranching.CreateSibling ();
485 if (!Statement.Resolve (ec))
489 ec.KillFlowBranching ();
491 ec.CurrentBranching.Infinite = infinite;
492 FlowReturns returns = ec.EndFlowBranching ();
493 may_return = returns != FlowReturns.NEVER;
499 protected override bool DoEmit (EmitContext ec)
504 ILGenerator ig = ec.ig;
505 Label old_begin = ec.LoopBegin;
506 Label old_end = ec.LoopEnd;
507 bool old_inloop = ec.InLoop;
508 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
509 Label loop = ig.DefineLabel ();
510 Label test = ig.DefineLabel ();
512 if (InitStatement != null)
513 if (! (InitStatement is EmptyStatement))
514 InitStatement.Emit (ec);
516 ec.LoopBegin = ig.DefineLabel ();
517 ec.LoopEnd = ig.DefineLabel ();
519 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
521 ig.Emit (OpCodes.Br, test);
525 ig.MarkLabel (ec.LoopBegin);
526 if (!(Increment is EmptyStatement))
531 // If test is null, there is no test, and we are just
535 EmitBoolExpression (ec, Test, loop, true);
537 ig.Emit (OpCodes.Br, loop);
538 ig.MarkLabel (ec.LoopEnd);
540 ec.LoopBegin = old_begin;
541 ec.LoopEnd = old_end;
542 ec.InLoop = old_inloop;
543 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
546 // Inform whether we are infinite or not
549 if (Test is BoolConstant){
550 BoolConstant bc = (BoolConstant) Test;
553 return may_return == false;
557 return may_return == false;
561 public class StatementExpression : Statement {
564 public StatementExpression (ExpressionStatement expr, Location l)
570 public override bool Resolve (EmitContext ec)
572 expr = (Expression) expr.Resolve (ec);
576 protected override bool DoEmit (EmitContext ec)
578 ILGenerator ig = ec.ig;
580 if (expr is ExpressionStatement)
581 ((ExpressionStatement) expr).EmitStatement (ec);
584 ig.Emit (OpCodes.Pop);
590 public override string ToString ()
592 return "StatementExpression (" + expr + ")";
597 /// Implements the return statement
599 public class Return : Statement {
600 public Expression Expr;
602 public Return (Expression expr, Location l)
608 public override bool Resolve (EmitContext ec)
611 Expr = Expr.Resolve (ec);
616 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
618 if (ec.CurrentBranching.InTryBlock ())
619 ec.CurrentBranching.AddFinallyVector (vector);
621 vector.CheckOutParameters (ec.CurrentBranching);
623 vector.Returns = FlowReturns.ALWAYS;
624 vector.Breaks = FlowReturns.ALWAYS;
628 protected override bool DoEmit (EmitContext ec)
631 Report.Error (157,loc,"Control can not leave the body of the finally block");
635 if (ec.ReturnType == null){
637 Report.Error (127, loc, "Return with a value not allowed here");
642 Report.Error (126, loc, "An object of type `" +
643 TypeManager.CSharpName (ec.ReturnType) + "' is " +
644 "expected for the return statement");
648 if (Expr.Type != ec.ReturnType)
649 Expr = Expression.ConvertImplicitRequired (
650 ec, Expr, ec.ReturnType, loc);
657 if (ec.InTry || ec.InCatch)
658 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
661 if (ec.InTry || ec.InCatch) {
662 if (!ec.HasReturnLabel) {
663 ec.ReturnLabel = ec.ig.DefineLabel ();
664 ec.HasReturnLabel = true;
666 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
668 ec.ig.Emit (OpCodes.Ret);
674 public class Goto : Statement {
677 LabeledStatement label;
679 public override bool Resolve (EmitContext ec)
681 label = block.LookupLabel (target);
685 "No such label `" + target + "' in this scope");
689 // If this is a forward goto.
690 if (!label.IsDefined)
691 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
693 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
698 public Goto (Block parent_block, string label, Location l)
700 block = parent_block;
705 public string Target {
711 protected override bool DoEmit (EmitContext ec)
713 Label l = label.LabelTarget (ec);
714 ec.ig.Emit (OpCodes.Br, l);
720 public class LabeledStatement : Statement {
721 public readonly Location Location;
729 public LabeledStatement (string label_name, Location l)
731 this.label_name = label_name;
735 public Label LabelTarget (EmitContext ec)
739 label = ec.ig.DefineLabel ();
745 public bool IsDefined {
751 public bool HasBeenReferenced {
757 public void AddUsageVector (FlowBranching.UsageVector vector)
760 vectors = new ArrayList ();
762 vectors.Add (vector.Clone ());
765 public override bool Resolve (EmitContext ec)
768 ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors);
770 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER;
771 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER;
779 protected override bool DoEmit (EmitContext ec)
782 ec.ig.MarkLabel (label);
790 /// `goto default' statement
792 public class GotoDefault : Statement {
794 public GotoDefault (Location l)
799 public override bool Resolve (EmitContext ec)
801 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE;
805 protected override bool DoEmit (EmitContext ec)
807 if (ec.Switch == null){
808 Report.Error (153, loc, "goto default is only valid in a switch statement");
812 if (!ec.Switch.GotDefault){
813 Report.Error (159, loc, "No default target on switch statement");
816 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
822 /// `goto case' statement
824 public class GotoCase : Statement {
828 public GotoCase (Expression e, Location l)
834 public override bool Resolve (EmitContext ec)
836 if (ec.Switch == null){
837 Report.Error (153, loc, "goto case is only valid in a switch statement");
841 expr = expr.Resolve (ec);
845 if (!(expr is Constant)){
846 Report.Error (159, loc, "Target expression for goto case is not constant");
850 object val = Expression.ConvertIntLiteral (
851 (Constant) expr, ec.Switch.SwitchType, loc);
856 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
861 "No such label 'case " + val + "': for the goto case");
864 label = sl.ILLabelCode;
866 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE;
870 protected override bool DoEmit (EmitContext ec)
872 ec.ig.Emit (OpCodes.Br, label);
877 public class Throw : Statement {
880 public Throw (Expression expr, Location l)
886 public override bool Resolve (EmitContext ec)
889 expr = expr.Resolve (ec);
893 ExprClass eclass = expr.eclass;
895 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
896 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
897 expr.Error118 ("value, variable, property or indexer access ");
903 if ((t != TypeManager.exception_type) &&
904 !t.IsSubclassOf (TypeManager.exception_type) &&
905 !(expr is NullLiteral)) {
906 Report.Error (155, loc,
907 "The type caught or thrown must be derived " +
908 "from System.Exception");
913 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION;
914 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION;
918 protected override bool DoEmit (EmitContext ec)
922 ec.ig.Emit (OpCodes.Rethrow);
926 "A throw statement with no argument is only " +
927 "allowed in a catch clause");
934 ec.ig.Emit (OpCodes.Throw);
940 public class Break : Statement {
942 public Break (Location l)
947 public override bool Resolve (EmitContext ec)
949 ec.CurrentBranching.MayLeaveLoop = true;
950 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
954 protected override bool DoEmit (EmitContext ec)
956 ILGenerator ig = ec.ig;
958 if (ec.InLoop == false && ec.Switch == null){
959 Report.Error (139, loc, "No enclosing loop or switch to continue to");
963 if (ec.InTry || ec.InCatch)
964 ig.Emit (OpCodes.Leave, ec.LoopEnd);
966 ig.Emit (OpCodes.Br, ec.LoopEnd);
972 public class Continue : Statement {
974 public Continue (Location l)
979 public override bool Resolve (EmitContext ec)
981 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
985 protected override bool DoEmit (EmitContext ec)
987 Label begin = ec.LoopBegin;
990 Report.Error (139, loc, "No enclosing loop to continue to");
995 // UGH: Non trivial. This Br might cross a try/catch boundary
999 // try { ... } catch { continue; }
1003 // try {} catch { while () { continue; }}
1005 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
1006 ec.ig.Emit (OpCodes.Leave, begin);
1007 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
1008 throw new Exception ("Should never happen");
1010 ec.ig.Emit (OpCodes.Br, begin);
1016 // This is used in the control flow analysis code to specify whether the
1017 // current code block may return to its enclosing block before reaching
1020 public enum FlowReturns {
1021 // It can never return.
1024 // This means that the block contains a conditional return statement
1028 // The code always returns, ie. there's an unconditional return / break
1032 // The code always throws an exception.
1035 // The current code block is unreachable. This happens if it's immediately
1036 // following a FlowReturns.ALWAYS block.
1041 // This is a special bit vector which can inherit from another bit vector doing a
1042 // copy-on-write strategy. The inherited vector may have a smaller size than the
1045 public class MyBitVector {
1046 public readonly int Count;
1047 public readonly MyBitVector InheritsFrom;
1052 public MyBitVector (int Count)
1053 : this (null, Count)
1056 public MyBitVector (MyBitVector InheritsFrom, int Count)
1058 this.InheritsFrom = InheritsFrom;
1063 // Checks whether this bit vector has been modified. After setting this to true,
1064 // we won't use the inherited vector anymore, but our own copy of it.
1066 public bool IsDirty {
1073 initialize_vector ();
1078 // Get/set bit `index' in the bit vector.
1080 public bool this [int index]
1084 throw new ArgumentOutOfRangeException ();
1086 // We're doing a "copy-on-write" strategy here; as long
1087 // as nobody writes to the array, we can use our parent's
1088 // copy instead of duplicating the vector.
1091 return vector [index];
1092 else if (InheritsFrom != null) {
1093 BitArray inherited = InheritsFrom.Vector;
1095 if (index < inherited.Count)
1096 return inherited [index];
1105 throw new ArgumentOutOfRangeException ();
1107 // Only copy the vector if we're actually modifying it.
1109 if (this [index] != value) {
1110 initialize_vector ();
1112 vector [index] = value;
1118 // If you explicitly convert the MyBitVector to a BitArray, you will get a deep
1119 // copy of the bit vector.
1121 public static explicit operator BitArray (MyBitVector vector)
1123 vector.initialize_vector ();
1124 return vector.Vector;
1128 // Performs an `or' operation on the bit vector. The `new_vector' may have a
1129 // different size than the current one.
1131 public void Or (MyBitVector new_vector)
1133 BitArray new_array = new_vector.Vector;
1135 initialize_vector ();
1138 if (vector.Count < new_array.Count)
1139 upper = vector.Count;
1141 upper = new_array.Count;
1143 for (int i = 0; i < upper; i++)
1144 vector [i] = vector [i] | new_array [i];
1148 // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
1149 // a different size than the current one.
1151 public void And (MyBitVector new_vector)
1153 BitArray new_array = new_vector.Vector;
1155 initialize_vector ();
1158 if (vector.Count < new_array.Count)
1159 lower = upper = vector.Count;
1161 lower = new_array.Count;
1162 upper = vector.Count;
1165 for (int i = 0; i < lower; i++)
1166 vector [i] = vector [i] & new_array [i];
1168 for (int i = lower; i < upper; i++)
1173 // This does a deep copy of the bit vector.
1175 public MyBitVector Clone ()
1177 MyBitVector retval = new MyBitVector (Count);
1179 retval.Vector = Vector;
1188 else if (!is_dirty && (InheritsFrom != null))
1189 return InheritsFrom.Vector;
1191 initialize_vector ();
1197 initialize_vector ();
1199 for (int i = 0; i < Math.Min (vector.Count, value.Count); i++)
1200 vector [i] = value [i];
1204 void initialize_vector ()
1209 vector = new BitArray (Count, false);
1210 if (InheritsFrom != null)
1211 Vector = InheritsFrom.Vector;
1216 public override string ToString ()
1218 StringBuilder sb = new StringBuilder ("MyBitVector (");
1220 BitArray vector = Vector;
1224 sb.Append ("INHERITED - ");
1225 for (int i = 0; i < vector.Count; i++) {
1228 sb.Append (vector [i]);
1232 return sb.ToString ();
1237 // The type of a FlowBranching.
1239 public enum FlowBranchingType {
1240 // Normal (conditional or toplevel) block.
1257 // A new instance of this class is created every time a new block is resolved
1258 // and if there's branching in the block's control flow.
1260 public class FlowBranching {
1262 // The type of this flow branching.
1264 public readonly FlowBranchingType Type;
1267 // The block this branching is contained in. This may be null if it's not
1268 // a top-level block and it doesn't declare any local variables.
1270 public readonly Block Block;
1273 // The parent of this branching or null if this is the top-block.
1275 public readonly FlowBranching Parent;
1278 // Start-Location of this flow branching.
1280 public readonly Location Location;
1283 // A list of UsageVectors. A new vector is added each time control flow may
1284 // take a different path.
1286 public ArrayList Siblings;
1289 // If this is an infinite loop.
1291 public bool Infinite;
1294 // If we may leave the current loop.
1296 public bool MayLeaveLoop;
1301 InternalParameters param_info;
1303 MyStructInfo[] struct_params;
1305 ArrayList finally_vectors;
1307 static int next_id = 0;
1311 // Performs an `And' operation on the FlowReturns status
1312 // (for instance, a block only returns ALWAYS if all its siblings
1315 public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b)
1317 if (b == FlowReturns.UNREACHABLE)
1321 case FlowReturns.NEVER:
1322 if (b == FlowReturns.NEVER)
1323 return FlowReturns.NEVER;
1325 return FlowReturns.SOMETIMES;
1327 case FlowReturns.SOMETIMES:
1328 return FlowReturns.SOMETIMES;
1330 case FlowReturns.ALWAYS:
1331 if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION))
1332 return FlowReturns.ALWAYS;
1334 return FlowReturns.SOMETIMES;
1336 case FlowReturns.EXCEPTION:
1337 if (b == FlowReturns.EXCEPTION)
1338 return FlowReturns.EXCEPTION;
1339 else if (b == FlowReturns.ALWAYS)
1340 return FlowReturns.ALWAYS;
1342 return FlowReturns.SOMETIMES;
1349 // The vector contains a BitArray with information about which local variables
1350 // and parameters are already initialized at the current code position.
1352 public class UsageVector {
1354 // If this is true, then the usage vector has been modified and must be
1355 // merged when we're done with this branching.
1357 public bool IsDirty;
1360 // The number of parameters in this block.
1362 public readonly int CountParameters;
1365 // The number of locals in this block.
1367 public readonly int CountLocals;
1370 // If not null, then we inherit our state from this vector and do a
1371 // copy-on-write. If null, then we're the first sibling in a top-level
1372 // block and inherit from the empty vector.
1374 public readonly UsageVector InheritsFrom;
1379 MyBitVector locals, parameters;
1380 FlowReturns real_returns, real_breaks;
1383 static int next_id = 0;
1387 // Normally, you should not use any of these constructors.
1389 public UsageVector (UsageVector parent, int num_params, int num_locals)
1391 this.InheritsFrom = parent;
1392 this.CountParameters = num_params;
1393 this.CountLocals = num_locals;
1394 this.real_returns = FlowReturns.NEVER;
1395 this.real_breaks = FlowReturns.NEVER;
1397 if (parent != null) {
1398 locals = new MyBitVector (parent.locals, CountLocals);
1400 parameters = new MyBitVector (parent.parameters, num_params);
1401 real_returns = parent.Returns;
1402 real_breaks = parent.Breaks;
1404 locals = new MyBitVector (null, CountLocals);
1406 parameters = new MyBitVector (null, num_params);
1412 public UsageVector (UsageVector parent)
1413 : this (parent, parent.CountParameters, parent.CountLocals)
1417 // This does a deep copy of the usage vector.
1419 public UsageVector Clone ()
1421 UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
1423 retval.locals = locals.Clone ();
1424 if (parameters != null)
1425 retval.parameters = parameters.Clone ();
1426 retval.real_returns = real_returns;
1427 retval.real_breaks = real_breaks;
1433 // State of parameter `number'.
1435 public bool this [int number]
1440 else if (number == 0)
1441 throw new ArgumentException ();
1443 return parameters [number - 1];
1449 else if (number == 0)
1450 throw new ArgumentException ();
1452 parameters [number - 1] = value;
1457 // State of the local variable `vi'.
1458 // If the local variable is a struct, use a non-zero `field_idx'
1459 // to check an individual field in it.
1461 public bool this [VariableInfo vi, int field_idx]
1464 if (vi.Number == -1)
1466 else if (vi.Number == 0)
1467 throw new ArgumentException ();
1469 return locals [vi.Number + field_idx - 1];
1473 if (vi.Number == -1)
1475 else if (vi.Number == 0)
1476 throw new ArgumentException ();
1478 locals [vi.Number + field_idx - 1] = value;
1483 // Specifies when the current block returns.
1484 // If this is FlowReturns.UNREACHABLE, then control can never reach the
1485 // end of the method (so that we don't need to emit a return statement).
1486 // The same applies for FlowReturns.EXCEPTION, but in this case the return
1487 // value will never be used.
1489 public FlowReturns Returns {
1491 return real_returns;
1495 real_returns = value;
1500 // Specifies whether control may return to our containing block
1501 // before reaching the end of this block. This happens if there
1502 // is a break/continue/goto/return in it.
1503 // This can also be used to find out whether the statement immediately
1504 // following the current block may be reached or not.
1506 public FlowReturns Breaks {
1512 real_breaks = value;
1516 public bool AlwaysBreaks {
1518 return (Breaks == FlowReturns.ALWAYS) ||
1519 (Breaks == FlowReturns.EXCEPTION) ||
1520 (Breaks == FlowReturns.UNREACHABLE);
1524 public bool MayBreak {
1526 return Breaks != FlowReturns.NEVER;
1530 public bool AlwaysReturns {
1532 return (Returns == FlowReturns.ALWAYS) ||
1533 (Returns == FlowReturns.EXCEPTION);
1537 public bool MayReturn {
1539 return (Returns == FlowReturns.SOMETIMES) ||
1540 (Returns == FlowReturns.ALWAYS);
1545 // Merge a child branching.
1547 public FlowReturns MergeChildren (FlowBranching branching, ICollection children)
1549 MyBitVector new_locals = null;
1550 MyBitVector new_params = null;
1552 FlowReturns new_returns = FlowReturns.NEVER;
1553 FlowReturns new_breaks = FlowReturns.NEVER;
1554 bool new_returns_set = false, new_breaks_set = false;
1556 Report.Debug (2, "MERGING CHILDREN", branching, branching.Type,
1557 this, children.Count);
1559 foreach (UsageVector child in children) {
1560 Report.Debug (2, " MERGING CHILD", child, child.is_finally);
1562 if (!child.is_finally) {
1563 if (child.Breaks != FlowReturns.UNREACHABLE) {
1564 // If Returns is already set, perform an
1565 // `And' operation on it, otherwise just set just.
1566 if (!new_returns_set) {
1567 new_returns = child.Returns;
1568 new_returns_set = true;
1570 new_returns = AndFlowReturns (
1571 new_returns, child.Returns);
1574 // If Breaks is already set, perform an
1575 // `And' operation on it, otherwise just set just.
1576 if (!new_breaks_set) {
1577 new_breaks = child.Breaks;
1578 new_breaks_set = true;
1580 new_breaks = AndFlowReturns (
1581 new_breaks, child.Breaks);
1584 // Ignore unreachable children.
1585 if (child.Returns == FlowReturns.UNREACHABLE)
1588 // A local variable is initialized after a flow branching if it
1589 // has been initialized in all its branches which do neither
1590 // always return or always throw an exception.
1592 // If a branch may return, but does not always return, then we
1593 // can treat it like a never-returning branch here: control will
1594 // only reach the code position after the branching if we did not
1597 // It's important to distinguish between always and sometimes
1598 // returning branches here:
1601 // 2 if (something) {
1605 // 6 Console.WriteLine (a);
1607 // The if block in lines 3-4 always returns, so we must not look
1608 // at the initialization of `a' in line 4 - thus it'll still be
1609 // uninitialized in line 6.
1611 // On the other hand, the following is allowed:
1618 // 6 Console.WriteLine (a);
1620 // Here, `a' is initialized in line 3 and we must not look at
1621 // line 5 since it always returns.
1623 if (child.is_finally) {
1624 if (new_locals == null)
1625 new_locals = locals.Clone ();
1626 new_locals.Or (child.locals);
1628 if (parameters != null) {
1629 if (new_params == null)
1630 new_params = parameters.Clone ();
1631 new_params.Or (child.parameters);
1635 if (!child.AlwaysReturns && !child.AlwaysBreaks) {
1636 if (new_locals != null)
1637 new_locals.And (child.locals);
1639 new_locals = locals.Clone ();
1640 new_locals.Or (child.locals);
1642 } else if (children.Count == 1) {
1643 new_locals = locals.Clone ();
1644 new_locals.Or (child.locals);
1647 // An `out' parameter must be assigned in all branches which do
1648 // not always throw an exception.
1649 if (parameters != null) {
1650 if (child.Breaks != FlowReturns.EXCEPTION) {
1651 if (new_params != null)
1652 new_params.And (child.parameters);
1654 new_params = parameters.Clone ();
1655 new_params.Or (child.parameters);
1657 } else if (children.Count == 1) {
1658 new_params = parameters.Clone ();
1659 new_params.Or (child.parameters);
1665 Returns = new_returns;
1666 if ((branching.Type == FlowBranchingType.BLOCK) ||
1667 (branching.Type == FlowBranchingType.EXCEPTION) ||
1668 (new_breaks == FlowReturns.UNREACHABLE) ||
1669 (new_breaks == FlowReturns.EXCEPTION))
1670 Breaks = new_breaks;
1671 else if (branching.Type == FlowBranchingType.SWITCH_SECTION)
1672 Breaks = new_returns;
1675 // We've now either reached the point after the branching or we will
1676 // never get there since we always return or always throw an exception.
1678 // If we can reach the point after the branching, mark all locals and
1679 // parameters as initialized which have been initialized in all branches
1680 // we need to look at (see above).
1683 if (((new_breaks != FlowReturns.ALWAYS) &&
1684 (new_breaks != FlowReturns.EXCEPTION) &&
1685 (new_breaks != FlowReturns.UNREACHABLE)) ||
1686 (children.Count == 1)) {
1687 if (new_locals != null)
1688 locals.Or (new_locals);
1690 if (new_params != null)
1691 parameters.Or (new_params);
1694 Report.Debug (2, "MERGING CHILDREN DONE", branching.Type,
1695 new_params, new_locals, new_returns, new_breaks,
1696 branching.Infinite, branching.MayLeaveLoop, this);
1698 if (branching.Type == FlowBranchingType.SWITCH_SECTION) {
1699 if ((new_breaks != FlowReturns.ALWAYS) &&
1700 (new_breaks != FlowReturns.EXCEPTION) &&
1701 (new_breaks != FlowReturns.UNREACHABLE))
1702 Report.Error (163, branching.Location,
1703 "Control cannot fall through from one " +
1704 "case label to another");
1707 if (branching.Infinite && !branching.MayLeaveLoop) {
1708 Report.Debug (1, "INFINITE", new_returns, new_breaks,
1709 Returns, Breaks, this);
1711 // We're actually infinite.
1712 if (new_returns == FlowReturns.NEVER) {
1713 Breaks = FlowReturns.UNREACHABLE;
1714 return FlowReturns.UNREACHABLE;
1717 // If we're an infinite loop and do not break, the code after
1718 // the loop can never be reached. However, if we may return
1719 // from the loop, then we do always return (or stay in the loop
1721 if ((new_returns == FlowReturns.SOMETIMES) ||
1722 (new_returns == FlowReturns.ALWAYS)) {
1723 Returns = FlowReturns.ALWAYS;
1724 return FlowReturns.ALWAYS;
1732 // Tells control flow analysis that the current code position may be reached with
1733 // a forward jump from any of the origins listed in `origin_vectors' which is a
1734 // list of UsageVectors.
1736 // This is used when resolving forward gotos - in the following example, the
1737 // variable `a' is uninitialized in line 8 becase this line may be reached via
1738 // the goto in line 4:
1748 // 8 Console.WriteLine (a);
1751 public void MergeJumpOrigins (ICollection origin_vectors)
1753 Report.Debug (1, "MERGING JUMP ORIGIN", this);
1755 real_breaks = FlowReturns.NEVER;
1756 real_returns = FlowReturns.NEVER;
1758 foreach (UsageVector vector in origin_vectors) {
1759 Report.Debug (1, " MERGING JUMP ORIGIN", vector);
1761 locals.And (vector.locals);
1762 if (parameters != null)
1763 parameters.And (vector.parameters);
1764 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1765 Returns = AndFlowReturns (Returns, vector.Returns);
1768 Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
1772 // This is used at the beginning of a finally block if there were
1773 // any return statements in the try block or one of the catch blocks.
1775 public void MergeFinallyOrigins (ICollection finally_vectors)
1777 Report.Debug (1, "MERGING FINALLY ORIGIN", this);
1779 real_breaks = FlowReturns.NEVER;
1781 foreach (UsageVector vector in finally_vectors) {
1782 Report.Debug (1, " MERGING FINALLY ORIGIN", vector);
1784 if (parameters != null)
1785 parameters.And (vector.parameters);
1786 Breaks = AndFlowReturns (Breaks, vector.Breaks);
1791 Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
1794 public void CheckOutParameters (FlowBranching branching)
1796 if (parameters != null)
1797 branching.CheckOutParameters (parameters, branching.Location);
1801 // Performs an `or' operation on the locals and the parameters.
1803 public void Or (UsageVector new_vector)
1805 locals.Or (new_vector.locals);
1806 if (parameters != null)
1807 parameters.Or (new_vector.parameters);
1811 // Performs an `and' operation on the locals.
1813 public void AndLocals (UsageVector new_vector)
1815 locals.And (new_vector.locals);
1819 // Returns a deep copy of the parameters.
1821 public MyBitVector Parameters {
1823 if (parameters != null)
1824 return parameters.Clone ();
1831 // Returns a deep copy of the locals.
1833 public MyBitVector Locals {
1835 return locals.Clone ();
1843 public override string ToString ()
1845 StringBuilder sb = new StringBuilder ();
1847 sb.Append ("Vector (");
1850 sb.Append (Returns);
1853 if (parameters != null) {
1855 sb.Append (parameters);
1861 return sb.ToString ();
1865 FlowBranching (FlowBranchingType type, Location loc)
1867 this.Siblings = new ArrayList ();
1869 this.Location = loc;
1875 // Creates a new flow branching for `block'.
1876 // This is used from Block.Resolve to create the top-level branching of
1879 public FlowBranching (Block block, InternalParameters ip, Location loc)
1880 : this (FlowBranchingType.BLOCK, loc)
1885 int count = (ip != null) ? ip.Count : 0;
1888 param_map = new int [count];
1889 struct_params = new MyStructInfo [count];
1892 for (int i = 0; i < count; i++) {
1893 Parameter.Modifier mod = param_info.ParameterModifier (i);
1895 if ((mod & Parameter.Modifier.OUT) == 0)
1898 param_map [i] = ++num_params;
1900 Type param_type = param_info.ParameterType (i);
1902 struct_params [i] = MyStructInfo.GetStructInfo (param_type);
1903 if (struct_params [i] != null)
1904 num_params += struct_params [i].Count;
1907 Siblings = new ArrayList ();
1908 Siblings.Add (new UsageVector (null, num_params, block.CountVariables));
1912 // Creates a new flow branching which is contained in `parent'.
1913 // You should only pass non-null for the `block' argument if this block
1914 // introduces any new variables - in this case, we need to create a new
1915 // usage vector with a different size than our parent's one.
1917 public FlowBranching (FlowBranching parent, FlowBranchingType type,
1918 Block block, Location loc)
1924 if (parent != null) {
1925 param_info = parent.param_info;
1926 param_map = parent.param_map;
1927 struct_params = parent.struct_params;
1928 num_params = parent.num_params;
1933 vector = new UsageVector (parent.CurrentUsageVector, num_params,
1934 Block.CountVariables);
1936 vector = new UsageVector (Parent.CurrentUsageVector);
1938 Siblings.Add (vector);
1941 case FlowBranchingType.EXCEPTION:
1942 finally_vectors = new ArrayList ();
1951 // Returns the branching's current usage vector.
1953 public UsageVector CurrentUsageVector
1956 return (UsageVector) Siblings [Siblings.Count - 1];
1961 // Creates a sibling of the current usage vector.
1963 public void CreateSibling ()
1965 Siblings.Add (new UsageVector (Parent.CurrentUsageVector));
1967 Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
1971 // Creates a sibling for a `finally' block.
1973 public void CreateSiblingForFinally ()
1975 if (Type != FlowBranchingType.EXCEPTION)
1976 throw new NotSupportedException ();
1980 CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
1984 // Check whether all `out' parameters have been assigned.
1986 public void CheckOutParameters (MyBitVector parameters, Location loc)
1991 for (int i = 0; i < param_map.Length; i++) {
1992 int index = param_map [i];
1997 if (parameters [index - 1])
2000 // If it's a struct, we must ensure that all its fields have
2001 // been assigned. If the struct has any non-public fields, this
2002 // can only be done by assigning the whole struct.
2004 MyStructInfo struct_info = struct_params [index - 1];
2005 if ((struct_info == null) || struct_info.HasNonPublicFields) {
2007 177, loc, "The out parameter `" +
2008 param_info.ParameterName (i) + "' must be " +
2009 "assigned before control leave the current method.");
2015 for (int j = 0; j < struct_info.Count; j++) {
2016 if (!parameters [index + j]) {
2018 177, loc, "The out parameter `" +
2019 param_info.ParameterName (i) + "' must be " +
2020 "assigned before control leave the current method.");
2029 // Merge a child branching.
2031 public FlowReturns MergeChild (FlowBranching child)
2033 FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings);
2035 if (child.Type != FlowBranchingType.LOOP_BLOCK)
2036 MayLeaveLoop |= child.MayLeaveLoop;
2038 MayLeaveLoop = false;
2044 // Does the toplevel merging.
2046 public FlowReturns MergeTopBlock ()
2048 if ((Type != FlowBranchingType.BLOCK) || (Block == null))
2049 throw new NotSupportedException ();
2051 UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
2053 Report.Debug (1, "MERGING TOP BLOCK", Location, vector);
2055 vector.MergeChildren (this, Siblings);
2058 Siblings.Add (vector);
2060 Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector);
2062 if (vector.Breaks != FlowReturns.EXCEPTION) {
2063 if (!vector.AlwaysBreaks)
2064 CheckOutParameters (CurrentUsageVector.Parameters, Location);
2065 return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns;
2067 return FlowReturns.EXCEPTION;
2070 public bool InTryBlock ()
2072 if (finally_vectors != null)
2074 else if (Parent != null)
2075 return Parent.InTryBlock ();
2080 public void AddFinallyVector (UsageVector vector)
2082 if (finally_vectors != null) {
2083 finally_vectors.Add (vector.Clone ());
2088 Parent.AddFinallyVector (vector);
2090 throw new NotSupportedException ();
2093 public bool IsVariableAssigned (VariableInfo vi)
2095 if (CurrentUsageVector.AlwaysBreaks)
2098 return CurrentUsageVector [vi, 0];
2101 public bool IsVariableAssigned (VariableInfo vi, int field_idx)
2103 if (CurrentUsageVector.AlwaysBreaks)
2106 return CurrentUsageVector [vi, field_idx];
2109 public void SetVariableAssigned (VariableInfo vi)
2111 if (CurrentUsageVector.AlwaysBreaks)
2114 CurrentUsageVector [vi, 0] = true;
2117 public void SetVariableAssigned (VariableInfo vi, int field_idx)
2119 if (CurrentUsageVector.AlwaysBreaks)
2122 CurrentUsageVector [vi, field_idx] = true;
2125 public bool IsParameterAssigned (int number)
2127 int index = param_map [number];
2132 if (CurrentUsageVector [index])
2135 // Parameter is not assigned, so check whether it's a struct.
2136 // If it's either not a struct or a struct which non-public
2137 // fields, return false.
2138 MyStructInfo struct_info = struct_params [number];
2139 if ((struct_info == null) || struct_info.HasNonPublicFields)
2142 // Ok, so each field must be assigned.
2143 for (int i = 0; i < struct_info.Count; i++)
2144 if (!CurrentUsageVector [index + i])
2150 public bool IsParameterAssigned (int number, string field_name)
2152 int index = param_map [number];
2157 MyStructInfo info = (MyStructInfo) struct_params [number];
2161 int field_idx = info [field_name];
2163 return CurrentUsageVector [index + field_idx];
2166 public void SetParameterAssigned (int number)
2168 if (param_map [number] == 0)
2171 if (!CurrentUsageVector.AlwaysBreaks)
2172 CurrentUsageVector [param_map [number]] = true;
2175 public void SetParameterAssigned (int number, string field_name)
2177 int index = param_map [number];
2182 MyStructInfo info = (MyStructInfo) struct_params [number];
2186 int field_idx = info [field_name];
2188 if (!CurrentUsageVector.AlwaysBreaks)
2189 CurrentUsageVector [index + field_idx] = true;
2192 public bool IsReachable ()
2197 case FlowBranchingType.SWITCH_SECTION:
2198 // The code following a switch block is reachable unless the switch
2199 // block always returns.
2200 reachable = !CurrentUsageVector.AlwaysReturns;
2203 case FlowBranchingType.LOOP_BLOCK:
2204 // The code following a loop is reachable unless the loop always
2205 // returns or it's an infinite loop without any `break's in it.
2206 reachable = !CurrentUsageVector.AlwaysReturns &&
2207 (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE);
2211 // The code following a block or exception is reachable unless the
2212 // block either always returns or always breaks.
2213 reachable = !CurrentUsageVector.AlwaysBreaks &&
2214 !CurrentUsageVector.AlwaysReturns;
2218 Report.Debug (1, "REACHABLE", Type, CurrentUsageVector.Returns,
2219 CurrentUsageVector.Breaks, CurrentUsageVector, reachable);
2224 public override string ToString ()
2226 StringBuilder sb = new StringBuilder ("FlowBranching (");
2231 if (Block != null) {
2233 sb.Append (Block.ID);
2235 sb.Append (Block.StartLocation);
2238 sb.Append (Siblings.Count);
2240 sb.Append (CurrentUsageVector);
2242 return sb.ToString ();
2246 public class MyStructInfo {
2247 public readonly Type Type;
2248 public readonly FieldInfo[] Fields;
2249 public readonly FieldInfo[] NonPublicFields;
2250 public readonly int Count;
2251 public readonly int CountNonPublic;
2252 public readonly bool HasNonPublicFields;
2254 private static Hashtable field_type_hash = new Hashtable ();
2255 private Hashtable field_hash;
2257 // Private constructor. To save memory usage, we only need to create one instance
2258 // of this class per struct type.
2259 private MyStructInfo (Type type)
2263 if (type is TypeBuilder) {
2264 TypeContainer tc = TypeManager.LookupTypeContainer (type);
2266 ArrayList fields = tc.Fields;
2267 if (fields != null) {
2268 foreach (Field field in fields) {
2269 if ((field.ModFlags & Modifiers.STATIC) != 0)
2271 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2278 Fields = new FieldInfo [Count];
2279 NonPublicFields = new FieldInfo [CountNonPublic];
2281 Count = CountNonPublic = 0;
2282 if (fields != null) {
2283 foreach (Field field in fields) {
2284 if ((field.ModFlags & Modifiers.STATIC) != 0)
2286 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2287 Fields [Count++] = field.FieldBuilder;
2289 NonPublicFields [CountNonPublic++] =
2295 Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public);
2296 Count = Fields.Length;
2298 NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic);
2299 CountNonPublic = NonPublicFields.Length;
2302 Count += NonPublicFields.Length;
2305 field_hash = new Hashtable ();
2306 foreach (FieldInfo field in Fields)
2307 field_hash.Add (field.Name, ++number);
2309 if (NonPublicFields.Length != 0)
2310 HasNonPublicFields = true;
2312 foreach (FieldInfo field in NonPublicFields)
2313 field_hash.Add (field.Name, ++number);
2316 public int this [string name] {
2318 if (field_hash.Contains (name))
2319 return (int) field_hash [name];
2325 public FieldInfo this [int index] {
2327 if (index >= Fields.Length)
2328 return NonPublicFields [index - Fields.Length];
2330 return Fields [index];
2334 public static MyStructInfo GetStructInfo (Type type)
2336 if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type))
2339 if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type))
2342 MyStructInfo info = (MyStructInfo) field_type_hash [type];
2346 info = new MyStructInfo (type);
2347 field_type_hash.Add (type, info);
2351 public static MyStructInfo GetStructInfo (TypeContainer tc)
2353 MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder];
2357 info = new MyStructInfo (tc.TypeBuilder);
2358 field_type_hash.Add (tc.TypeBuilder, info);
2363 public class VariableInfo : IVariable {
2364 public Expression Type;
2365 public LocalBuilder LocalBuilder;
2366 public Type VariableType;
2367 public readonly string Name;
2368 public readonly Location Location;
2369 public readonly int Block;
2374 public bool Assigned;
2375 public bool ReadOnly;
2377 public VariableInfo (Expression type, string name, int block, Location l)
2382 LocalBuilder = null;
2386 public VariableInfo (TypeContainer tc, int block, Location l)
2388 VariableType = tc.TypeBuilder;
2389 struct_info = MyStructInfo.GetStructInfo (tc);
2391 LocalBuilder = null;
2395 MyStructInfo struct_info;
2396 public MyStructInfo StructInfo {
2402 public bool IsAssigned (EmitContext ec, Location loc)
2404 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
2407 MyStructInfo struct_info = StructInfo;
2408 if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
2409 Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
2410 ec.CurrentBranching.SetVariableAssigned (this);
2414 int count = struct_info.Count;
2416 for (int i = 0; i < count; i++) {
2417 if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
2419 Report.Error (165, loc,
2420 "Use of unassigned local variable `" +
2422 ec.CurrentBranching.SetVariableAssigned (this);
2426 FieldInfo field = struct_info [i];
2427 Report.Error (171, loc,
2428 "Field `" + TypeManager.CSharpName (VariableType) +
2429 "." + field.Name + "' must be fully initialized " +
2430 "before control leaves the constructor");
2438 public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
2440 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) ||
2441 (struct_info == null))
2444 int field_idx = StructInfo [name];
2448 if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) {
2449 Report.Error (170, loc,
2450 "Use of possibly unassigned field `" + name + "'");
2451 ec.CurrentBranching.SetVariableAssigned (this, field_idx);
2458 public void SetAssigned (EmitContext ec)
2460 if (ec.DoFlowAnalysis)
2461 ec.CurrentBranching.SetVariableAssigned (this);
2464 public void SetFieldAssigned (EmitContext ec, string name)
2466 if (ec.DoFlowAnalysis && (struct_info != null))
2467 ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]);
2470 public bool Resolve (DeclSpace decl)
2472 if (struct_info != null)
2475 if (VariableType == null)
2476 VariableType = decl.ResolveType (Type, false, Location);
2478 if (VariableType == null)
2481 struct_info = MyStructInfo.GetStructInfo (VariableType);
2486 public void MakePinned ()
2488 TypeManager.MakePinned (LocalBuilder);
2491 public override string ToString ()
2493 return "VariableInfo (" + Number + "," + Type + "," + Location + ")";
2498 /// Block represents a C# block.
2502 /// This class is used in a number of places: either to represent
2503 /// explicit blocks that the programmer places or implicit blocks.
2505 /// Implicit blocks are used as labels or to introduce variable
2508 public class Block : Statement {
2509 public readonly Block Parent;
2510 public readonly bool Implicit;
2511 public readonly Location StartLocation;
2512 public Location EndLocation;
2515 // The statements in this block
2517 ArrayList statements;
2520 // An array of Blocks. We keep track of children just
2521 // to generate the local variable declarations.
2523 // Statements and child statements are handled through the
2529 // Labels. (label, block) pairs.
2534 // Keeps track of (name, type) pairs
2536 Hashtable variables;
2539 // Keeps track of constants
2540 Hashtable constants;
2543 // Maps variable names to ILGenerator.LocalBuilders
2545 Hashtable local_builders;
2553 public Block (Block parent)
2554 : this (parent, false, Location.Null, Location.Null)
2557 public Block (Block parent, bool implicit_block)
2558 : this (parent, implicit_block, Location.Null, Location.Null)
2561 public Block (Block parent, bool implicit_block, Parameters parameters)
2562 : this (parent, implicit_block, parameters, Location.Null, Location.Null)
2565 public Block (Block parent, Location start, Location end)
2566 : this (parent, false, start, end)
2569 public Block (Block parent, Parameters parameters, Location start, Location end)
2570 : this (parent, false, parameters, start, end)
2573 public Block (Block parent, bool implicit_block, Location start, Location end)
2574 : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
2578 public Block (Block parent, bool implicit_block, Parameters parameters,
2579 Location start, Location end)
2582 parent.AddChild (this);
2584 this.Parent = parent;
2585 this.Implicit = implicit_block;
2586 this.parameters = parameters;
2587 this.StartLocation = start;
2588 this.EndLocation = end;
2591 statements = new ArrayList ();
2600 void AddChild (Block b)
2602 if (children == null)
2603 children = new ArrayList ();
2608 public void SetEndLocation (Location loc)
2614 /// Adds a label to the current block.
2618 /// false if the name already exists in this block. true
2622 public bool AddLabel (string name, LabeledStatement target)
2625 labels = new Hashtable ();
2626 if (labels.Contains (name))
2629 labels.Add (name, target);
2633 public LabeledStatement LookupLabel (string name)
2635 if (labels != null){
2636 if (labels.Contains (name))
2637 return ((LabeledStatement) labels [name]);
2641 return Parent.LookupLabel (name);
2646 VariableInfo this_variable = null;
2649 // Returns the "this" instance variable of this block.
2650 // See AddThisVariable() for more information.
2652 public VariableInfo ThisVariable {
2654 if (this_variable != null)
2655 return this_variable;
2656 else if (Parent != null)
2657 return Parent.ThisVariable;
2663 Hashtable child_variable_names;
2666 // Marks a variable with name @name as being used in a child block.
2667 // If a variable name has been used in a child block, it's illegal to
2668 // declare a variable with the same name in the current block.
2670 public void AddChildVariableName (string name)
2672 if (child_variable_names == null)
2673 child_variable_names = new Hashtable ();
2675 if (!child_variable_names.Contains (name))
2676 child_variable_names.Add (name, true);
2680 // Marks all variables from block @block and all its children as being
2681 // used in a child block.
2683 public void AddChildVariableNames (Block block)
2685 if (block.Variables != null) {
2686 foreach (string name in block.Variables.Keys)
2687 AddChildVariableName (name);
2690 foreach (Block child in block.children) {
2691 if (child.Variables != null) {
2692 foreach (string name in child.Variables.Keys)
2693 AddChildVariableName (name);
2699 // Checks whether a variable name has already been used in a child block.
2701 public bool IsVariableNameUsedInChildBlock (string name)
2703 if (child_variable_names == null)
2706 return child_variable_names.Contains (name);
2710 // This is used by non-static `struct' constructors which do not have an
2711 // initializer - in this case, the constructor must initialize all of the
2712 // struct's fields. To do this, we add a "this" variable and use the flow
2713 // analysis code to ensure that it's been fully initialized before control
2714 // leaves the constructor.
2716 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
2718 if (this_variable != null)
2719 return this_variable;
2721 this_variable = new VariableInfo (tc, ID, l);
2723 if (variables == null)
2724 variables = new Hashtable ();
2725 variables.Add ("this", this_variable);
2727 return this_variable;
2730 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
2732 if (variables == null)
2733 variables = new Hashtable ();
2735 VariableInfo vi = GetVariableInfo (name);
2738 Report.Error (136, l, "A local variable named `" + name + "' " +
2739 "cannot be declared in this scope since it would " +
2740 "give a different meaning to `" + name + "', which " +
2741 "is already used in a `parent or current' scope to " +
2742 "denote something else");
2744 Report.Error (128, l, "A local variable `" + name + "' is already " +
2745 "defined in this scope");
2749 if (IsVariableNameUsedInChildBlock (name)) {
2750 Report.Error (136, l, "A local variable named `" + name + "' " +
2751 "cannot be declared in this scope since it would " +
2752 "give a different meaning to `" + name + "', which " +
2753 "is already used in a `child' scope to denote something " +
2760 Parameter p = pars.GetParameterByName (name, out idx);
2762 Report.Error (136, l, "A local variable named `" + name + "' " +
2763 "cannot be declared in this scope since it would " +
2764 "give a different meaning to `" + name + "', which " +
2765 "is already used in a `parent or current' scope to " +
2766 "denote something else");
2771 vi = new VariableInfo (type, name, ID, l);
2773 variables.Add (name, vi);
2775 if (variables_initialized)
2776 throw new Exception ();
2778 // Console.WriteLine ("Adding {0} to {1}", name, ID);
2782 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
2784 if (AddVariable (type, name, pars, l) == null)
2787 if (constants == null)
2788 constants = new Hashtable ();
2790 constants.Add (name, value);
2794 public Hashtable Variables {
2800 public VariableInfo GetVariableInfo (string name)
2802 if (variables != null) {
2804 temp = variables [name];
2807 return (VariableInfo) temp;
2812 return Parent.GetVariableInfo (name);
2817 public Expression GetVariableType (string name)
2819 VariableInfo vi = GetVariableInfo (name);
2827 public Expression GetConstantExpression (string name)
2829 if (constants != null) {
2831 temp = constants [name];
2834 return (Expression) temp;
2838 return Parent.GetConstantExpression (name);
2844 /// True if the variable named @name has been defined
2847 public bool IsVariableDefined (string name)
2849 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
2850 if (variables != null) {
2851 if (variables.Contains (name))
2856 return Parent.IsVariableDefined (name);
2862 /// True if the variable named @name is a constant
2864 public bool IsConstant (string name)
2866 Expression e = null;
2868 e = GetConstantExpression (name);
2874 /// Use to fetch the statement associated with this label
2876 public Statement this [string name] {
2878 return (Statement) labels [name];
2882 Parameters parameters = null;
2883 public Parameters Parameters {
2886 return Parent.Parameters;
2893 /// A list of labels that were not used within this block
2895 public string [] GetUnreferenced ()
2897 // FIXME: Implement me
2901 public void AddStatement (Statement s)
2918 bool variables_initialized = false;
2919 int count_variables = 0, first_variable = 0;
2921 void UpdateVariableInfo (EmitContext ec)
2923 DeclSpace ds = ec.DeclSpace;
2928 first_variable += Parent.CountVariables;
2930 count_variables = first_variable;
2931 if (variables != null) {
2932 foreach (VariableInfo vi in variables.Values) {
2933 if (!vi.Resolve (ds)) {
2938 vi.Number = ++count_variables;
2940 if (vi.StructInfo != null)
2941 count_variables += vi.StructInfo.Count;
2945 variables_initialized = true;
2950 // The number of local variables in this block
2952 public int CountVariables
2955 if (!variables_initialized)
2956 throw new Exception ();
2958 return count_variables;
2963 /// Emits the variable declarations and labels.
2966 /// tc: is our typecontainer (to resolve type references)
2967 /// ig: is the code generator:
2968 /// toplevel: the toplevel block. This is used for checking
2969 /// that no two labels with the same name are used.
2971 public void EmitMeta (EmitContext ec, Block toplevel)
2973 DeclSpace ds = ec.DeclSpace;
2974 ILGenerator ig = ec.ig;
2976 if (!variables_initialized)
2977 UpdateVariableInfo (ec);
2980 // Process this block variables
2982 if (variables != null){
2983 local_builders = new Hashtable ();
2985 foreach (DictionaryEntry de in variables){
2986 string name = (string) de.Key;
2987 VariableInfo vi = (VariableInfo) de.Value;
2989 if (vi.VariableType == null)
2992 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
2994 if (CodeGen.SymbolWriter != null)
2995 vi.LocalBuilder.SetLocalSymInfo (name);
2997 if (constants == null)
3000 Expression cv = (Expression) constants [name];
3004 Expression e = cv.Resolve (ec);
3008 if (!(e is Constant)){
3009 Report.Error (133, vi.Location,
3010 "The expression being assigned to `" +
3011 name + "' must be constant (" + e + ")");
3015 constants.Remove (name);
3016 constants.Add (name, e);
3021 // Now, handle the children
3023 if (children != null){
3024 foreach (Block b in children)
3025 b.EmitMeta (ec, toplevel);
3029 public void UsageWarning ()
3033 if (variables != null){
3034 foreach (DictionaryEntry de in variables){
3035 VariableInfo vi = (VariableInfo) de.Value;
3040 name = (string) de.Key;
3044 219, vi.Location, "The variable `" + name +
3045 "' is assigned but its value is never used");
3048 168, vi.Location, "The variable `" +
3050 "' is declared but never used");
3055 if (children != null)
3056 foreach (Block b in children)
3060 bool has_ret = false;
3062 public override bool Resolve (EmitContext ec)
3064 Block prev_block = ec.CurrentBlock;
3067 ec.CurrentBlock = this;
3068 ec.StartFlowBranching (this);
3070 Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
3072 if (!variables_initialized)
3073 UpdateVariableInfo (ec);
3075 ArrayList new_statements = new ArrayList ();
3076 bool unreachable = false, warning_shown = false;
3078 foreach (Statement s in statements){
3079 if (unreachable && !(s is LabeledStatement)) {
3080 if (!warning_shown && !(s is EmptyStatement)) {
3081 warning_shown = true;
3082 Warning_DeadCodeFound (s.loc);
3088 if (s.Resolve (ec) == false) {
3093 if (s is LabeledStatement)
3094 unreachable = false;
3096 unreachable = ! ec.CurrentBranching.IsReachable ();
3098 new_statements.Add (s);
3101 statements = new_statements;
3103 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
3105 FlowReturns returns = ec.EndFlowBranching ();
3106 ec.CurrentBlock = prev_block;
3108 // If we're a non-static `struct' constructor which doesn't have an
3109 // initializer, then we must initialize all of the struct's fields.
3110 if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
3111 !this_variable.IsAssigned (ec, loc))
3114 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
3115 foreach (LabeledStatement label in labels.Values)
3116 if (!label.HasBeenReferenced)
3117 Report.Warning (164, label.Location,
3118 "This label has not been referenced");
3121 if ((returns == FlowReturns.ALWAYS) ||
3122 (returns == FlowReturns.EXCEPTION) ||
3123 (returns == FlowReturns.UNREACHABLE))
3129 protected override bool DoEmit (EmitContext ec)
3131 Block prev_block = ec.CurrentBlock;
3133 ec.CurrentBlock = this;
3135 ec.Mark (StartLocation);
3136 foreach (Statement s in statements)
3138 ec.Mark (EndLocation);
3140 ec.CurrentBlock = prev_block;
3145 public class SwitchLabel {
3148 public Location loc;
3149 public Label ILLabel;
3150 public Label ILLabelCode;
3153 // if expr == null, then it is the default case.
3155 public SwitchLabel (Expression expr, Location l)
3161 public Expression Label {
3167 public object Converted {
3174 // Resolves the expression, reduces it to a literal if possible
3175 // and then converts it to the requested type.
3177 public bool ResolveAndReduce (EmitContext ec, Type required_type)
3179 ILLabel = ec.ig.DefineLabel ();
3180 ILLabelCode = ec.ig.DefineLabel ();
3185 Expression e = label.Resolve (ec);
3190 if (!(e is Constant)){
3191 Console.WriteLine ("Value is: " + label);
3192 Report.Error (150, loc, "A constant value is expected");
3196 if (e is StringConstant || e is NullLiteral){
3197 if (required_type == TypeManager.string_type){
3199 ILLabel = ec.ig.DefineLabel ();
3204 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
3205 if (converted == null)
3212 public class SwitchSection {
3213 // An array of SwitchLabels.
3214 public readonly ArrayList Labels;
3215 public readonly Block Block;
3217 public SwitchSection (ArrayList labels, Block block)
3224 public class Switch : Statement {
3225 public readonly ArrayList Sections;
3226 public Expression Expr;
3229 /// Maps constants whose type type SwitchType to their SwitchLabels.
3231 public Hashtable Elements;
3234 /// The governing switch type
3236 public Type SwitchType;
3242 Label default_target;
3243 Expression new_expr;
3246 // The types allowed to be implicitly cast from
3247 // on the governing type
3249 static Type [] allowed_types;
3251 public Switch (Expression e, ArrayList sects, Location l)
3258 public bool GotDefault {
3264 public Label DefaultTarget {
3266 return default_target;
3271 // Determines the governing type for a switch. The returned
3272 // expression might be the expression from the switch, or an
3273 // expression that includes any potential conversions to the
3274 // integral types or to string.
3276 Expression SwitchGoverningType (EmitContext ec, Type t)
3278 if (t == TypeManager.int32_type ||
3279 t == TypeManager.uint32_type ||
3280 t == TypeManager.char_type ||
3281 t == TypeManager.byte_type ||
3282 t == TypeManager.sbyte_type ||
3283 t == TypeManager.ushort_type ||
3284 t == TypeManager.short_type ||
3285 t == TypeManager.uint64_type ||
3286 t == TypeManager.int64_type ||
3287 t == TypeManager.string_type ||
3288 t == TypeManager.bool_type ||
3289 t.IsSubclassOf (TypeManager.enum_type))
3292 if (allowed_types == null){
3293 allowed_types = new Type [] {
3294 TypeManager.sbyte_type,
3295 TypeManager.byte_type,
3296 TypeManager.short_type,
3297 TypeManager.ushort_type,
3298 TypeManager.int32_type,
3299 TypeManager.uint32_type,
3300 TypeManager.int64_type,
3301 TypeManager.uint64_type,
3302 TypeManager.char_type,
3303 TypeManager.bool_type,
3304 TypeManager.string_type
3309 // Try to find a *user* defined implicit conversion.
3311 // If there is no implicit conversion, or if there are multiple
3312 // conversions, we have to report an error
3314 Expression converted = null;
3315 foreach (Type tt in allowed_types){
3318 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
3322 if (converted != null){
3323 Report.Error (-12, loc, "More than one conversion to an integral " +
3324 " type exists for type `" +
3325 TypeManager.CSharpName (Expr.Type)+"'");
3333 void error152 (string n)
3336 152, "The label `" + n + ":' " +
3337 "is already present on this switch statement");
3341 // Performs the basic sanity checks on the switch statement
3342 // (looks for duplicate keys and non-constant expressions).
3344 // It also returns a hashtable with the keys that we will later
3345 // use to compute the switch tables
3347 bool CheckSwitch (EmitContext ec)
3351 Elements = new Hashtable ();
3353 got_default = false;
3355 if (TypeManager.IsEnumType (SwitchType)){
3356 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3358 compare_type = SwitchType;
3360 foreach (SwitchSection ss in Sections){
3361 foreach (SwitchLabel sl in ss.Labels){
3362 if (!sl.ResolveAndReduce (ec, SwitchType)){
3367 if (sl.Label == null){
3369 error152 ("default");
3376 object key = sl.Converted;
3378 if (key is Constant)
3379 key = ((Constant) key).GetValue ();
3382 key = NullLiteral.Null;
3384 string lname = null;
3385 if (compare_type == TypeManager.uint64_type){
3386 ulong v = (ulong) key;
3388 if (Elements.Contains (v))
3389 lname = v.ToString ();
3391 Elements.Add (v, sl);
3392 } else if (compare_type == TypeManager.int64_type){
3393 long v = (long) key;
3395 if (Elements.Contains (v))
3396 lname = v.ToString ();
3398 Elements.Add (v, sl);
3399 } else if (compare_type == TypeManager.uint32_type){
3400 uint v = (uint) key;
3402 if (Elements.Contains (v))
3403 lname = v.ToString ();
3405 Elements.Add (v, sl);
3406 } else if (compare_type == TypeManager.char_type){
3407 char v = (char) key;
3409 if (Elements.Contains (v))
3410 lname = v.ToString ();
3412 Elements.Add (v, sl);
3413 } else if (compare_type == TypeManager.byte_type){
3414 byte v = (byte) key;
3416 if (Elements.Contains (v))
3417 lname = v.ToString ();
3419 Elements.Add (v, sl);
3420 } else if (compare_type == TypeManager.sbyte_type){
3421 sbyte v = (sbyte) key;
3423 if (Elements.Contains (v))
3424 lname = v.ToString ();
3426 Elements.Add (v, sl);
3427 } else if (compare_type == TypeManager.short_type){
3428 short v = (short) key;
3430 if (Elements.Contains (v))
3431 lname = v.ToString ();
3433 Elements.Add (v, sl);
3434 } else if (compare_type == TypeManager.ushort_type){
3435 ushort v = (ushort) key;
3437 if (Elements.Contains (v))
3438 lname = v.ToString ();
3440 Elements.Add (v, sl);
3441 } else if (compare_type == TypeManager.string_type){
3442 if (key is NullLiteral){
3443 if (Elements.Contains (NullLiteral.Null))
3446 Elements.Add (NullLiteral.Null, null);
3448 string s = (string) key;
3450 if (Elements.Contains (s))
3453 Elements.Add (s, sl);
3455 } else if (compare_type == TypeManager.int32_type) {
3458 if (Elements.Contains (v))
3459 lname = v.ToString ();
3461 Elements.Add (v, sl);
3462 } else if (compare_type == TypeManager.bool_type) {
3463 bool v = (bool) key;
3465 if (Elements.Contains (v))
3466 lname = v.ToString ();
3468 Elements.Add (v, sl);
3472 throw new Exception ("Unknown switch type!" +
3473 SwitchType + " " + compare_type);
3477 error152 ("case + " + lname);
3488 void EmitObjectInteger (ILGenerator ig, object k)
3491 IntConstant.EmitInt (ig, (int) k);
3492 else if (k is Constant) {
3493 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3496 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3499 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3501 IntConstant.EmitInt (ig, (int) (long) k);
3502 ig.Emit (OpCodes.Conv_I8);
3505 LongConstant.EmitLong (ig, (long) k);
3507 else if (k is ulong)
3509 if ((ulong) k < (1L<<32))
3511 IntConstant.EmitInt (ig, (int) (long) k);
3512 ig.Emit (OpCodes.Conv_U8);
3516 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
3520 IntConstant.EmitInt (ig, (int) ((char) k));
3521 else if (k is sbyte)
3522 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3524 IntConstant.EmitInt (ig, (int) ((byte) k));
3525 else if (k is short)
3526 IntConstant.EmitInt (ig, (int) ((short) k));
3527 else if (k is ushort)
3528 IntConstant.EmitInt (ig, (int) ((ushort) k));
3530 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3532 throw new Exception ("Unhandled case");
3535 // structure used to hold blocks of keys while calculating table switch
3536 class KeyBlock : IComparable
3538 public KeyBlock (long _nFirst)
3540 nFirst = nLast = _nFirst;
3544 public ArrayList rgKeys = null;
3547 get { return (int) (nLast - nFirst + 1); }
3549 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3551 return kbLast.nLast - kbFirst.nFirst + 1;
3553 public int CompareTo (object obj)
3555 KeyBlock kb = (KeyBlock) obj;
3556 int nLength = Length;
3557 int nLengthOther = kb.Length;
3558 if (nLengthOther == nLength)
3559 return (int) (kb.nFirst - nFirst);
3560 return nLength - nLengthOther;
3565 /// This method emits code for a lookup-based switch statement (non-string)
3566 /// Basically it groups the cases into blocks that are at least half full,
3567 /// and then spits out individual lookup opcodes for each block.
3568 /// It emits the longest blocks first, and short blocks are just
3569 /// handled with direct compares.
3571 /// <param name="ec"></param>
3572 /// <param name="val"></param>
3573 /// <returns></returns>
3574 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
3576 int cElements = Elements.Count;
3577 object [] rgKeys = new object [cElements];
3578 Elements.Keys.CopyTo (rgKeys, 0);
3579 Array.Sort (rgKeys);
3581 // initialize the block list with one element per key
3582 ArrayList rgKeyBlocks = new ArrayList ();
3583 foreach (object key in rgKeys)
3584 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
3587 // iteratively merge the blocks while they are at least half full
3588 // there's probably a really cool way to do this with a tree...
3589 while (rgKeyBlocks.Count > 1)
3591 ArrayList rgKeyBlocksNew = new ArrayList ();
3592 kbCurr = (KeyBlock) rgKeyBlocks [0];
3593 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3595 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3596 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
3599 kbCurr.nLast = kb.nLast;
3603 // start a new block
3604 rgKeyBlocksNew.Add (kbCurr);
3608 rgKeyBlocksNew.Add (kbCurr);
3609 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3611 rgKeyBlocks = rgKeyBlocksNew;
3614 // initialize the key lists
3615 foreach (KeyBlock kb in rgKeyBlocks)
3616 kb.rgKeys = new ArrayList ();
3618 // fill the key lists
3620 if (rgKeyBlocks.Count > 0) {
3621 kbCurr = (KeyBlock) rgKeyBlocks [0];
3622 foreach (object key in rgKeys)
3624 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
3626 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3627 kbCurr.rgKeys.Add (key);
3631 // sort the blocks so we can tackle the largest ones first
3632 rgKeyBlocks.Sort ();
3634 // okay now we can start...
3635 ILGenerator ig = ec.ig;
3636 Label lblEnd = ig.DefineLabel (); // at the end ;-)
3637 Label lblDefault = ig.DefineLabel ();
3639 Type typeKeys = null;
3640 if (rgKeys.Length > 0)
3641 typeKeys = rgKeys [0].GetType (); // used for conversions
3643 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3645 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3646 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3649 foreach (object key in kb.rgKeys)
3651 ig.Emit (OpCodes.Ldloc, val);
3652 EmitObjectInteger (ig, key);
3653 SwitchLabel sl = (SwitchLabel) Elements [key];
3654 ig.Emit (OpCodes.Beq, sl.ILLabel);
3659 // TODO: if all the keys in the block are the same and there are
3660 // no gaps/defaults then just use a range-check.
3661 if (SwitchType == TypeManager.int64_type ||
3662 SwitchType == TypeManager.uint64_type)
3664 // TODO: optimize constant/I4 cases
3666 // check block range (could be > 2^31)
3667 ig.Emit (OpCodes.Ldloc, val);
3668 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3669 ig.Emit (OpCodes.Blt, lblDefault);
3670 ig.Emit (OpCodes.Ldloc, val);
3671 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3672 ig.Emit (OpCodes.Bgt, lblDefault);
3675 ig.Emit (OpCodes.Ldloc, val);
3678 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3679 ig.Emit (OpCodes.Sub);
3681 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3686 ig.Emit (OpCodes.Ldloc, val);
3687 int nFirst = (int) kb.nFirst;
3690 IntConstant.EmitInt (ig, nFirst);
3691 ig.Emit (OpCodes.Sub);
3693 else if (nFirst < 0)
3695 IntConstant.EmitInt (ig, -nFirst);
3696 ig.Emit (OpCodes.Add);
3700 // first, build the list of labels for the switch
3702 int cJumps = kb.Length;
3703 Label [] rgLabels = new Label [cJumps];
3704 for (int iJump = 0; iJump < cJumps; iJump++)
3706 object key = kb.rgKeys [iKey];
3707 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
3709 SwitchLabel sl = (SwitchLabel) Elements [key];
3710 rgLabels [iJump] = sl.ILLabel;
3714 rgLabels [iJump] = lblDefault;
3716 // emit the switch opcode
3717 ig.Emit (OpCodes.Switch, rgLabels);
3720 // mark the default for this block
3722 ig.MarkLabel (lblDefault);
3725 // TODO: find the default case and emit it here,
3726 // to prevent having to do the following jump.
3727 // make sure to mark other labels in the default section
3729 // the last default just goes to the end
3730 ig.Emit (OpCodes.Br, lblDefault);
3732 // now emit the code for the sections
3733 bool fFoundDefault = false;
3734 bool fAllReturn = true;
3735 foreach (SwitchSection ss in Sections)
3737 foreach (SwitchLabel sl in ss.Labels)
3739 ig.MarkLabel (sl.ILLabel);
3740 ig.MarkLabel (sl.ILLabelCode);
3741 if (sl.Label == null)
3743 ig.MarkLabel (lblDefault);
3744 fFoundDefault = true;
3747 bool returns = ss.Block.Emit (ec);
3748 fAllReturn &= returns;
3749 //ig.Emit (OpCodes.Br, lblEnd);
3752 if (!fFoundDefault) {
3753 ig.MarkLabel (lblDefault);
3756 ig.MarkLabel (lblEnd);
3761 // This simple emit switch works, but does not take advantage of the
3763 // TODO: remove non-string logic from here
3764 // TODO: binary search strings?
3766 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3768 ILGenerator ig = ec.ig;
3769 Label end_of_switch = ig.DefineLabel ();
3770 Label next_test = ig.DefineLabel ();
3771 Label null_target = ig.DefineLabel ();
3772 bool default_found = false;
3773 bool first_test = true;
3774 bool pending_goto_end = false;
3775 bool all_return = true;
3776 bool is_string = false;
3780 // Special processing for strings: we cant compare
3783 if (SwitchType == TypeManager.string_type){
3784 ig.Emit (OpCodes.Ldloc, val);
3787 if (Elements.Contains (NullLiteral.Null)){
3788 ig.Emit (OpCodes.Brfalse, null_target);
3790 ig.Emit (OpCodes.Brfalse, default_target);
3792 ig.Emit (OpCodes.Ldloc, val);
3793 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
3794 ig.Emit (OpCodes.Stloc, val);
3797 foreach (SwitchSection ss in Sections){
3798 Label sec_begin = ig.DefineLabel ();
3800 if (pending_goto_end)
3801 ig.Emit (OpCodes.Br, end_of_switch);
3803 int label_count = ss.Labels.Count;
3805 foreach (SwitchLabel sl in ss.Labels){
3806 ig.MarkLabel (sl.ILLabel);
3809 ig.MarkLabel (next_test);
3810 next_test = ig.DefineLabel ();
3813 // If we are the default target
3815 if (sl.Label == null){
3816 ig.MarkLabel (default_target);
3817 default_found = true;
3819 object lit = sl.Converted;
3821 if (lit is NullLiteral){
3823 if (label_count == 1)
3824 ig.Emit (OpCodes.Br, next_test);
3829 StringConstant str = (StringConstant) lit;
3831 ig.Emit (OpCodes.Ldloc, val);
3832 ig.Emit (OpCodes.Ldstr, str.Value);
3833 if (label_count == 1)
3834 ig.Emit (OpCodes.Bne_Un, next_test);
3836 ig.Emit (OpCodes.Beq, sec_begin);
3838 ig.Emit (OpCodes.Ldloc, val);
3839 EmitObjectInteger (ig, lit);
3840 ig.Emit (OpCodes.Ceq);
3841 if (label_count == 1)
3842 ig.Emit (OpCodes.Brfalse, next_test);
3844 ig.Emit (OpCodes.Brtrue, sec_begin);
3848 if (label_count != 1)
3849 ig.Emit (OpCodes.Br, next_test);
3852 ig.MarkLabel (null_target);
3853 ig.MarkLabel (sec_begin);
3854 foreach (SwitchLabel sl in ss.Labels)
3855 ig.MarkLabel (sl.ILLabelCode);
3857 bool returns = ss.Block.Emit (ec);
3859 pending_goto_end = false;
3862 pending_goto_end = true;
3866 if (!default_found){
3867 ig.MarkLabel (default_target);
3870 ig.MarkLabel (next_test);
3871 ig.MarkLabel (end_of_switch);
3876 public override bool Resolve (EmitContext ec)
3878 Expr = Expr.Resolve (ec);
3882 new_expr = SwitchGoverningType (ec, Expr.Type);
3883 if (new_expr == null){
3884 Report.Error (151, loc, "An integer type or string was expected for switch");
3889 SwitchType = new_expr.Type;
3891 if (!CheckSwitch (ec))
3894 Switch old_switch = ec.Switch;
3896 ec.Switch.SwitchType = SwitchType;
3898 ec.StartFlowBranching (FlowBranchingType.SWITCH, loc);
3901 foreach (SwitchSection ss in Sections){
3903 ec.CurrentBranching.CreateSibling ();
3907 if (ss.Block.Resolve (ec) != true)
3913 ec.CurrentBranching.CreateSibling ();
3915 ec.EndFlowBranching ();
3916 ec.Switch = old_switch;
3921 protected override bool DoEmit (EmitContext ec)
3923 // Store variable for comparission purposes
3924 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
3926 ec.ig.Emit (OpCodes.Stloc, value);
3928 ILGenerator ig = ec.ig;
3930 default_target = ig.DefineLabel ();
3933 // Setup the codegen context
3935 Label old_end = ec.LoopEnd;
3936 Switch old_switch = ec.Switch;
3938 ec.LoopEnd = ig.DefineLabel ();
3943 if (SwitchType == TypeManager.string_type)
3944 all_return = SimpleSwitchEmit (ec, value);
3946 all_return = TableSwitchEmit (ec, value);
3948 // Restore context state.
3949 ig.MarkLabel (ec.LoopEnd);
3952 // Restore the previous context
3954 ec.LoopEnd = old_end;
3955 ec.Switch = old_switch;
3961 public class Lock : Statement {
3963 Statement Statement;
3965 public Lock (Expression expr, Statement stmt, Location l)
3972 public override bool Resolve (EmitContext ec)
3974 expr = expr.Resolve (ec);
3975 return Statement.Resolve (ec) && expr != null;
3978 protected override bool DoEmit (EmitContext ec)
3980 Type type = expr.Type;
3983 if (type.IsValueType){
3984 Report.Error (185, loc, "lock statement requires the expression to be " +
3985 " a reference type (type is: `" +
3986 TypeManager.CSharpName (type) + "'");
3990 ILGenerator ig = ec.ig;
3991 LocalBuilder temp = ig.DeclareLocal (type);
3994 ig.Emit (OpCodes.Dup);
3995 ig.Emit (OpCodes.Stloc, temp);
3996 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3999 Label end = ig.BeginExceptionBlock ();
4000 bool old_in_try = ec.InTry;
4002 Label finish = ig.DefineLabel ();
4003 val = Statement.Emit (ec);
4004 ec.InTry = old_in_try;
4005 // ig.Emit (OpCodes.Leave, finish);
4007 ig.MarkLabel (finish);
4010 ig.BeginFinallyBlock ();
4011 ig.Emit (OpCodes.Ldloc, temp);
4012 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4013 ig.EndExceptionBlock ();
4019 public class Unchecked : Statement {
4020 public readonly Block Block;
4022 public Unchecked (Block b)
4027 public override bool Resolve (EmitContext ec)
4029 return Block.Resolve (ec);
4032 protected override bool DoEmit (EmitContext ec)
4034 bool previous_state = ec.CheckState;
4035 bool previous_state_const = ec.ConstantCheckState;
4038 ec.CheckState = false;
4039 ec.ConstantCheckState = false;
4040 val = Block.Emit (ec);
4041 ec.CheckState = previous_state;
4042 ec.ConstantCheckState = previous_state_const;
4048 public class Checked : Statement {
4049 public readonly Block Block;
4051 public Checked (Block b)
4056 public override bool Resolve (EmitContext ec)
4058 bool previous_state = ec.CheckState;
4059 bool previous_state_const = ec.ConstantCheckState;
4061 ec.CheckState = true;
4062 ec.ConstantCheckState = true;
4063 bool ret = Block.Resolve (ec);
4064 ec.CheckState = previous_state;
4065 ec.ConstantCheckState = previous_state_const;
4070 protected override bool DoEmit (EmitContext ec)
4072 bool previous_state = ec.CheckState;
4073 bool previous_state_const = ec.ConstantCheckState;
4076 ec.CheckState = true;
4077 ec.ConstantCheckState = true;
4078 val = Block.Emit (ec);
4079 ec.CheckState = previous_state;
4080 ec.ConstantCheckState = previous_state_const;
4086 public class Unsafe : Statement {
4087 public readonly Block Block;
4089 public Unsafe (Block b)
4094 public override bool Resolve (EmitContext ec)
4096 bool previous_state = ec.InUnsafe;
4100 val = Block.Resolve (ec);
4101 ec.InUnsafe = previous_state;
4106 protected override bool DoEmit (EmitContext ec)
4108 bool previous_state = ec.InUnsafe;
4112 val = Block.Emit (ec);
4113 ec.InUnsafe = previous_state;
4122 public class Fixed : Statement {
4124 ArrayList declarators;
4125 Statement statement;
4130 public bool is_object;
4131 public VariableInfo vi;
4132 public Expression expr;
4133 public Expression converted;
4136 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4139 declarators = decls;
4144 public override bool Resolve (EmitContext ec)
4146 expr_type = ec.DeclSpace.ResolveType (type, false, loc);
4147 if (expr_type == null)
4150 data = new FixedData [declarators.Count];
4153 foreach (Pair p in declarators){
4154 VariableInfo vi = (VariableInfo) p.First;
4155 Expression e = (Expression) p.Second;
4160 // The rules for the possible declarators are pretty wise,
4161 // but the production on the grammar is more concise.
4163 // So we have to enforce these rules here.
4165 // We do not resolve before doing the case 1 test,
4166 // because the grammar is explicit in that the token &
4167 // is present, so we need to test for this particular case.
4171 // Case 1: & object.
4173 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4174 Expression child = ((Unary) e).Expr;
4177 if (child is ParameterReference || child is LocalVariableReference){
4180 "No need to use fixed statement for parameters or " +
4181 "local variable declarations (address is already " +
4190 child = ((Unary) e).Expr;
4192 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4195 data [i].is_object = true;
4197 data [i].converted = null;
4211 if (e.Type.IsArray){
4212 Type array_type = e.Type.GetElementType ();
4216 // Provided that array_type is unmanaged,
4218 if (!TypeManager.VerifyUnManaged (array_type, loc))
4222 // and T* is implicitly convertible to the
4223 // pointer type given in the fixed statement.
4225 ArrayPtr array_ptr = new ArrayPtr (e, loc);
4227 Expression converted = Expression.ConvertImplicitRequired (
4228 ec, array_ptr, vi.VariableType, loc);
4229 if (converted == null)
4232 data [i].is_object = false;
4234 data [i].converted = converted;
4244 if (e.Type == TypeManager.string_type){
4245 data [i].is_object = false;
4247 data [i].converted = null;
4253 return statement.Resolve (ec);
4256 protected override bool DoEmit (EmitContext ec)
4258 ILGenerator ig = ec.ig;
4260 bool is_ret = false;
4262 for (int i = 0; i < data.Length; i++) {
4263 VariableInfo vi = data [i].vi;
4266 // Case 1: & object.
4268 if (data [i].is_object) {
4270 // Store pointer in pinned location
4272 data [i].expr.Emit (ec);
4273 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4275 is_ret = statement.Emit (ec);
4277 // Clear the pinned variable.
4278 ig.Emit (OpCodes.Ldc_I4_0);
4279 ig.Emit (OpCodes.Conv_U);
4280 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4288 if (data [i].expr.Type.IsArray){
4290 // Store pointer in pinned location
4292 data [i].converted.Emit (ec);
4294 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4296 is_ret = statement.Emit (ec);
4298 // Clear the pinned variable.
4299 ig.Emit (OpCodes.Ldc_I4_0);
4300 ig.Emit (OpCodes.Conv_U);
4301 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4309 if (data [i].expr.Type == TypeManager.string_type){
4310 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
4311 TypeManager.MakePinned (pinned_string);
4313 data [i].expr.Emit (ec);
4314 ig.Emit (OpCodes.Stloc, pinned_string);
4316 Expression sptr = new StringPtr (pinned_string, loc);
4317 Expression converted = Expression.ConvertImplicitRequired (
4318 ec, sptr, vi.VariableType, loc);
4320 if (converted == null)
4323 converted.Emit (ec);
4324 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4326 is_ret = statement.Emit (ec);
4328 // Clear the pinned variable
4329 ig.Emit (OpCodes.Ldnull);
4330 ig.Emit (OpCodes.Stloc, pinned_string);
4338 public class Catch {
4339 public readonly string Name;
4340 public readonly Block Block;
4341 public readonly Location Location;
4343 Expression type_expr;
4346 public Catch (Expression type, string name, Block block, Location l)
4354 public Type CatchType {
4360 public bool IsGeneral {
4362 return type_expr == null;
4366 public bool Resolve (EmitContext ec)
4368 if (type_expr != null) {
4369 type = ec.DeclSpace.ResolveType (type_expr, false, Location);
4373 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4374 Report.Error (155, Location,
4375 "The type caught or thrown must be derived " +
4376 "from System.Exception");
4382 if (!Block.Resolve (ec))
4389 public class Try : Statement {
4390 public readonly Block Fini, Block;
4391 public readonly ArrayList Specific;
4392 public readonly Catch General;
4395 // specific, general and fini might all be null.
4397 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4399 if (specific == null && general == null){
4400 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4404 this.Specific = specific;
4405 this.General = general;
4410 public override bool Resolve (EmitContext ec)
4414 ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation);
4416 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4418 bool old_in_try = ec.InTry;
4421 if (!Block.Resolve (ec))
4424 ec.InTry = old_in_try;
4426 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4428 Report.Debug (1, "START OF CATCH BLOCKS", vector);
4430 foreach (Catch c in Specific){
4431 ec.CurrentBranching.CreateSibling ();
4432 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4434 if (c.Name != null) {
4435 VariableInfo vi = c.Block.GetVariableInfo (c.Name);
4437 throw new Exception ();
4442 bool old_in_catch = ec.InCatch;
4445 if (!c.Resolve (ec))
4448 ec.InCatch = old_in_catch;
4450 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4452 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4453 vector.AndLocals (current);
4456 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4458 if (General != null){
4459 ec.CurrentBranching.CreateSibling ();
4460 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4462 bool old_in_catch = ec.InCatch;
4465 if (!General.Resolve (ec))
4468 ec.InCatch = old_in_catch;
4470 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4472 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4473 vector.AndLocals (current);
4476 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4479 ec.CurrentBranching.CreateSiblingForFinally ();
4480 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4482 bool old_in_finally = ec.InFinally;
4483 ec.InFinally = true;
4485 if (!Fini.Resolve (ec))
4488 ec.InFinally = old_in_finally;
4491 FlowReturns returns = ec.EndFlowBranching ();
4493 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4495 Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector);
4497 if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) {
4498 ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc);
4501 ec.CurrentBranching.CurrentUsageVector.Or (vector);
4503 Report.Debug (1, "END OF TRY", ec.CurrentBranching);
4508 protected override bool DoEmit (EmitContext ec)
4510 ILGenerator ig = ec.ig;
4512 Label finish = ig.DefineLabel ();;
4516 end = ig.BeginExceptionBlock ();
4517 bool old_in_try = ec.InTry;
4519 returns = Block.Emit (ec);
4520 ec.InTry = old_in_try;
4523 // System.Reflection.Emit provides this automatically:
4524 // ig.Emit (OpCodes.Leave, finish);
4526 bool old_in_catch = ec.InCatch;
4528 DeclSpace ds = ec.DeclSpace;
4530 foreach (Catch c in Specific){
4533 ig.BeginCatchBlock (c.CatchType);
4535 if (c.Name != null){
4536 vi = c.Block.GetVariableInfo (c.Name);
4538 throw new Exception ("Variable does not exist in this block");
4540 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4542 ig.Emit (OpCodes.Pop);
4544 if (!c.Block.Emit (ec))
4548 if (General != null){
4549 ig.BeginCatchBlock (TypeManager.object_type);
4550 ig.Emit (OpCodes.Pop);
4551 if (!General.Block.Emit (ec))
4554 ec.InCatch = old_in_catch;
4556 ig.MarkLabel (finish);
4558 ig.BeginFinallyBlock ();
4559 bool old_in_finally = ec.InFinally;
4560 ec.InFinally = true;
4562 ec.InFinally = old_in_finally;
4565 ig.EndExceptionBlock ();
4568 if (!returns || ec.InTry || ec.InCatch)
4571 // Unfortunately, System.Reflection.Emit automatically emits a leave
4572 // to the end of the finally block. This is a problem if `returns'
4573 // is true since we may jump to a point after the end of the method.
4574 // As a workaround, emit an explicit ret here.
4576 if (ec.ReturnType != null)
4577 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4578 ec.ig.Emit (OpCodes.Ret);
4584 public class Using : Statement {
4585 object expression_or_block;
4586 Statement Statement;
4591 Expression [] converted_vars;
4592 ExpressionStatement [] assign;
4594 public Using (object expression_or_block, Statement stmt, Location l)
4596 this.expression_or_block = expression_or_block;
4602 // Resolves for the case of using using a local variable declaration.
4604 bool ResolveLocalVariableDecls (EmitContext ec)
4606 bool need_conv = false;
4607 expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
4610 if (expr_type == null)
4614 // The type must be an IDisposable or an implicit conversion
4617 converted_vars = new Expression [var_list.Count];
4618 assign = new ExpressionStatement [var_list.Count];
4619 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4620 foreach (DictionaryEntry e in var_list){
4621 Expression var = (Expression) e.Key;
4623 var = var.ResolveLValue (ec, new EmptyExpression ());
4627 converted_vars [i] = Expression.ConvertImplicitRequired (
4628 ec, var, TypeManager.idisposable_type, loc);
4630 if (converted_vars [i] == null)
4638 foreach (DictionaryEntry e in var_list){
4639 LocalVariableReference var = (LocalVariableReference) e.Key;
4640 Expression new_expr = (Expression) e.Value;
4643 a = new Assign (var, new_expr, loc);
4649 converted_vars [i] = var;
4650 assign [i] = (ExpressionStatement) a;
4657 bool ResolveExpression (EmitContext ec)
4659 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4660 conv = Expression.ConvertImplicitRequired (
4661 ec, expr, TypeManager.idisposable_type, loc);
4671 // Emits the code for the case of using using a local variable declaration.
4673 bool EmitLocalVariableDecls (EmitContext ec)
4675 ILGenerator ig = ec.ig;
4678 bool old_in_try = ec.InTry;
4680 for (i = 0; i < assign.Length; i++) {
4681 assign [i].EmitStatement (ec);
4683 ig.BeginExceptionBlock ();
4685 Statement.Emit (ec);
4686 ec.InTry = old_in_try;
4688 bool old_in_finally = ec.InFinally;
4689 ec.InFinally = true;
4690 var_list.Reverse ();
4691 foreach (DictionaryEntry e in var_list){
4692 LocalVariableReference var = (LocalVariableReference) e.Key;
4693 Label skip = ig.DefineLabel ();
4696 ig.BeginFinallyBlock ();
4699 ig.Emit (OpCodes.Brfalse, skip);
4700 converted_vars [i].Emit (ec);
4701 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4702 ig.MarkLabel (skip);
4703 ig.EndExceptionBlock ();
4705 ec.InFinally = old_in_finally;
4710 bool EmitExpression (EmitContext ec)
4713 // Make a copy of the expression and operate on that.
4715 ILGenerator ig = ec.ig;
4716 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
4721 ig.Emit (OpCodes.Stloc, local_copy);
4723 bool old_in_try = ec.InTry;
4725 ig.BeginExceptionBlock ();
4726 Statement.Emit (ec);
4727 ec.InTry = old_in_try;
4729 Label skip = ig.DefineLabel ();
4730 bool old_in_finally = ec.InFinally;
4731 ig.BeginFinallyBlock ();
4732 ig.Emit (OpCodes.Ldloc, local_copy);
4733 ig.Emit (OpCodes.Brfalse, skip);
4734 ig.Emit (OpCodes.Ldloc, local_copy);
4735 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4736 ig.MarkLabel (skip);
4737 ec.InFinally = old_in_finally;
4738 ig.EndExceptionBlock ();
4743 public override bool Resolve (EmitContext ec)
4745 if (expression_or_block is DictionaryEntry){
4746 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4747 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4749 if (!ResolveLocalVariableDecls (ec))
4752 } else if (expression_or_block is Expression){
4753 expr = (Expression) expression_or_block;
4755 expr = expr.Resolve (ec);
4759 expr_type = expr.Type;
4761 if (!ResolveExpression (ec))
4765 return Statement.Resolve (ec);
4768 protected override bool DoEmit (EmitContext ec)
4770 if (expression_or_block is DictionaryEntry)
4771 return EmitLocalVariableDecls (ec);
4772 else if (expression_or_block is Expression)
4773 return EmitExpression (ec);
4780 /// Implementation of the foreach C# statement
4782 public class Foreach : Statement {
4784 LocalVariableReference variable;
4786 Statement statement;
4787 ForeachHelperMethods hm;
4788 Expression empty, conv;
4789 Type array_type, element_type;
4792 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4793 Statement stmt, Location l)
4796 this.variable = var;
4802 public override bool Resolve (EmitContext ec)
4804 expr = expr.Resolve (ec);
4808 var_type = ec.DeclSpace.ResolveType (type, false, loc);
4809 if (var_type == null)
4813 // We need an instance variable. Not sure this is the best
4814 // way of doing this.
4816 // FIXME: When we implement propertyaccess, will those turn
4817 // out to return values in ExprClass? I think they should.
4819 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4820 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4821 error1579 (expr.Type);
4825 if (expr.Type.IsArray) {
4826 array_type = expr.Type;
4827 element_type = array_type.GetElementType ();
4829 empty = new EmptyExpression (element_type);
4831 hm = ProbeCollectionType (ec, expr.Type);
4833 error1579 (expr.Type);
4837 array_type = expr.Type;
4838 element_type = hm.element_type;
4840 empty = new EmptyExpression (hm.element_type);
4843 ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
4844 ec.CurrentBranching.CreateSibling ();
4848 // FIXME: maybe we can apply the same trick we do in the
4849 // array handling to avoid creating empty and conv in some cases.
4851 // Although it is not as important in this case, as the type
4852 // will not likely be object (what the enumerator will return).
4854 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
4858 if (variable.ResolveLValue (ec, empty) == null)
4861 if (!statement.Resolve (ec))
4864 FlowReturns returns = ec.EndFlowBranching ();
4870 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4872 static MethodInfo FetchMethodMoveNext (Type t)
4874 MemberList move_next_list;
4876 move_next_list = TypeContainer.FindMembers (
4877 t, MemberTypes.Method,
4878 BindingFlags.Public | BindingFlags.Instance,
4879 Type.FilterName, "MoveNext");
4880 if (move_next_list.Count == 0)
4883 foreach (MemberInfo m in move_next_list){
4884 MethodInfo mi = (MethodInfo) m;
4887 args = TypeManager.GetArgumentTypes (mi);
4888 if (args != null && args.Length == 0){
4889 if (mi.ReturnType == TypeManager.bool_type)
4897 // Retrieves a `public T get_Current ()' method from the Type `t'
4899 static MethodInfo FetchMethodGetCurrent (Type t)
4901 MemberList move_next_list;
4903 move_next_list = TypeContainer.FindMembers (
4904 t, MemberTypes.Method,
4905 BindingFlags.Public | BindingFlags.Instance,
4906 Type.FilterName, "get_Current");
4907 if (move_next_list.Count == 0)
4910 foreach (MemberInfo m in move_next_list){
4911 MethodInfo mi = (MethodInfo) m;
4914 args = TypeManager.GetArgumentTypes (mi);
4915 if (args != null && args.Length == 0)
4922 // This struct records the helper methods used by the Foreach construct
4924 class ForeachHelperMethods {
4925 public EmitContext ec;
4926 public MethodInfo get_enumerator;
4927 public MethodInfo move_next;
4928 public MethodInfo get_current;
4929 public Type element_type;
4930 public Type enumerator_type;
4931 public bool is_disposable;
4933 public ForeachHelperMethods (EmitContext ec)
4936 this.element_type = TypeManager.object_type;
4937 this.enumerator_type = TypeManager.ienumerator_type;
4938 this.is_disposable = true;
4942 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
4947 if (!(m is MethodInfo))
4950 if (m.Name != "GetEnumerator")
4953 MethodInfo mi = (MethodInfo) m;
4954 Type [] args = TypeManager.GetArgumentTypes (mi);
4956 if (args.Length != 0)
4959 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
4960 EmitContext ec = hm.ec;
4963 // Check whether GetEnumerator is accessible to us
4965 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
4967 Type declaring = mi.DeclaringType;
4968 if (prot == MethodAttributes.Private){
4969 if (declaring != ec.ContainerType)
4971 } else if (prot == MethodAttributes.FamANDAssem){
4972 // If from a different assembly, false
4973 if (!(mi is MethodBuilder))
4976 // Are we being invoked from the same class, or from a derived method?
4978 if (ec.ContainerType != declaring){
4979 if (!ec.ContainerType.IsSubclassOf (declaring))
4982 } else if (prot == MethodAttributes.FamORAssem){
4983 if (!(mi is MethodBuilder ||
4984 ec.ContainerType == declaring ||
4985 ec.ContainerType.IsSubclassOf (declaring)))
4987 } if (prot == MethodAttributes.Family){
4988 if (!(ec.ContainerType == declaring ||
4989 ec.ContainerType.IsSubclassOf (declaring)))
4994 // Ok, we can access it, now make sure that we can do something
4995 // with this `GetEnumerator'
4998 if (mi.ReturnType == TypeManager.ienumerator_type ||
4999 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
5000 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
5001 hm.move_next = TypeManager.bool_movenext_void;
5002 hm.get_current = TypeManager.object_getcurrent_void;
5007 // Ok, so they dont return an IEnumerable, we will have to
5008 // find if they support the GetEnumerator pattern.
5010 Type return_type = mi.ReturnType;
5012 hm.move_next = FetchMethodMoveNext (return_type);
5013 if (hm.move_next == null)
5015 hm.get_current = FetchMethodGetCurrent (return_type);
5016 if (hm.get_current == null)
5019 hm.element_type = hm.get_current.ReturnType;
5020 hm.enumerator_type = return_type;
5021 hm.is_disposable = TypeManager.ImplementsInterface (
5022 hm.enumerator_type, TypeManager.idisposable_type);
5028 /// This filter is used to find the GetEnumerator method
5029 /// on which IEnumerator operates
5031 static MemberFilter FilterEnumerator;
5035 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
5038 void error1579 (Type t)
5040 Report.Error (1579, loc,
5041 "foreach statement cannot operate on variables of type `" +
5042 t.FullName + "' because that class does not provide a " +
5043 " GetEnumerator method or it is inaccessible");
5046 static bool TryType (Type t, ForeachHelperMethods hm)
5050 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
5051 BindingFlags.Public | BindingFlags.NonPublic |
5052 BindingFlags.Instance,
5053 FilterEnumerator, hm);
5058 hm.get_enumerator = (MethodInfo) mi [0];
5063 // Looks for a usable GetEnumerator in the Type, and if found returns
5064 // the three methods that participate: GetEnumerator, MoveNext and get_Current
5066 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
5068 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
5070 if (TryType (t, hm))
5074 // Now try to find the method in the interfaces
5077 Type [] ifaces = t.GetInterfaces ();
5079 foreach (Type i in ifaces){
5080 if (TryType (i, hm))
5085 // Since TypeBuilder.GetInterfaces only returns the interface
5086 // types for this type, we have to keep looping, but once
5087 // we hit a non-TypeBuilder (ie, a Type), then we know we are
5088 // done, because it returns all the types
5090 if ((t is TypeBuilder))
5100 // FIXME: possible optimization.
5101 // We might be able to avoid creating `empty' if the type is the sam
5103 bool EmitCollectionForeach (EmitContext ec)
5105 ILGenerator ig = ec.ig;
5106 LocalBuilder enumerator, disposable;
5108 enumerator = ig.DeclareLocal (hm.enumerator_type);
5109 if (hm.is_disposable)
5110 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
5115 // Instantiate the enumerator
5117 if (expr.Type.IsValueType){
5118 if (expr is IMemoryLocation){
5119 IMemoryLocation ml = (IMemoryLocation) expr;
5121 ml.AddressOf (ec, AddressOp.Load);
5123 throw new Exception ("Expr " + expr + " of type " + expr.Type +
5124 " does not implement IMemoryLocation");
5125 ig.Emit (OpCodes.Call, hm.get_enumerator);
5128 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
5130 ig.Emit (OpCodes.Stloc, enumerator);
5133 // Protect the code in a try/finalize block, so that
5134 // if the beast implement IDisposable, we get rid of it
5137 bool old_in_try = ec.InTry;
5139 if (hm.is_disposable) {
5140 l = ig.BeginExceptionBlock ();
5144 Label end_try = ig.DefineLabel ();
5146 ig.MarkLabel (ec.LoopBegin);
5147 ig.Emit (OpCodes.Ldloc, enumerator);
5148 ig.Emit (OpCodes.Callvirt, hm.move_next);
5149 ig.Emit (OpCodes.Brfalse, end_try);
5150 ig.Emit (OpCodes.Ldloc, enumerator);
5151 ig.Emit (OpCodes.Callvirt, hm.get_current);
5152 variable.EmitAssign (ec, conv);
5153 statement.Emit (ec);
5154 ig.Emit (OpCodes.Br, ec.LoopBegin);
5155 ig.MarkLabel (end_try);
5156 ec.InTry = old_in_try;
5158 // The runtime provides this for us.
5159 // ig.Emit (OpCodes.Leave, end);
5162 // Now the finally block
5164 if (hm.is_disposable) {
5165 Label end_finally = ig.DefineLabel ();
5166 bool old_in_finally = ec.InFinally;
5167 ec.InFinally = true;
5168 ig.BeginFinallyBlock ();
5170 ig.Emit (OpCodes.Ldloc, enumerator);
5171 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5172 ig.Emit (OpCodes.Stloc, disposable);
5173 ig.Emit (OpCodes.Ldloc, disposable);
5174 ig.Emit (OpCodes.Brfalse, end_finally);
5175 ig.Emit (OpCodes.Ldloc, disposable);
5176 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5177 ig.MarkLabel (end_finally);
5178 ec.InFinally = old_in_finally;
5180 // The runtime generates this anyways.
5181 // ig.Emit (OpCodes.Endfinally);
5183 ig.EndExceptionBlock ();
5186 ig.MarkLabel (ec.LoopEnd);
5191 // FIXME: possible optimization.
5192 // We might be able to avoid creating `empty' if the type is the sam
5194 bool EmitArrayForeach (EmitContext ec)
5196 int rank = array_type.GetArrayRank ();
5197 ILGenerator ig = ec.ig;
5199 LocalBuilder copy = ig.DeclareLocal (array_type);
5202 // Make our copy of the array
5205 ig.Emit (OpCodes.Stloc, copy);
5208 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
5212 ig.Emit (OpCodes.Ldc_I4_0);
5213 ig.Emit (OpCodes.Stloc, counter);
5214 test = ig.DefineLabel ();
5215 ig.Emit (OpCodes.Br, test);
5217 loop = ig.DefineLabel ();
5218 ig.MarkLabel (loop);
5220 ig.Emit (OpCodes.Ldloc, copy);
5221 ig.Emit (OpCodes.Ldloc, counter);
5222 ArrayAccess.EmitLoadOpcode (ig, var_type);
5224 variable.EmitAssign (ec, conv);
5226 statement.Emit (ec);
5228 ig.MarkLabel (ec.LoopBegin);
5229 ig.Emit (OpCodes.Ldloc, counter);
5230 ig.Emit (OpCodes.Ldc_I4_1);
5231 ig.Emit (OpCodes.Add);
5232 ig.Emit (OpCodes.Stloc, counter);
5234 ig.MarkLabel (test);
5235 ig.Emit (OpCodes.Ldloc, counter);
5236 ig.Emit (OpCodes.Ldloc, copy);
5237 ig.Emit (OpCodes.Ldlen);
5238 ig.Emit (OpCodes.Conv_I4);
5239 ig.Emit (OpCodes.Blt, loop);
5241 LocalBuilder [] dim_len = new LocalBuilder [rank];
5242 LocalBuilder [] dim_count = new LocalBuilder [rank];
5243 Label [] loop = new Label [rank];
5244 Label [] test = new Label [rank];
5247 for (dim = 0; dim < rank; dim++){
5248 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
5249 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
5250 test [dim] = ig.DefineLabel ();
5251 loop [dim] = ig.DefineLabel ();
5254 for (dim = 0; dim < rank; dim++){
5255 ig.Emit (OpCodes.Ldloc, copy);
5256 IntLiteral.EmitInt (ig, dim);
5257 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
5258 ig.Emit (OpCodes.Stloc, dim_len [dim]);
5261 for (dim = 0; dim < rank; dim++){
5262 ig.Emit (OpCodes.Ldc_I4_0);
5263 ig.Emit (OpCodes.Stloc, dim_count [dim]);
5264 ig.Emit (OpCodes.Br, test [dim]);
5265 ig.MarkLabel (loop [dim]);
5268 ig.Emit (OpCodes.Ldloc, copy);
5269 for (dim = 0; dim < rank; dim++)
5270 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5273 // FIXME: Maybe we can cache the computation of `get'?
5275 Type [] args = new Type [rank];
5278 for (int i = 0; i < rank; i++)
5279 args [i] = TypeManager.int32_type;
5281 ModuleBuilder mb = CodeGen.ModuleBuilder;
5282 get = mb.GetArrayMethod (
5284 CallingConventions.HasThis| CallingConventions.Standard,
5286 ig.Emit (OpCodes.Call, get);
5287 variable.EmitAssign (ec, conv);
5288 statement.Emit (ec);
5289 ig.MarkLabel (ec.LoopBegin);
5290 for (dim = rank - 1; dim >= 0; dim--){
5291 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5292 ig.Emit (OpCodes.Ldc_I4_1);
5293 ig.Emit (OpCodes.Add);
5294 ig.Emit (OpCodes.Stloc, dim_count [dim]);
5296 ig.MarkLabel (test [dim]);
5297 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5298 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
5299 ig.Emit (OpCodes.Blt, loop [dim]);
5302 ig.MarkLabel (ec.LoopEnd);
5307 protected override bool DoEmit (EmitContext ec)
5311 ILGenerator ig = ec.ig;
5313 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5314 bool old_inloop = ec.InLoop;
5315 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
5316 ec.LoopBegin = ig.DefineLabel ();
5317 ec.LoopEnd = ig.DefineLabel ();
5319 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
5322 ret_val = EmitCollectionForeach (ec);
5324 ret_val = EmitArrayForeach (ec);
5326 ec.LoopBegin = old_begin;
5327 ec.LoopEnd = old_end;
5328 ec.InLoop = old_inloop;
5329 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;