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, 2003 Ximian, Inc.
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Diagnostics;
17 namespace Mono.CSharp {
19 using System.Collections;
21 public abstract class Statement {
25 /// Resolves the statement, true means that all sub-statements
28 public virtual bool Resolve (EmitContext ec)
34 /// Return value indicates whether all code paths emitted return.
36 protected abstract bool DoEmit (EmitContext ec);
39 /// Return value indicates whether all code paths emitted return.
41 public virtual bool Emit (EmitContext ec)
48 /// Encapsulates the emission of a boolean test and jumping to a
51 /// This will emit the bool expression in `bool_expr' and if
52 /// `target_is_for_true' is true, then the code will generate a
53 /// brtrue to the target. Otherwise a brfalse.
55 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
56 Label target, bool target_is_for_true)
58 ILGenerator ig = ec.ig;
61 if (bool_expr is Unary){
62 Unary u = (Unary) bool_expr;
64 if (u.Oper == Unary.Operator.LogicalNot){
67 u.EmitLogicalNot (ec);
69 } else if (bool_expr is Binary){
70 Binary b = (Binary) bool_expr;
72 if (b.EmitBranchable (ec, target, target_is_for_true))
79 if (target_is_for_true){
81 ig.Emit (OpCodes.Brfalse, target);
83 ig.Emit (OpCodes.Brtrue, target);
86 ig.Emit (OpCodes.Brtrue, target);
88 ig.Emit (OpCodes.Brfalse, target);
92 public static void Warning_DeadCodeFound (Location loc)
94 Report.Warning (162, loc, "Unreachable code detected");
98 public class EmptyStatement : Statement {
99 public override bool Resolve (EmitContext ec)
104 protected override bool DoEmit (EmitContext ec)
110 public class If : Statement {
112 public Statement TrueStatement;
113 public Statement FalseStatement;
115 public If (Expression expr, Statement trueStatement, Location l)
118 TrueStatement = trueStatement;
122 public If (Expression expr,
123 Statement trueStatement,
124 Statement falseStatement,
128 TrueStatement = trueStatement;
129 FalseStatement = falseStatement;
133 public override bool Resolve (EmitContext ec)
135 Report.Debug (1, "START IF BLOCK", loc);
137 expr = Expression.ResolveBoolean (ec, expr, loc);
142 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
144 if (!TrueStatement.Resolve (ec)) {
145 ec.KillFlowBranching ();
149 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
151 if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
152 ec.KillFlowBranching ();
156 ec.EndFlowBranching ();
158 Report.Debug (1, "END IF BLOCK", loc);
163 protected override bool DoEmit (EmitContext ec)
165 ILGenerator ig = ec.ig;
166 Label false_target = ig.DefineLabel ();
168 bool is_true_ret, is_false_ret;
171 // Dead code elimination
173 if (expr is BoolConstant){
174 bool take = ((BoolConstant) expr).Value;
177 if (FalseStatement != null){
178 Warning_DeadCodeFound (FalseStatement.loc);
180 return TrueStatement.Emit (ec);
182 Warning_DeadCodeFound (TrueStatement.loc);
183 if (FalseStatement != null)
184 return FalseStatement.Emit (ec);
188 EmitBoolExpression (ec, expr, false_target, false);
190 is_true_ret = TrueStatement.Emit (ec);
191 is_false_ret = is_true_ret;
193 if (FalseStatement != null){
194 bool branch_emitted = false;
196 end = ig.DefineLabel ();
198 ig.Emit (OpCodes.Br, end);
199 branch_emitted = true;
202 ig.MarkLabel (false_target);
203 is_false_ret = FalseStatement.Emit (ec);
208 ig.MarkLabel (false_target);
209 is_false_ret = false;
212 return is_true_ret && is_false_ret;
216 public class Do : Statement {
217 public Expression expr;
218 public readonly Statement EmbeddedStatement;
219 bool infinite, may_return;
221 public Do (Statement statement, Expression boolExpr, Location l)
224 EmbeddedStatement = statement;
228 public override bool Resolve (EmitContext ec)
232 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
234 if (!EmbeddedStatement.Resolve (ec))
237 expr = Expression.ResolveBoolean (ec, expr, loc);
240 else if (expr is BoolConstant){
241 bool res = ((BoolConstant) expr).Value;
247 ec.CurrentBranching.Infinite = infinite;
248 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
249 may_return = returns != FlowBranching.FlowReturns.Never;
254 protected override bool DoEmit (EmitContext ec)
256 ILGenerator ig = ec.ig;
257 Label loop = ig.DefineLabel ();
258 Label old_begin = ec.LoopBegin;
259 Label old_end = ec.LoopEnd;
260 bool old_inloop = ec.InLoop;
261 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
263 ec.LoopBegin = ig.DefineLabel ();
264 ec.LoopEnd = ig.DefineLabel ();
266 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
269 EmbeddedStatement.Emit (ec);
270 ig.MarkLabel (ec.LoopBegin);
273 // Dead code elimination
275 if (expr is BoolConstant){
276 bool res = ((BoolConstant) expr).Value;
279 ec.ig.Emit (OpCodes.Br, loop);
281 EmitBoolExpression (ec, expr, loop, true);
283 ig.MarkLabel (ec.LoopEnd);
285 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
286 ec.LoopBegin = old_begin;
287 ec.LoopEnd = old_end;
288 ec.InLoop = old_inloop;
291 return may_return == false;
297 public class While : Statement {
298 public Expression expr;
299 public readonly Statement Statement;
300 bool may_return, empty, infinite;
302 public While (Expression boolExpr, Statement statement, Location l)
304 this.expr = boolExpr;
305 Statement = statement;
309 public override bool Resolve (EmitContext ec)
313 expr = Expression.ResolveBoolean (ec, expr, loc);
317 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
320 // Inform whether we are infinite or not
322 if (expr is BoolConstant){
323 BoolConstant bc = (BoolConstant) expr;
325 if (bc.Value == false){
326 Warning_DeadCodeFound (Statement.loc);
332 // We are not infinite, so the loop may or may not be executed.
334 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
337 if (!Statement.Resolve (ec))
341 ec.KillFlowBranching ();
343 ec.CurrentBranching.Infinite = infinite;
344 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
345 may_return = returns != FlowBranching.FlowReturns.Never;
351 protected override bool DoEmit (EmitContext ec)
356 ILGenerator ig = ec.ig;
357 Label old_begin = ec.LoopBegin;
358 Label old_end = ec.LoopEnd;
359 bool old_inloop = ec.InLoop;
360 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
363 ec.LoopBegin = ig.DefineLabel ();
364 ec.LoopEnd = ig.DefineLabel ();
366 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
369 // Inform whether we are infinite or not
371 if (expr is BoolConstant){
372 BoolConstant bc = (BoolConstant) expr;
374 ig.MarkLabel (ec.LoopBegin);
376 ig.Emit (OpCodes.Br, ec.LoopBegin);
379 // Inform that we are infinite (ie, `we return'), only
380 // if we do not `break' inside the code.
382 ret = may_return == false;
383 ig.MarkLabel (ec.LoopEnd);
385 Label while_loop = ig.DefineLabel ();
387 ig.Emit (OpCodes.Br, ec.LoopBegin);
388 ig.MarkLabel (while_loop);
392 ig.MarkLabel (ec.LoopBegin);
394 EmitBoolExpression (ec, expr, while_loop, true);
395 ig.MarkLabel (ec.LoopEnd);
400 ec.LoopBegin = old_begin;
401 ec.LoopEnd = old_end;
402 ec.InLoop = old_inloop;
403 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
409 public class For : Statement {
411 readonly Statement InitStatement;
412 readonly Statement Increment;
413 readonly Statement Statement;
414 bool may_return, infinite, empty;
416 public For (Statement initStatement,
422 InitStatement = initStatement;
424 Increment = increment;
425 Statement = statement;
429 public override bool Resolve (EmitContext ec)
433 if (InitStatement != null){
434 if (!InitStatement.Resolve (ec))
439 Test = Expression.ResolveBoolean (ec, Test, loc);
442 else if (Test is BoolConstant){
443 BoolConstant bc = (BoolConstant) Test;
445 if (bc.Value == false){
446 Warning_DeadCodeFound (Statement.loc);
454 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
456 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
458 if (!Statement.Resolve (ec))
461 if (Increment != null){
462 if (!Increment.Resolve (ec))
467 ec.KillFlowBranching ();
469 ec.CurrentBranching.Infinite = infinite;
470 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
471 may_return = returns != FlowBranching.FlowReturns.Never;
477 protected override bool DoEmit (EmitContext ec)
482 ILGenerator ig = ec.ig;
483 Label old_begin = ec.LoopBegin;
484 Label old_end = ec.LoopEnd;
485 bool old_inloop = ec.InLoop;
486 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
487 Label loop = ig.DefineLabel ();
488 Label test = ig.DefineLabel ();
490 if (InitStatement != null)
491 if (! (InitStatement is EmptyStatement))
492 InitStatement.Emit (ec);
494 ec.LoopBegin = ig.DefineLabel ();
495 ec.LoopEnd = ig.DefineLabel ();
497 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
499 ig.Emit (OpCodes.Br, test);
503 ig.MarkLabel (ec.LoopBegin);
504 if (!(Increment is EmptyStatement))
509 // If test is null, there is no test, and we are just
514 // The Resolve code already catches the case for Test == BoolConstant (false)
515 // so we know that this is true
517 if (Test is BoolConstant)
518 ig.Emit (OpCodes.Br, loop);
520 EmitBoolExpression (ec, Test, loop, true);
522 ig.Emit (OpCodes.Br, loop);
523 ig.MarkLabel (ec.LoopEnd);
525 ec.LoopBegin = old_begin;
526 ec.LoopEnd = old_end;
527 ec.InLoop = old_inloop;
528 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
531 // Inform whether we are infinite or not
534 if (Test is BoolConstant){
535 BoolConstant bc = (BoolConstant) Test;
538 return may_return == false;
542 return may_return == false;
546 public class StatementExpression : Statement {
547 ExpressionStatement expr;
549 public StatementExpression (ExpressionStatement expr, Location l)
555 public override bool Resolve (EmitContext ec)
557 expr = expr.ResolveStatement (ec);
561 protected override bool DoEmit (EmitContext ec)
563 ILGenerator ig = ec.ig;
565 expr.EmitStatement (ec);
570 public override string ToString ()
572 return "StatementExpression (" + expr + ")";
577 /// Implements the return statement
579 public class Return : Statement {
580 public Expression Expr;
582 public Return (Expression expr, Location l)
588 public override bool Resolve (EmitContext ec)
591 Expr = Expr.Resolve (ec);
597 Report.Error (-206, loc, "Return statement not allowed inside iterators");
601 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
603 if (ec.CurrentBranching.InTryBlock ())
604 ec.CurrentBranching.AddFinallyVector (vector);
606 vector.CheckOutParameters (ec.CurrentBranching);
608 ec.CurrentBranching.Return ();
612 protected override bool DoEmit (EmitContext ec)
615 Report.Error (157, loc, "Control can not leave the body of the finally block");
619 if (ec.ReturnType == null){
621 Report.Error (127, loc, "Return with a value not allowed here");
626 Report.Error (126, loc, "An object of type `" +
627 TypeManager.CSharpName (ec.ReturnType) + "' is " +
628 "expected for the return statement");
632 if (Expr.Type != ec.ReturnType)
633 Expr = Convert.ImplicitConversionRequired (
634 ec, Expr, ec.ReturnType, loc);
641 if (ec.InTry || ec.InCatch)
642 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
645 if (ec.InTry || ec.InCatch) {
646 if (!ec.HasReturnLabel) {
647 ec.ReturnLabel = ec.ig.DefineLabel ();
648 ec.HasReturnLabel = true;
650 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
652 ec.ig.Emit (OpCodes.Ret);
653 ec.NeedExplicitReturn = false;
660 public class Goto : Statement {
663 LabeledStatement label;
665 public override bool Resolve (EmitContext ec)
667 label = block.LookupLabel (target);
671 "No such label `" + target + "' in this scope");
675 // If this is a forward goto.
676 if (!label.IsDefined)
677 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
679 ec.CurrentBranching.Goto ();
684 public Goto (Block parent_block, string label, Location l)
686 block = parent_block;
691 public string Target {
697 protected override bool DoEmit (EmitContext ec)
699 Label l = label.LabelTarget (ec);
700 ec.ig.Emit (OpCodes.Br, l);
706 public class LabeledStatement : Statement {
707 public readonly Location Location;
715 public LabeledStatement (string label_name, Location l)
717 this.label_name = label_name;
721 public Label LabelTarget (EmitContext ec)
725 label = ec.ig.DefineLabel ();
731 public bool IsDefined {
737 public bool HasBeenReferenced {
743 public void AddUsageVector (FlowBranching.UsageVector vector)
746 vectors = new ArrayList ();
748 vectors.Add (vector.Clone ());
751 public override bool Resolve (EmitContext ec)
753 ec.CurrentBranching.Label (vectors);
760 protected override bool DoEmit (EmitContext ec)
763 ec.ig.MarkLabel (label);
771 /// `goto default' statement
773 public class GotoDefault : Statement {
775 public GotoDefault (Location l)
780 public override bool Resolve (EmitContext ec)
782 ec.CurrentBranching.Goto ();
786 protected override bool DoEmit (EmitContext ec)
788 if (ec.Switch == null){
789 Report.Error (153, loc, "goto default is only valid in a switch statement");
793 if (!ec.Switch.GotDefault){
794 Report.Error (159, loc, "No default target on switch statement");
797 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
803 /// `goto case' statement
805 public class GotoCase : Statement {
809 public GotoCase (Expression e, Location l)
815 public override bool Resolve (EmitContext ec)
817 if (ec.Switch == null){
818 Report.Error (153, loc, "goto case is only valid in a switch statement");
822 expr = expr.Resolve (ec);
826 if (!(expr is Constant)){
827 Report.Error (159, loc, "Target expression for goto case is not constant");
831 object val = Expression.ConvertIntLiteral (
832 (Constant) expr, ec.Switch.SwitchType, loc);
837 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
842 "No such label 'case " + val + "': for the goto case");
846 label = sl.ILLabelCode;
848 ec.CurrentBranching.Goto ();
852 protected override bool DoEmit (EmitContext ec)
854 ec.ig.Emit (OpCodes.Br, label);
859 public class Throw : Statement {
862 public Throw (Expression expr, Location l)
868 public override bool Resolve (EmitContext ec)
871 expr = expr.Resolve (ec);
875 ExprClass eclass = expr.eclass;
877 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
878 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
879 expr.Error_UnexpectedKind ("value, variable, property or indexer access ");
885 if ((t != TypeManager.exception_type) &&
886 !t.IsSubclassOf (TypeManager.exception_type) &&
887 !(expr is NullLiteral)) {
888 Report.Error (155, loc,
889 "The type caught or thrown must be derived " +
890 "from System.Exception");
895 ec.CurrentBranching.Throw ();
899 protected override bool DoEmit (EmitContext ec)
903 ec.ig.Emit (OpCodes.Rethrow);
907 "A throw statement with no argument is only " +
908 "allowed in a catch clause");
915 ec.ig.Emit (OpCodes.Throw);
921 public class Break : Statement {
923 public Break (Location l)
928 public override bool Resolve (EmitContext ec)
930 ec.CurrentBranching.MayLeaveLoop = true;
931 ec.CurrentBranching.Break ();
935 protected override bool DoEmit (EmitContext ec)
937 ILGenerator ig = ec.ig;
939 if (ec.InLoop == false && ec.Switch == null){
940 Report.Error (139, loc, "No enclosing loop or switch to continue to");
944 if (ec.InTry || ec.InCatch)
945 ig.Emit (OpCodes.Leave, ec.LoopEnd);
947 ig.Emit (OpCodes.Br, ec.LoopEnd);
953 public class Continue : Statement {
955 public Continue (Location l)
960 public override bool Resolve (EmitContext ec)
962 ec.CurrentBranching.Goto ();
966 protected override bool DoEmit (EmitContext ec)
968 Label begin = ec.LoopBegin;
971 Report.Error (139, loc, "No enclosing loop to continue to");
976 // UGH: Non trivial. This Br might cross a try/catch boundary
980 // try { ... } catch { continue; }
984 // try {} catch { while () { continue; }}
986 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
987 ec.ig.Emit (OpCodes.Leave, begin);
988 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
989 throw new Exception ("Should never happen");
991 ec.ig.Emit (OpCodes.Br, begin);
996 public class LocalInfo {
997 public Expression Type;
1000 // Most of the time a variable will be stored in a LocalBuilder
1002 // But sometimes, it will be stored in a field. The context of the field will
1003 // be stored in the EmitContext
1006 public LocalBuilder LocalBuilder;
1007 public FieldBuilder FieldBuilder;
1009 public Type VariableType;
1010 public readonly string Name;
1011 public readonly Location Location;
1012 public readonly Block Block;
1014 public VariableInfo VariableInfo;
1025 public LocalInfo (Expression type, string name, Block block, Location l)
1033 public LocalInfo (TypeContainer tc, Block block, Location l)
1035 VariableType = tc.TypeBuilder;
1040 public bool IsThisAssigned (EmitContext ec, Location loc)
1042 if (VariableInfo == null)
1043 throw new Exception ();
1045 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1048 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1051 public bool Resolve (DeclSpace decl)
1053 if (VariableType == null)
1054 VariableType = decl.ResolveType (Type, false, Location);
1056 if (VariableType == null)
1062 public void MakePinned ()
1064 TypeManager.MakePinned (LocalBuilder);
1065 flags |= Flags.Fixed;
1068 public bool IsFixed {
1070 if (((flags & Flags.Fixed) != 0) || TypeManager.IsValueType (VariableType))
1077 public override string ToString ()
1079 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1080 Name, Type, VariableInfo, Location);
1085 return (flags & Flags.Used) != 0;
1088 flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
1092 public bool Assigned {
1094 return (flags & Flags.Assigned) != 0;
1097 flags = value ? (flags | Flags.Assigned) : (flags & ~Flags.Assigned);
1101 public bool ReadOnly {
1103 return (flags & Flags.ReadOnly) != 0;
1106 flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
1115 /// Block represents a C# block.
1119 /// This class is used in a number of places: either to represent
1120 /// explicit blocks that the programmer places or implicit blocks.
1122 /// Implicit blocks are used as labels or to introduce variable
1125 public class Block : Statement {
1126 public readonly Block Parent;
1127 public readonly Location StartLocation;
1128 public Location EndLocation = Location.Null;
1131 public enum Flags : byte {
1135 VariablesInitialized = 8,
1140 public bool Implicit {
1142 return (flags & Flags.Implicit) != 0;
1146 public bool Unchecked {
1148 return (flags & Flags.Unchecked) != 0;
1151 flags |= Flags.Unchecked;
1156 // The statements in this block
1158 ArrayList statements;
1161 // An array of Blocks. We keep track of children just
1162 // to generate the local variable declarations.
1164 // Statements and child statements are handled through the
1170 // Labels. (label, block) pairs.
1175 // Keeps track of (name, type) pairs
1177 Hashtable variables;
1180 // Keeps track of constants
1181 Hashtable constants;
1184 // If this is a switch section, the enclosing switch block.
1192 public Block (Block parent)
1193 : this (parent, (Flags) 0, Location.Null, Location.Null)
1196 public Block (Block parent, Flags flags)
1197 : this (parent, flags, Location.Null, Location.Null)
1200 public Block (Block parent, Flags flags, Parameters parameters)
1201 : this (parent, flags, parameters, Location.Null, Location.Null)
1204 public Block (Block parent, Location start, Location end)
1205 : this (parent, (Flags) 0, start, end)
1208 public Block (Block parent, Parameters parameters, Location start, Location end)
1209 : this (parent, (Flags) 0, parameters, start, end)
1212 public Block (Block parent, Flags flags, Location start, Location end)
1213 : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
1216 public Block (Block parent, Flags flags, Parameters parameters,
1217 Location start, Location end)
1220 parent.AddChild (this);
1222 this.Parent = parent;
1224 this.parameters = parameters;
1225 this.StartLocation = start;
1226 this.EndLocation = end;
1229 statements = new ArrayList ();
1232 public Block CreateSwitchBlock (Location start)
1234 Block new_block = new Block (this, start, start);
1235 new_block.switch_block = this;
1245 void AddChild (Block b)
1247 if (children == null)
1248 children = new ArrayList ();
1253 public void SetEndLocation (Location loc)
1259 /// Adds a label to the current block.
1263 /// false if the name already exists in this block. true
1267 public bool AddLabel (string name, LabeledStatement target)
1269 if (switch_block != null)
1270 return switch_block.AddLabel (name, target);
1273 labels = new Hashtable ();
1274 if (labels.Contains (name))
1277 labels.Add (name, target);
1281 public LabeledStatement LookupLabel (string name)
1283 if (switch_block != null)
1284 return switch_block.LookupLabel (name);
1286 if (labels != null){
1287 if (labels.Contains (name))
1288 return ((LabeledStatement) labels [name]);
1292 return Parent.LookupLabel (name);
1297 LocalInfo this_variable = null;
1300 // Returns the "this" instance variable of this block.
1301 // See AddThisVariable() for more information.
1303 public LocalInfo ThisVariable {
1305 if (this_variable != null)
1306 return this_variable;
1307 else if (Parent != null)
1308 return Parent.ThisVariable;
1314 Hashtable child_variable_names;
1317 // Marks a variable with name @name as being used in a child block.
1318 // If a variable name has been used in a child block, it's illegal to
1319 // declare a variable with the same name in the current block.
1321 public void AddChildVariableName (string name)
1323 if (child_variable_names == null)
1324 child_variable_names = new Hashtable ();
1326 if (!child_variable_names.Contains (name))
1327 child_variable_names.Add (name, true);
1331 // Marks all variables from block @block and all its children as being
1332 // used in a child block.
1334 public void AddChildVariableNames (Block block)
1336 if (block.Variables != null) {
1337 foreach (string name in block.Variables.Keys)
1338 AddChildVariableName (name);
1341 if (block.children != null) {
1342 foreach (Block child in block.children)
1343 AddChildVariableNames (child);
1346 if (block.child_variable_names != null) {
1347 foreach (string name in block.child_variable_names.Keys)
1348 AddChildVariableName (name);
1353 // Checks whether a variable name has already been used in a child block.
1355 public bool IsVariableNameUsedInChildBlock (string name)
1357 if (child_variable_names == null)
1360 return child_variable_names.Contains (name);
1364 // This is used by non-static `struct' constructors which do not have an
1365 // initializer - in this case, the constructor must initialize all of the
1366 // struct's fields. To do this, we add a "this" variable and use the flow
1367 // analysis code to ensure that it's been fully initialized before control
1368 // leaves the constructor.
1370 public LocalInfo AddThisVariable (TypeContainer tc, Location l)
1372 if (this_variable != null)
1373 return this_variable;
1375 if (variables == null)
1376 variables = new Hashtable ();
1378 this_variable = new LocalInfo (tc, this, l);
1380 variables.Add ("this", this_variable);
1382 return this_variable;
1385 public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
1387 if (variables == null)
1388 variables = new Hashtable ();
1390 LocalInfo vi = GetLocalInfo (name);
1392 if (vi.Block != this)
1393 Report.Error (136, l, "A local variable named `" + name + "' " +
1394 "cannot be declared in this scope since it would " +
1395 "give a different meaning to `" + name + "', which " +
1396 "is already used in a `parent or current' scope to " +
1397 "denote something else");
1399 Report.Error (128, l, "A local variable `" + name + "' is already " +
1400 "defined in this scope");
1404 if (IsVariableNameUsedInChildBlock (name)) {
1405 Report.Error (136, l, "A local variable named `" + name + "' " +
1406 "cannot be declared in this scope since it would " +
1407 "give a different meaning to `" + name + "', which " +
1408 "is already used in a `child' scope to denote something " +
1415 Parameter p = pars.GetParameterByName (name, out idx);
1417 Report.Error (136, l, "A local variable named `" + name + "' " +
1418 "cannot be declared in this scope since it would " +
1419 "give a different meaning to `" + name + "', which " +
1420 "is already used in a `parent or current' scope to " +
1421 "denote something else");
1426 vi = new LocalInfo (type, name, this, l);
1428 variables.Add (name, vi);
1430 if ((flags & Flags.VariablesInitialized) != 0)
1431 throw new Exception ();
1433 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1437 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1439 if (AddVariable (type, name, pars, l) == null)
1442 if (constants == null)
1443 constants = new Hashtable ();
1445 constants.Add (name, value);
1449 public Hashtable Variables {
1455 public LocalInfo GetLocalInfo (string name)
1457 if (variables != null) {
1459 temp = variables [name];
1462 return (LocalInfo) temp;
1467 return Parent.GetLocalInfo (name);
1472 public Expression GetVariableType (string name)
1474 LocalInfo vi = GetLocalInfo (name);
1482 public Expression GetConstantExpression (string name)
1484 if (constants != null) {
1486 temp = constants [name];
1489 return (Expression) temp;
1493 return Parent.GetConstantExpression (name);
1499 /// True if the variable named @name is a constant
1501 public bool IsConstant (string name)
1503 Expression e = null;
1505 e = GetConstantExpression (name);
1511 /// Use to fetch the statement associated with this label
1513 public Statement this [string name] {
1515 return (Statement) labels [name];
1519 Parameters parameters = null;
1520 public Parameters Parameters {
1523 return Parent.Parameters;
1530 /// A list of labels that were not used within this block
1532 public string [] GetUnreferenced ()
1534 // FIXME: Implement me
1538 public void AddStatement (Statement s)
1541 flags |= Flags.BlockUsed;
1546 return (flags & Flags.BlockUsed) != 0;
1552 flags |= Flags.BlockUsed;
1555 VariableMap param_map, local_map;
1557 public VariableMap ParameterMap {
1559 if ((flags & Flags.VariablesInitialized) == 0)
1560 throw new Exception ();
1566 public VariableMap LocalMap {
1568 if ((flags & Flags.VariablesInitialized) == 0)
1569 throw new Exception ();
1575 public bool LiftVariable (LocalInfo local_info)
1581 /// Emits the variable declarations and labels.
1584 /// tc: is our typecontainer (to resolve type references)
1585 /// ig: is the code generator:
1587 public void EmitMeta (EmitContext ec, InternalParameters ip)
1589 DeclSpace ds = ec.DeclSpace;
1590 ILGenerator ig = ec.ig;
1593 // Compute the VariableMap's.
1595 // Unfortunately, we don't know the type when adding variables with
1596 // AddVariable(), so we need to compute this info here.
1600 if (variables != null) {
1601 foreach (LocalInfo li in variables.Values)
1602 li.Resolve (ec.DeclSpace);
1604 locals = new LocalInfo [variables.Count];
1605 variables.Values.CopyTo (locals, 0);
1607 locals = new LocalInfo [0];
1610 local_map = new VariableMap (Parent.LocalMap, locals);
1612 local_map = new VariableMap (locals);
1614 param_map = new VariableMap (ip);
1615 flags |= Flags.VariablesInitialized;
1617 bool old_check_state = ec.ConstantCheckState;
1618 ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
1619 bool remap_locals = ec.RemapToProxy;
1622 // Process this block variables
1624 if (variables != null){
1625 foreach (DictionaryEntry de in variables){
1626 string name = (string) de.Key;
1627 LocalInfo vi = (LocalInfo) de.Value;
1629 if (vi.VariableType == null)
1632 Type variable_type = vi.VariableType;
1634 if (variable_type.IsPointer){
1636 // Am not really convinced that this test is required (Microsoft does it)
1637 // but the fact is that you would not be able to use the pointer variable
1640 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1646 vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
1648 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1650 if (constants == null)
1653 Expression cv = (Expression) constants [name];
1657 ec.CurrentBlock = this;
1658 Expression e = cv.Resolve (ec);
1662 if (!(e is Constant)){
1663 Report.Error (133, vi.Location,
1664 "The expression being assigned to `" +
1665 name + "' must be constant (" + e + ")");
1669 constants.Remove (name);
1670 constants.Add (name, e);
1673 ec.ConstantCheckState = old_check_state;
1676 // Now, handle the children
1678 if (children != null){
1679 foreach (Block b in children)
1680 b.EmitMeta (ec, ip);
1684 public void UsageWarning ()
1688 if (variables != null){
1689 foreach (DictionaryEntry de in variables){
1690 LocalInfo vi = (LocalInfo) de.Value;
1695 name = (string) de.Key;
1699 219, vi.Location, "The variable `" + name +
1700 "' is assigned but its value is never used");
1703 168, vi.Location, "The variable `" +
1705 "' is declared but never used");
1710 if (children != null)
1711 foreach (Block b in children)
1715 public override bool Resolve (EmitContext ec)
1717 Block prev_block = ec.CurrentBlock;
1720 ec.CurrentBlock = this;
1721 ec.StartFlowBranching (this);
1723 Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1725 ArrayList new_statements = new ArrayList ();
1726 bool unreachable = false, warning_shown = false;
1728 foreach (Statement s in statements){
1729 if (unreachable && !(s is LabeledStatement)) {
1730 if (!warning_shown && !(s is EmptyStatement)) {
1731 warning_shown = true;
1732 Warning_DeadCodeFound (s.loc);
1738 if (s.Resolve (ec) == false) {
1743 if (s is LabeledStatement)
1744 unreachable = false;
1746 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
1748 new_statements.Add (s);
1751 statements = new_statements;
1753 Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
1755 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
1756 ec.CurrentBlock = prev_block;
1758 // If we're a non-static `struct' constructor which doesn't have an
1759 // initializer, then we must initialize all of the struct's fields.
1760 if ((this_variable != null) && (returns != FlowBranching.FlowReturns.Exception) &&
1761 !this_variable.IsThisAssigned (ec, loc))
1764 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
1765 foreach (LabeledStatement label in labels.Values)
1766 if (!label.HasBeenReferenced)
1767 Report.Warning (164, label.Location,
1768 "This label has not been referenced");
1771 Report.Debug (1, "RESOLVE BLOCK DONE #2", StartLocation, returns);
1773 if ((returns == FlowBranching.FlowReturns.Always) ||
1774 (returns == FlowBranching.FlowReturns.Exception) ||
1775 (returns == FlowBranching.FlowReturns.Unreachable))
1776 flags |= Flags.HasRet;
1781 protected override bool DoEmit (EmitContext ec)
1783 foreach (Statement s in statements)
1786 return (flags & Flags.HasRet) != 0;
1789 public override bool Emit (EmitContext ec)
1791 Block prev_block = ec.CurrentBlock;
1793 ec.CurrentBlock = this;
1795 bool emit_debug_info = (CodeGen.SymbolWriter != null);
1796 bool is_lexical_block = !Implicit && (Parent != null);
1798 if (emit_debug_info) {
1799 if (is_lexical_block)
1800 ec.ig.BeginScope ();
1802 if (variables != null) {
1803 foreach (DictionaryEntry de in variables) {
1804 string name = (string) de.Key;
1805 LocalInfo vi = (LocalInfo) de.Value;
1807 if (vi.LocalBuilder == null)
1810 vi.LocalBuilder.SetLocalSymInfo (name);
1815 ec.Mark (StartLocation, true);
1816 bool retval = DoEmit (ec);
1817 ec.Mark (EndLocation, true);
1819 if (emit_debug_info && is_lexical_block)
1822 ec.CurrentBlock = prev_block;
1828 public class SwitchLabel {
1831 public Location loc;
1832 public Label ILLabel;
1833 public Label ILLabelCode;
1836 // if expr == null, then it is the default case.
1838 public SwitchLabel (Expression expr, Location l)
1844 public Expression Label {
1850 public object Converted {
1857 // Resolves the expression, reduces it to a literal if possible
1858 // and then converts it to the requested type.
1860 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1862 ILLabel = ec.ig.DefineLabel ();
1863 ILLabelCode = ec.ig.DefineLabel ();
1868 Expression e = label.Resolve (ec);
1873 if (!(e is Constant)){
1874 Report.Error (150, loc, "A constant value is expected, got: " + e);
1878 if (e is StringConstant || e is NullLiteral){
1879 if (required_type == TypeManager.string_type){
1881 ILLabel = ec.ig.DefineLabel ();
1886 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1887 if (converted == null)
1894 public class SwitchSection {
1895 // An array of SwitchLabels.
1896 public readonly ArrayList Labels;
1897 public readonly Block Block;
1899 public SwitchSection (ArrayList labels, Block block)
1906 public class Switch : Statement {
1907 public readonly ArrayList Sections;
1908 public Expression Expr;
1911 /// Maps constants whose type type SwitchType to their SwitchLabels.
1913 public Hashtable Elements;
1916 /// The governing switch type
1918 public Type SwitchType;
1924 Label default_target;
1925 Expression new_expr;
1928 // The types allowed to be implicitly cast from
1929 // on the governing type
1931 static Type [] allowed_types;
1933 public Switch (Expression e, ArrayList sects, Location l)
1940 public bool GotDefault {
1946 public Label DefaultTarget {
1948 return default_target;
1953 // Determines the governing type for a switch. The returned
1954 // expression might be the expression from the switch, or an
1955 // expression that includes any potential conversions to the
1956 // integral types or to string.
1958 Expression SwitchGoverningType (EmitContext ec, Type t)
1960 if (t == TypeManager.int32_type ||
1961 t == TypeManager.uint32_type ||
1962 t == TypeManager.char_type ||
1963 t == TypeManager.byte_type ||
1964 t == TypeManager.sbyte_type ||
1965 t == TypeManager.ushort_type ||
1966 t == TypeManager.short_type ||
1967 t == TypeManager.uint64_type ||
1968 t == TypeManager.int64_type ||
1969 t == TypeManager.string_type ||
1970 t == TypeManager.bool_type ||
1971 t.IsSubclassOf (TypeManager.enum_type))
1974 if (allowed_types == null){
1975 allowed_types = new Type [] {
1976 TypeManager.sbyte_type,
1977 TypeManager.byte_type,
1978 TypeManager.short_type,
1979 TypeManager.ushort_type,
1980 TypeManager.int32_type,
1981 TypeManager.uint32_type,
1982 TypeManager.int64_type,
1983 TypeManager.uint64_type,
1984 TypeManager.char_type,
1985 TypeManager.bool_type,
1986 TypeManager.string_type
1991 // Try to find a *user* defined implicit conversion.
1993 // If there is no implicit conversion, or if there are multiple
1994 // conversions, we have to report an error
1996 Expression converted = null;
1997 foreach (Type tt in allowed_types){
2000 e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
2004 if (converted != null){
2005 Report.Error (-12, loc, "More than one conversion to an integral " +
2006 " type exists for type `" +
2007 TypeManager.CSharpName (Expr.Type)+"'");
2015 void error152 (string n)
2018 152, "The label `" + n + ":' " +
2019 "is already present on this switch statement");
2023 // Performs the basic sanity checks on the switch statement
2024 // (looks for duplicate keys and non-constant expressions).
2026 // It also returns a hashtable with the keys that we will later
2027 // use to compute the switch tables
2029 bool CheckSwitch (EmitContext ec)
2033 Elements = new Hashtable ();
2035 got_default = false;
2037 if (TypeManager.IsEnumType (SwitchType)){
2038 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2040 compare_type = SwitchType;
2042 foreach (SwitchSection ss in Sections){
2043 foreach (SwitchLabel sl in ss.Labels){
2044 if (!sl.ResolveAndReduce (ec, SwitchType)){
2049 if (sl.Label == null){
2051 error152 ("default");
2058 object key = sl.Converted;
2060 if (key is Constant)
2061 key = ((Constant) key).GetValue ();
2064 key = NullLiteral.Null;
2066 string lname = null;
2067 if (compare_type == TypeManager.uint64_type){
2068 ulong v = (ulong) key;
2070 if (Elements.Contains (v))
2071 lname = v.ToString ();
2073 Elements.Add (v, sl);
2074 } else if (compare_type == TypeManager.int64_type){
2075 long v = (long) key;
2077 if (Elements.Contains (v))
2078 lname = v.ToString ();
2080 Elements.Add (v, sl);
2081 } else if (compare_type == TypeManager.uint32_type){
2082 uint v = (uint) key;
2084 if (Elements.Contains (v))
2085 lname = v.ToString ();
2087 Elements.Add (v, sl);
2088 } else if (compare_type == TypeManager.char_type){
2089 char v = (char) key;
2091 if (Elements.Contains (v))
2092 lname = v.ToString ();
2094 Elements.Add (v, sl);
2095 } else if (compare_type == TypeManager.byte_type){
2096 byte v = (byte) key;
2098 if (Elements.Contains (v))
2099 lname = v.ToString ();
2101 Elements.Add (v, sl);
2102 } else if (compare_type == TypeManager.sbyte_type){
2103 sbyte v = (sbyte) key;
2105 if (Elements.Contains (v))
2106 lname = v.ToString ();
2108 Elements.Add (v, sl);
2109 } else if (compare_type == TypeManager.short_type){
2110 short v = (short) key;
2112 if (Elements.Contains (v))
2113 lname = v.ToString ();
2115 Elements.Add (v, sl);
2116 } else if (compare_type == TypeManager.ushort_type){
2117 ushort v = (ushort) key;
2119 if (Elements.Contains (v))
2120 lname = v.ToString ();
2122 Elements.Add (v, sl);
2123 } else if (compare_type == TypeManager.string_type){
2124 if (key is NullLiteral){
2125 if (Elements.Contains (NullLiteral.Null))
2128 Elements.Add (NullLiteral.Null, null);
2130 string s = (string) key;
2132 if (Elements.Contains (s))
2135 Elements.Add (s, sl);
2137 } else if (compare_type == TypeManager.int32_type) {
2140 if (Elements.Contains (v))
2141 lname = v.ToString ();
2143 Elements.Add (v, sl);
2144 } else if (compare_type == TypeManager.bool_type) {
2145 bool v = (bool) key;
2147 if (Elements.Contains (v))
2148 lname = v.ToString ();
2150 Elements.Add (v, sl);
2154 throw new Exception ("Unknown switch type!" +
2155 SwitchType + " " + compare_type);
2159 error152 ("case + " + lname);
2170 void EmitObjectInteger (ILGenerator ig, object k)
2173 IntConstant.EmitInt (ig, (int) k);
2174 else if (k is Constant) {
2175 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2178 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2181 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2183 IntConstant.EmitInt (ig, (int) (long) k);
2184 ig.Emit (OpCodes.Conv_I8);
2187 LongConstant.EmitLong (ig, (long) k);
2189 else if (k is ulong)
2191 if ((ulong) k < (1L<<32))
2193 IntConstant.EmitInt (ig, (int) (long) k);
2194 ig.Emit (OpCodes.Conv_U8);
2198 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2202 IntConstant.EmitInt (ig, (int) ((char) k));
2203 else if (k is sbyte)
2204 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2206 IntConstant.EmitInt (ig, (int) ((byte) k));
2207 else if (k is short)
2208 IntConstant.EmitInt (ig, (int) ((short) k));
2209 else if (k is ushort)
2210 IntConstant.EmitInt (ig, (int) ((ushort) k));
2212 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2214 throw new Exception ("Unhandled case");
2217 // structure used to hold blocks of keys while calculating table switch
2218 class KeyBlock : IComparable
2220 public KeyBlock (long _nFirst)
2222 nFirst = nLast = _nFirst;
2226 public ArrayList rgKeys = null;
2229 get { return (int) (nLast - nFirst + 1); }
2231 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2233 return kbLast.nLast - kbFirst.nFirst + 1;
2235 public int CompareTo (object obj)
2237 KeyBlock kb = (KeyBlock) obj;
2238 int nLength = Length;
2239 int nLengthOther = kb.Length;
2240 if (nLengthOther == nLength)
2241 return (int) (kb.nFirst - nFirst);
2242 return nLength - nLengthOther;
2247 /// This method emits code for a lookup-based switch statement (non-string)
2248 /// Basically it groups the cases into blocks that are at least half full,
2249 /// and then spits out individual lookup opcodes for each block.
2250 /// It emits the longest blocks first, and short blocks are just
2251 /// handled with direct compares.
2253 /// <param name="ec"></param>
2254 /// <param name="val"></param>
2255 /// <returns></returns>
2256 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
2258 int cElements = Elements.Count;
2259 object [] rgKeys = new object [cElements];
2260 Elements.Keys.CopyTo (rgKeys, 0);
2261 Array.Sort (rgKeys);
2263 // initialize the block list with one element per key
2264 ArrayList rgKeyBlocks = new ArrayList ();
2265 foreach (object key in rgKeys)
2266 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
2269 // iteratively merge the blocks while they are at least half full
2270 // there's probably a really cool way to do this with a tree...
2271 while (rgKeyBlocks.Count > 1)
2273 ArrayList rgKeyBlocksNew = new ArrayList ();
2274 kbCurr = (KeyBlock) rgKeyBlocks [0];
2275 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2277 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2278 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
2281 kbCurr.nLast = kb.nLast;
2285 // start a new block
2286 rgKeyBlocksNew.Add (kbCurr);
2290 rgKeyBlocksNew.Add (kbCurr);
2291 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2293 rgKeyBlocks = rgKeyBlocksNew;
2296 // initialize the key lists
2297 foreach (KeyBlock kb in rgKeyBlocks)
2298 kb.rgKeys = new ArrayList ();
2300 // fill the key lists
2302 if (rgKeyBlocks.Count > 0) {
2303 kbCurr = (KeyBlock) rgKeyBlocks [0];
2304 foreach (object key in rgKeys)
2306 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
2307 System.Convert.ToInt64 (key) > kbCurr.nLast;
2309 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2310 kbCurr.rgKeys.Add (key);
2314 // sort the blocks so we can tackle the largest ones first
2315 rgKeyBlocks.Sort ();
2317 // okay now we can start...
2318 ILGenerator ig = ec.ig;
2319 Label lblEnd = ig.DefineLabel (); // at the end ;-)
2320 Label lblDefault = ig.DefineLabel ();
2322 Type typeKeys = null;
2323 if (rgKeys.Length > 0)
2324 typeKeys = rgKeys [0].GetType (); // used for conversions
2326 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2328 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2329 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2332 foreach (object key in kb.rgKeys)
2334 ig.Emit (OpCodes.Ldloc, val);
2335 EmitObjectInteger (ig, key);
2336 SwitchLabel sl = (SwitchLabel) Elements [key];
2337 ig.Emit (OpCodes.Beq, sl.ILLabel);
2342 // TODO: if all the keys in the block are the same and there are
2343 // no gaps/defaults then just use a range-check.
2344 if (SwitchType == TypeManager.int64_type ||
2345 SwitchType == TypeManager.uint64_type)
2347 // TODO: optimize constant/I4 cases
2349 // check block range (could be > 2^31)
2350 ig.Emit (OpCodes.Ldloc, val);
2351 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2352 ig.Emit (OpCodes.Blt, lblDefault);
2353 ig.Emit (OpCodes.Ldloc, val);
2354 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2355 ig.Emit (OpCodes.Bgt, lblDefault);
2358 ig.Emit (OpCodes.Ldloc, val);
2361 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2362 ig.Emit (OpCodes.Sub);
2364 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
2369 ig.Emit (OpCodes.Ldloc, val);
2370 int nFirst = (int) kb.nFirst;
2373 IntConstant.EmitInt (ig, nFirst);
2374 ig.Emit (OpCodes.Sub);
2376 else if (nFirst < 0)
2378 IntConstant.EmitInt (ig, -nFirst);
2379 ig.Emit (OpCodes.Add);
2383 // first, build the list of labels for the switch
2385 int cJumps = kb.Length;
2386 Label [] rgLabels = new Label [cJumps];
2387 for (int iJump = 0; iJump < cJumps; iJump++)
2389 object key = kb.rgKeys [iKey];
2390 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
2392 SwitchLabel sl = (SwitchLabel) Elements [key];
2393 rgLabels [iJump] = sl.ILLabel;
2397 rgLabels [iJump] = lblDefault;
2399 // emit the switch opcode
2400 ig.Emit (OpCodes.Switch, rgLabels);
2403 // mark the default for this block
2405 ig.MarkLabel (lblDefault);
2408 // TODO: find the default case and emit it here,
2409 // to prevent having to do the following jump.
2410 // make sure to mark other labels in the default section
2412 // the last default just goes to the end
2413 ig.Emit (OpCodes.Br, lblDefault);
2415 // now emit the code for the sections
2416 bool fFoundDefault = false;
2417 bool fAllReturn = true;
2418 foreach (SwitchSection ss in Sections)
2420 foreach (SwitchLabel sl in ss.Labels)
2422 ig.MarkLabel (sl.ILLabel);
2423 ig.MarkLabel (sl.ILLabelCode);
2424 if (sl.Label == null)
2426 ig.MarkLabel (lblDefault);
2427 fFoundDefault = true;
2430 bool returns = ss.Block.Emit (ec);
2431 fAllReturn &= returns;
2432 //ig.Emit (OpCodes.Br, lblEnd);
2435 if (!fFoundDefault) {
2436 ig.MarkLabel (lblDefault);
2439 ig.MarkLabel (lblEnd);
2444 // This simple emit switch works, but does not take advantage of the
2446 // TODO: remove non-string logic from here
2447 // TODO: binary search strings?
2449 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
2451 ILGenerator ig = ec.ig;
2452 Label end_of_switch = ig.DefineLabel ();
2453 Label next_test = ig.DefineLabel ();
2454 Label null_target = ig.DefineLabel ();
2455 bool default_found = false;
2456 bool first_test = true;
2457 bool pending_goto_end = false;
2458 bool all_return = true;
2461 ig.Emit (OpCodes.Ldloc, val);
2463 if (Elements.Contains (NullLiteral.Null)){
2464 ig.Emit (OpCodes.Brfalse, null_target);
2466 ig.Emit (OpCodes.Brfalse, default_target);
2468 ig.Emit (OpCodes.Ldloc, val);
2469 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
2470 ig.Emit (OpCodes.Stloc, val);
2472 int section_count = Sections.Count;
2473 for (int section = 0; section < section_count; section++){
2474 SwitchSection ss = (SwitchSection) Sections [section];
2475 Label sec_begin = ig.DefineLabel ();
2477 if (pending_goto_end)
2478 ig.Emit (OpCodes.Br, end_of_switch);
2480 int label_count = ss.Labels.Count;
2482 for (int label = 0; label < label_count; label++){
2483 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
2484 ig.MarkLabel (sl.ILLabel);
2487 ig.MarkLabel (next_test);
2488 next_test = ig.DefineLabel ();
2491 // If we are the default target
2493 if (sl.Label == null){
2494 ig.MarkLabel (default_target);
2495 default_found = true;
2497 object lit = sl.Converted;
2499 if (lit is NullLiteral){
2501 if (label_count == 1)
2502 ig.Emit (OpCodes.Br, next_test);
2506 StringConstant str = (StringConstant) lit;
2508 ig.Emit (OpCodes.Ldloc, val);
2509 ig.Emit (OpCodes.Ldstr, str.Value);
2510 if (label_count == 1)
2511 ig.Emit (OpCodes.Bne_Un, next_test);
2513 if (label+1 == label_count)
2514 ig.Emit (OpCodes.Bne_Un, next_test);
2516 ig.Emit (OpCodes.Beq, sec_begin);
2521 ig.MarkLabel (null_target);
2522 ig.MarkLabel (sec_begin);
2523 foreach (SwitchLabel sl in ss.Labels)
2524 ig.MarkLabel (sl.ILLabelCode);
2526 bool returns = ss.Block.Emit (ec);
2528 pending_goto_end = false;
2531 pending_goto_end = true;
2535 if (!default_found){
2536 ig.MarkLabel (default_target);
2539 ig.MarkLabel (next_test);
2540 ig.MarkLabel (end_of_switch);
2545 public override bool Resolve (EmitContext ec)
2547 Expr = Expr.Resolve (ec);
2551 new_expr = SwitchGoverningType (ec, Expr.Type);
2552 if (new_expr == null){
2553 Report.Error (151, loc, "An integer type or string was expected for switch");
2558 SwitchType = new_expr.Type;
2560 if (!CheckSwitch (ec))
2563 Switch old_switch = ec.Switch;
2565 ec.Switch.SwitchType = SwitchType;
2567 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
2570 foreach (SwitchSection ss in Sections){
2572 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.SwitchSection);
2576 if (ss.Block.Resolve (ec) != true)
2582 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.SwitchSection);
2584 ec.EndFlowBranching ();
2585 ec.Switch = old_switch;
2590 protected override bool DoEmit (EmitContext ec)
2592 // Store variable for comparission purposes
2593 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2595 ec.ig.Emit (OpCodes.Stloc, value);
2597 ILGenerator ig = ec.ig;
2599 default_target = ig.DefineLabel ();
2602 // Setup the codegen context
2604 Label old_end = ec.LoopEnd;
2605 Switch old_switch = ec.Switch;
2607 ec.LoopEnd = ig.DefineLabel ();
2612 if (SwitchType == TypeManager.string_type)
2613 all_return = SimpleSwitchEmit (ec, value);
2615 all_return = TableSwitchEmit (ec, value);
2617 // Restore context state.
2618 ig.MarkLabel (ec.LoopEnd);
2621 // Restore the previous context
2623 ec.LoopEnd = old_end;
2624 ec.Switch = old_switch;
2630 public class Lock : Statement {
2632 Statement Statement;
2634 public Lock (Expression expr, Statement stmt, Location l)
2641 public override bool Resolve (EmitContext ec)
2643 expr = expr.Resolve (ec);
2644 return Statement.Resolve (ec) && expr != null;
2647 protected override bool DoEmit (EmitContext ec)
2649 Type type = expr.Type;
2652 if (type.IsValueType){
2653 Report.Error (185, loc, "lock statement requires the expression to be " +
2654 " a reference type (type is: `" +
2655 TypeManager.CSharpName (type) + "'");
2659 ILGenerator ig = ec.ig;
2660 LocalBuilder temp = ig.DeclareLocal (type);
2663 ig.Emit (OpCodes.Dup);
2664 ig.Emit (OpCodes.Stloc, temp);
2665 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2668 Label end = ig.BeginExceptionBlock ();
2669 bool old_in_try = ec.InTry;
2671 Label finish = ig.DefineLabel ();
2672 val = Statement.Emit (ec);
2673 ec.InTry = old_in_try;
2674 // ig.Emit (OpCodes.Leave, finish);
2676 ig.MarkLabel (finish);
2679 ig.BeginFinallyBlock ();
2680 ig.Emit (OpCodes.Ldloc, temp);
2681 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2682 ig.EndExceptionBlock ();
2688 public class Unchecked : Statement {
2689 public readonly Block Block;
2691 public Unchecked (Block b)
2697 public override bool Resolve (EmitContext ec)
2699 bool previous_state = ec.CheckState;
2700 bool previous_state_const = ec.ConstantCheckState;
2702 ec.CheckState = false;
2703 ec.ConstantCheckState = false;
2704 bool ret = Block.Resolve (ec);
2705 ec.CheckState = previous_state;
2706 ec.ConstantCheckState = previous_state_const;
2711 protected override bool DoEmit (EmitContext ec)
2713 bool previous_state = ec.CheckState;
2714 bool previous_state_const = ec.ConstantCheckState;
2717 ec.CheckState = false;
2718 ec.ConstantCheckState = false;
2719 val = Block.Emit (ec);
2720 ec.CheckState = previous_state;
2721 ec.ConstantCheckState = previous_state_const;
2727 public class Checked : Statement {
2728 public readonly Block Block;
2730 public Checked (Block b)
2733 b.Unchecked = false;
2736 public override bool Resolve (EmitContext ec)
2738 bool previous_state = ec.CheckState;
2739 bool previous_state_const = ec.ConstantCheckState;
2741 ec.CheckState = true;
2742 ec.ConstantCheckState = true;
2743 bool ret = Block.Resolve (ec);
2744 ec.CheckState = previous_state;
2745 ec.ConstantCheckState = previous_state_const;
2750 protected override bool DoEmit (EmitContext ec)
2752 bool previous_state = ec.CheckState;
2753 bool previous_state_const = ec.ConstantCheckState;
2756 ec.CheckState = true;
2757 ec.ConstantCheckState = true;
2758 val = Block.Emit (ec);
2759 ec.CheckState = previous_state;
2760 ec.ConstantCheckState = previous_state_const;
2766 public class Unsafe : Statement {
2767 public readonly Block Block;
2769 public Unsafe (Block b)
2774 public override bool Resolve (EmitContext ec)
2776 bool previous_state = ec.InUnsafe;
2780 val = Block.Resolve (ec);
2781 ec.InUnsafe = previous_state;
2786 protected override bool DoEmit (EmitContext ec)
2788 bool previous_state = ec.InUnsafe;
2792 val = Block.Emit (ec);
2793 ec.InUnsafe = previous_state;
2802 public class Fixed : Statement {
2804 ArrayList declarators;
2805 Statement statement;
2810 public bool is_object;
2811 public LocalInfo vi;
2812 public Expression expr;
2813 public Expression converted;
2816 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
2819 declarators = decls;
2824 public override bool Resolve (EmitContext ec)
2827 Expression.UnsafeError (loc);
2831 expr_type = ec.DeclSpace.ResolveType (type, false, loc);
2832 if (expr_type == null)
2835 if (ec.RemapToProxy){
2836 Report.Error (-210, loc, "Fixed statement not allowed in iterators");
2840 data = new FixedData [declarators.Count];
2842 if (!expr_type.IsPointer){
2843 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
2848 foreach (Pair p in declarators){
2849 LocalInfo vi = (LocalInfo) p.First;
2850 Expression e = (Expression) p.Second;
2852 vi.VariableInfo = null;
2856 // The rules for the possible declarators are pretty wise,
2857 // but the production on the grammar is more concise.
2859 // So we have to enforce these rules here.
2861 // We do not resolve before doing the case 1 test,
2862 // because the grammar is explicit in that the token &
2863 // is present, so we need to test for this particular case.
2867 // Case 1: & object.
2869 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2870 Expression child = ((Unary) e).Expr;
2873 if (child is ParameterReference || child is LocalVariableReference){
2876 "No need to use fixed statement for parameters or " +
2877 "local variable declarations (address is already " +
2882 ec.InFixedInitializer = true;
2884 ec.InFixedInitializer = false;
2888 child = ((Unary) e).Expr;
2890 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2893 data [i].is_object = true;
2895 data [i].converted = null;
2902 ec.InFixedInitializer = true;
2904 ec.InFixedInitializer = false;
2911 if (e.Type.IsArray){
2912 Type array_type = TypeManager.GetElementType (e.Type);
2916 // Provided that array_type is unmanaged,
2918 if (!TypeManager.VerifyUnManaged (array_type, loc))
2922 // and T* is implicitly convertible to the
2923 // pointer type given in the fixed statement.
2925 ArrayPtr array_ptr = new ArrayPtr (e, loc);
2927 Expression converted = Convert.ImplicitConversionRequired (
2928 ec, array_ptr, vi.VariableType, loc);
2929 if (converted == null)
2932 data [i].is_object = false;
2934 data [i].converted = converted;
2944 if (e.Type == TypeManager.string_type){
2945 data [i].is_object = false;
2947 data [i].converted = null;
2953 return statement.Resolve (ec);
2956 protected override bool DoEmit (EmitContext ec)
2958 ILGenerator ig = ec.ig;
2960 bool is_ret = false;
2961 LocalBuilder [] clear_list = new LocalBuilder [data.Length];
2963 for (int i = 0; i < data.Length; i++) {
2964 LocalInfo vi = data [i].vi;
2967 // Case 1: & object.
2969 if (data [i].is_object) {
2971 // Store pointer in pinned location
2973 data [i].expr.Emit (ec);
2974 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2975 clear_list [i] = vi.LocalBuilder;
2982 if (data [i].expr.Type.IsArray){
2984 // Store pointer in pinned location
2986 data [i].converted.Emit (ec);
2988 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2989 clear_list [i] = vi.LocalBuilder;
2996 if (data [i].expr.Type == TypeManager.string_type){
2997 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2998 TypeManager.MakePinned (pinned_string);
2999 clear_list [i] = pinned_string;
3001 data [i].expr.Emit (ec);
3002 ig.Emit (OpCodes.Stloc, pinned_string);
3004 Expression sptr = new StringPtr (pinned_string, loc);
3005 Expression converted = Convert.ImplicitConversionRequired (
3006 ec, sptr, vi.VariableType, loc);
3008 if (converted == null)
3011 converted.Emit (ec);
3012 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3016 is_ret = statement.Emit (ec);
3021 // Clear the pinned variable
3023 for (int i = 0; i < data.Length; i++) {
3024 LocalInfo vi = data [i].vi;
3026 if (data [i].is_object || data [i].expr.Type.IsArray) {
3027 ig.Emit (OpCodes.Ldc_I4_0);
3028 ig.Emit (OpCodes.Conv_U);
3029 ig.Emit (OpCodes.Stloc, clear_list [i]);
3030 } else if (data [i].expr.Type == TypeManager.string_type){
3031 ig.Emit (OpCodes.Ldnull);
3032 ig.Emit (OpCodes.Stloc, clear_list [i]);
3040 public class Catch {
3041 public readonly string Name;
3042 public readonly Block Block;
3043 public readonly Location Location;
3045 Expression type_expr;
3048 public Catch (Expression type, string name, Block block, Location l)
3056 public Type CatchType {
3062 public bool IsGeneral {
3064 return type_expr == null;
3068 public bool Resolve (EmitContext ec)
3070 if (type_expr != null) {
3071 type = ec.DeclSpace.ResolveType (type_expr, false, Location);
3075 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3076 Report.Error (155, Location,
3077 "The type caught or thrown must be derived " +
3078 "from System.Exception");
3084 if (!Block.Resolve (ec))
3091 public class Try : Statement {
3092 public readonly Block Fini, Block;
3093 public readonly ArrayList Specific;
3094 public readonly Catch General;
3097 // specific, general and fini might all be null.
3099 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3101 if (specific == null && general == null){
3102 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3106 this.Specific = specific;
3107 this.General = general;
3112 public override bool Resolve (EmitContext ec)
3116 ec.StartFlowBranching (FlowBranching.BranchingType.Exception, Block.StartLocation);
3118 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3120 bool old_in_try = ec.InTry;
3123 if (!Block.Resolve (ec))
3126 ec.InTry = old_in_try;
3128 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3130 Report.Debug (1, "START OF CATCH BLOCKS", vector);
3132 foreach (Catch c in Specific){
3133 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Catch);
3134 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3136 if (c.Name != null) {
3137 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
3139 throw new Exception ();
3141 vi.VariableInfo = null;
3144 bool old_in_catch = ec.InCatch;
3147 if (!c.Resolve (ec))
3150 ec.InCatch = old_in_catch;
3153 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
3155 if (General != null){
3156 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Catch);
3157 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3159 bool old_in_catch = ec.InCatch;
3162 if (!General.Resolve (ec))
3165 ec.InCatch = old_in_catch;
3168 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
3172 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Finally);
3173 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3175 bool old_in_finally = ec.InFinally;
3176 ec.InFinally = true;
3178 if (!Fini.Resolve (ec))
3181 ec.InFinally = old_in_finally;
3184 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
3186 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3188 Report.Debug (1, "END OF TRY", ec.CurrentBranching, returns, vector, f_vector);
3190 if (returns != FlowBranching.FlowReturns.Always) {
3191 // Unfortunately, System.Reflection.Emit automatically emits a leave
3192 // to the end of the finally block. This is a problem if `returns'
3193 // is true since we may jump to a point after the end of the method.
3194 // As a workaround, emit an explicit ret here.
3195 ec.NeedExplicitReturn = true;
3201 protected override bool DoEmit (EmitContext ec)
3203 ILGenerator ig = ec.ig;
3205 Label finish = ig.DefineLabel ();;
3209 end = ig.BeginExceptionBlock ();
3210 bool old_in_try = ec.InTry;
3212 returns = Block.Emit (ec);
3213 ec.InTry = old_in_try;
3216 // System.Reflection.Emit provides this automatically:
3217 // ig.Emit (OpCodes.Leave, finish);
3219 bool old_in_catch = ec.InCatch;
3221 DeclSpace ds = ec.DeclSpace;
3223 foreach (Catch c in Specific){
3226 ig.BeginCatchBlock (c.CatchType);
3228 if (c.Name != null){
3229 vi = c.Block.GetLocalInfo (c.Name);
3231 throw new Exception ("Variable does not exist in this block");
3233 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3235 ig.Emit (OpCodes.Pop);
3237 if (!c.Block.Emit (ec))
3241 if (General != null){
3242 ig.BeginCatchBlock (TypeManager.object_type);
3243 ig.Emit (OpCodes.Pop);
3244 if (!General.Block.Emit (ec))
3247 ec.InCatch = old_in_catch;
3249 ig.MarkLabel (finish);
3251 ig.BeginFinallyBlock ();
3252 bool old_in_finally = ec.InFinally;
3253 ec.InFinally = true;
3255 ec.InFinally = old_in_finally;
3258 ig.EndExceptionBlock ();
3265 public class Using : Statement {
3266 object expression_or_block;
3267 Statement Statement;
3272 Expression [] converted_vars;
3273 ExpressionStatement [] assign;
3275 public Using (object expression_or_block, Statement stmt, Location l)
3277 this.expression_or_block = expression_or_block;
3283 // Resolves for the case of using using a local variable declaration.
3285 bool ResolveLocalVariableDecls (EmitContext ec)
3287 bool need_conv = false;
3288 expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
3291 if (expr_type == null)
3295 // The type must be an IDisposable or an implicit conversion
3298 converted_vars = new Expression [var_list.Count];
3299 assign = new ExpressionStatement [var_list.Count];
3300 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3301 foreach (DictionaryEntry e in var_list){
3302 Expression var = (Expression) e.Key;
3304 var = var.ResolveLValue (ec, new EmptyExpression ());
3308 converted_vars [i] = Convert.ImplicitConversionRequired (
3309 ec, var, TypeManager.idisposable_type, loc);
3311 if (converted_vars [i] == null)
3319 foreach (DictionaryEntry e in var_list){
3320 LocalVariableReference var = (LocalVariableReference) e.Key;
3321 Expression new_expr = (Expression) e.Value;
3324 a = new Assign (var, new_expr, loc);
3330 converted_vars [i] = var;
3331 assign [i] = (ExpressionStatement) a;
3338 bool ResolveExpression (EmitContext ec)
3340 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3341 conv = Convert.ImplicitConversionRequired (
3342 ec, expr, TypeManager.idisposable_type, loc);
3352 // Emits the code for the case of using using a local variable declaration.
3354 bool EmitLocalVariableDecls (EmitContext ec)
3356 ILGenerator ig = ec.ig;
3359 bool old_in_try = ec.InTry;
3361 for (i = 0; i < assign.Length; i++) {
3362 assign [i].EmitStatement (ec);
3364 ig.BeginExceptionBlock ();
3366 Statement.Emit (ec);
3367 ec.InTry = old_in_try;
3369 bool old_in_finally = ec.InFinally;
3370 ec.InFinally = true;
3371 var_list.Reverse ();
3372 foreach (DictionaryEntry e in var_list){
3373 LocalVariableReference var = (LocalVariableReference) e.Key;
3374 Label skip = ig.DefineLabel ();
3377 ig.BeginFinallyBlock ();
3380 ig.Emit (OpCodes.Brfalse, skip);
3381 converted_vars [i].Emit (ec);
3382 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3383 ig.MarkLabel (skip);
3384 ig.EndExceptionBlock ();
3386 ec.InFinally = old_in_finally;
3391 bool EmitExpression (EmitContext ec)
3394 // Make a copy of the expression and operate on that.
3396 ILGenerator ig = ec.ig;
3397 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
3402 ig.Emit (OpCodes.Stloc, local_copy);
3404 bool old_in_try = ec.InTry;
3406 ig.BeginExceptionBlock ();
3407 Statement.Emit (ec);
3408 ec.InTry = old_in_try;
3410 Label skip = ig.DefineLabel ();
3411 bool old_in_finally = ec.InFinally;
3412 ig.BeginFinallyBlock ();
3413 ig.Emit (OpCodes.Ldloc, local_copy);
3414 ig.Emit (OpCodes.Brfalse, skip);
3415 ig.Emit (OpCodes.Ldloc, local_copy);
3416 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3417 ig.MarkLabel (skip);
3418 ec.InFinally = old_in_finally;
3419 ig.EndExceptionBlock ();
3424 public override bool Resolve (EmitContext ec)
3426 if (expression_or_block is DictionaryEntry){
3427 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
3428 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
3430 if (!ResolveLocalVariableDecls (ec))
3433 } else if (expression_or_block is Expression){
3434 expr = (Expression) expression_or_block;
3436 expr = expr.Resolve (ec);
3440 expr_type = expr.Type;
3442 if (!ResolveExpression (ec))
3446 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
3448 bool ok = Statement.Resolve (ec);
3451 ec.KillFlowBranching ();
3455 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
3457 if (returns != FlowBranching.FlowReturns.Always) {
3458 // Unfortunately, System.Reflection.Emit automatically emits a leave
3459 // to the end of the finally block. This is a problem if `returns'
3460 // is true since we may jump to a point after the end of the method.
3461 // As a workaround, emit an explicit ret here.
3462 ec.NeedExplicitReturn = true;
3468 protected override bool DoEmit (EmitContext ec)
3470 if (expression_or_block is DictionaryEntry)
3471 return EmitLocalVariableDecls (ec);
3472 else if (expression_or_block is Expression)
3473 return EmitExpression (ec);
3480 /// Implementation of the foreach C# statement
3482 public class Foreach : Statement {
3484 Expression variable;
3486 Statement statement;
3487 ForeachHelperMethods hm;
3488 Expression empty, conv;
3489 Type array_type, element_type;
3492 public Foreach (Expression type, LocalVariableReference var, Expression expr,
3493 Statement stmt, Location l)
3496 this.variable = var;
3502 public override bool Resolve (EmitContext ec)
3504 expr = expr.Resolve (ec);
3508 var_type = ec.DeclSpace.ResolveType (type, false, loc);
3509 if (var_type == null)
3513 // We need an instance variable. Not sure this is the best
3514 // way of doing this.
3516 // FIXME: When we implement propertyaccess, will those turn
3517 // out to return values in ExprClass? I think they should.
3519 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3520 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
3521 error1579 (expr.Type);
3525 if (expr.Type.IsArray) {
3526 array_type = expr.Type;
3527 element_type = TypeManager.GetElementType (array_type);
3529 empty = new EmptyExpression (element_type);
3531 hm = ProbeCollectionType (ec, expr.Type);
3533 error1579 (expr.Type);
3537 array_type = expr.Type;
3538 element_type = hm.element_type;
3540 empty = new EmptyExpression (hm.element_type);
3543 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
3544 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
3548 // FIXME: maybe we can apply the same trick we do in the
3549 // array handling to avoid creating empty and conv in some cases.
3551 // Although it is not as important in this case, as the type
3552 // will not likely be object (what the enumerator will return).
3554 conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
3558 variable = variable.ResolveLValue (ec, empty);
3559 if (variable == null)
3562 if (!statement.Resolve (ec))
3565 FlowBranching.FlowReturns returns = ec.EndFlowBranching ();
3571 // Retrieves a `public bool MoveNext ()' method from the Type `t'
3573 static MethodInfo FetchMethodMoveNext (Type t)
3575 MemberList move_next_list;
3577 move_next_list = TypeContainer.FindMembers (
3578 t, MemberTypes.Method,
3579 BindingFlags.Public | BindingFlags.Instance,
3580 Type.FilterName, "MoveNext");
3581 if (move_next_list.Count == 0)
3584 foreach (MemberInfo m in move_next_list){
3585 MethodInfo mi = (MethodInfo) m;
3588 args = TypeManager.GetArgumentTypes (mi);
3589 if (args != null && args.Length == 0){
3590 if (mi.ReturnType == TypeManager.bool_type)
3598 // Retrieves a `public T get_Current ()' method from the Type `t'
3600 static MethodInfo FetchMethodGetCurrent (Type t)
3602 MemberList move_next_list;
3604 move_next_list = TypeContainer.FindMembers (
3605 t, MemberTypes.Method,
3606 BindingFlags.Public | BindingFlags.Instance,
3607 Type.FilterName, "get_Current");
3608 if (move_next_list.Count == 0)
3611 foreach (MemberInfo m in move_next_list){
3612 MethodInfo mi = (MethodInfo) m;
3615 args = TypeManager.GetArgumentTypes (mi);
3616 if (args != null && args.Length == 0)
3623 // This struct records the helper methods used by the Foreach construct
3625 class ForeachHelperMethods {
3626 public EmitContext ec;
3627 public MethodInfo get_enumerator;
3628 public MethodInfo move_next;
3629 public MethodInfo get_current;
3630 public Type element_type;
3631 public Type enumerator_type;
3632 public bool is_disposable;
3634 public ForeachHelperMethods (EmitContext ec)
3637 this.element_type = TypeManager.object_type;
3638 this.enumerator_type = TypeManager.ienumerator_type;
3639 this.is_disposable = true;
3643 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
3648 if (!(m is MethodInfo))
3651 if (m.Name != "GetEnumerator")
3654 MethodInfo mi = (MethodInfo) m;
3655 Type [] args = TypeManager.GetArgumentTypes (mi);
3657 if (args.Length != 0)
3660 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
3661 EmitContext ec = hm.ec;
3664 // Check whether GetEnumerator is accessible to us
3666 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
3668 Type declaring = mi.DeclaringType;
3669 if (prot == MethodAttributes.Private){
3670 if (declaring != ec.ContainerType)
3672 } else if (prot == MethodAttributes.FamANDAssem){
3673 // If from a different assembly, false
3674 if (!(mi is MethodBuilder))
3677 // Are we being invoked from the same class, or from a derived method?
3679 if (ec.ContainerType != declaring){
3680 if (!ec.ContainerType.IsSubclassOf (declaring))
3683 } else if (prot == MethodAttributes.FamORAssem){
3684 if (!(mi is MethodBuilder ||
3685 ec.ContainerType == declaring ||
3686 ec.ContainerType.IsSubclassOf (declaring)))
3688 } if (prot == MethodAttributes.Family){
3689 if (!(ec.ContainerType == declaring ||
3690 ec.ContainerType.IsSubclassOf (declaring)))
3694 if ((mi.ReturnType == TypeManager.ienumerator_type) && (declaring == TypeManager.string_type))
3696 // Apply the same optimization as MS: skip the GetEnumerator
3697 // returning an IEnumerator, and use the one returning a
3698 // CharEnumerator instead. This allows us to avoid the
3699 // try-finally block and the boxing.
3704 // Ok, we can access it, now make sure that we can do something
3705 // with this `GetEnumerator'
3708 if (mi.ReturnType == TypeManager.ienumerator_type ||
3709 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
3710 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
3711 if (declaring != TypeManager.string_type) {
3712 hm.move_next = TypeManager.bool_movenext_void;
3713 hm.get_current = TypeManager.object_getcurrent_void;
3719 // Ok, so they dont return an IEnumerable, we will have to
3720 // find if they support the GetEnumerator pattern.
3722 Type return_type = mi.ReturnType;
3724 hm.move_next = FetchMethodMoveNext (return_type);
3725 if (hm.move_next == null)
3727 hm.get_current = FetchMethodGetCurrent (return_type);
3728 if (hm.get_current == null)
3731 hm.element_type = hm.get_current.ReturnType;
3732 hm.enumerator_type = return_type;
3733 hm.is_disposable = !hm.enumerator_type.IsSealed ||
3734 TypeManager.ImplementsInterface (
3735 hm.enumerator_type, TypeManager.idisposable_type);
3741 /// This filter is used to find the GetEnumerator method
3742 /// on which IEnumerator operates
3744 static MemberFilter FilterEnumerator;
3748 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
3751 void error1579 (Type t)
3753 Report.Error (1579, loc,
3754 "foreach statement cannot operate on variables of type `" +
3755 t.FullName + "' because that class does not provide a " +
3756 " GetEnumerator method or it is inaccessible");
3759 static bool TryType (Type t, ForeachHelperMethods hm)
3763 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
3764 BindingFlags.Public | BindingFlags.NonPublic |
3765 BindingFlags.Instance,
3766 FilterEnumerator, hm);
3771 hm.get_enumerator = (MethodInfo) mi [0];
3776 // Looks for a usable GetEnumerator in the Type, and if found returns
3777 // the three methods that participate: GetEnumerator, MoveNext and get_Current
3779 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
3781 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
3783 if (TryType (t, hm))
3787 // Now try to find the method in the interfaces
3790 Type [] ifaces = t.GetInterfaces ();
3792 foreach (Type i in ifaces){
3793 if (TryType (i, hm))
3798 // Since TypeBuilder.GetInterfaces only returns the interface
3799 // types for this type, we have to keep looping, but once
3800 // we hit a non-TypeBuilder (ie, a Type), then we know we are
3801 // done, because it returns all the types
3803 if ((t is TypeBuilder))
3813 // FIXME: possible optimization.
3814 // We might be able to avoid creating `empty' if the type is the sam
3816 bool EmitCollectionForeach (EmitContext ec)
3818 ILGenerator ig = ec.ig;
3819 VariableStorage enumerator, disposable;
3821 enumerator = new VariableStorage (ec, hm.enumerator_type);
3822 if (hm.is_disposable)
3823 disposable = new VariableStorage (ec, TypeManager.idisposable_type);
3827 enumerator.EmitThis ();
3829 // Instantiate the enumerator
3831 if (expr.Type.IsValueType){
3832 if (expr is IMemoryLocation){
3833 IMemoryLocation ml = (IMemoryLocation) expr;
3835 ml.AddressOf (ec, AddressOp.Load);
3837 throw new Exception ("Expr " + expr + " of type " + expr.Type +
3838 " does not implement IMemoryLocation");
3839 ig.Emit (OpCodes.Call, hm.get_enumerator);
3842 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
3844 enumerator.EmitStore ();
3847 // Protect the code in a try/finalize block, so that
3848 // if the beast implement IDisposable, we get rid of it
3851 bool old_in_try = ec.InTry;
3853 if (hm.is_disposable) {
3854 l = ig.BeginExceptionBlock ();
3858 Label end_try = ig.DefineLabel ();
3860 ig.MarkLabel (ec.LoopBegin);
3861 enumerator.EmitLoad ();
3862 ig.Emit (OpCodes.Callvirt, hm.move_next);
3863 ig.Emit (OpCodes.Brfalse, end_try);
3867 enumerator.EmitLoad ();
3868 ig.Emit (OpCodes.Callvirt, hm.get_current);
3872 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
3874 ((IAssignMethod)variable).EmitAssign (ec, conv);
3876 statement.Emit (ec);
3877 ig.Emit (OpCodes.Br, ec.LoopBegin);
3878 ig.MarkLabel (end_try);
3879 ec.InTry = old_in_try;
3881 // The runtime provides this for us.
3882 // ig.Emit (OpCodes.Leave, end);
3885 // Now the finally block
3887 if (hm.is_disposable) {
3888 Label end_finally = ig.DefineLabel ();
3889 bool old_in_finally = ec.InFinally;
3890 ec.InFinally = true;
3891 ig.BeginFinallyBlock ();
3893 disposable.EmitThis ();
3894 enumerator.EmitThis ();
3895 enumerator.EmitLoad ();
3896 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
3897 disposable.EmitStore ();
3898 disposable.EmitLoad ();
3899 ig.Emit (OpCodes.Brfalse, end_finally);
3900 disposable.EmitThis ();
3901 disposable.EmitLoad ();
3902 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3903 ig.MarkLabel (end_finally);
3904 ec.InFinally = old_in_finally;
3906 // The runtime generates this anyways.
3907 // ig.Emit (OpCodes.Endfinally);
3909 ig.EndExceptionBlock ();
3912 ig.MarkLabel (ec.LoopEnd);
3917 // FIXME: possible optimization.
3918 // We might be able to avoid creating `empty' if the type is the sam
3920 bool EmitArrayForeach (EmitContext ec)
3922 int rank = array_type.GetArrayRank ();
3923 ILGenerator ig = ec.ig;
3925 VariableStorage copy = new VariableStorage (ec, array_type);
3928 // Make our copy of the array
3935 VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
3939 counter.EmitThis ();
3940 ig.Emit (OpCodes.Ldc_I4_0);
3941 counter.EmitStore ();
3942 test = ig.DefineLabel ();
3943 ig.Emit (OpCodes.Br, test);
3945 loop = ig.DefineLabel ();
3946 ig.MarkLabel (loop);
3953 counter.EmitThis ();
3954 counter.EmitLoad ();
3957 // Load the value, we load the value using the underlying type,
3958 // then we use the variable.EmitAssign to load using the proper cast.
3960 ArrayAccess.EmitLoadOpcode (ig, element_type);
3963 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
3965 ((IAssignMethod)variable).EmitAssign (ec, conv);
3967 statement.Emit (ec);
3969 ig.MarkLabel (ec.LoopBegin);
3970 counter.EmitThis ();
3971 counter.EmitThis ();
3972 counter.EmitLoad ();
3973 ig.Emit (OpCodes.Ldc_I4_1);
3974 ig.Emit (OpCodes.Add);
3975 counter.EmitStore ();
3977 ig.MarkLabel (test);
3978 counter.EmitThis ();
3979 counter.EmitLoad ();
3982 ig.Emit (OpCodes.Ldlen);
3983 ig.Emit (OpCodes.Conv_I4);
3984 ig.Emit (OpCodes.Blt, loop);
3986 VariableStorage [] dim_len = new VariableStorage [rank];
3987 VariableStorage [] dim_count = new VariableStorage [rank];
3988 Label [] loop = new Label [rank];
3989 Label [] test = new Label [rank];
3992 for (dim = 0; dim < rank; dim++){
3993 dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
3994 dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
3995 test [dim] = ig.DefineLabel ();
3996 loop [dim] = ig.DefineLabel ();
3999 for (dim = 0; dim < rank; dim++){
4000 dim_len [dim].EmitThis ();
4003 IntLiteral.EmitInt (ig, dim);
4004 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
4005 dim_len [dim].EmitStore ();
4009 for (dim = 0; dim < rank; dim++){
4010 dim_count [dim].EmitThis ();
4011 ig.Emit (OpCodes.Ldc_I4_0);
4012 dim_count [dim].EmitStore ();
4013 ig.Emit (OpCodes.Br, test [dim]);
4014 ig.MarkLabel (loop [dim]);
4021 for (dim = 0; dim < rank; dim++){
4022 dim_count [dim].EmitThis ();
4023 dim_count [dim].EmitLoad ();
4027 // FIXME: Maybe we can cache the computation of `get'?
4029 Type [] args = new Type [rank];
4032 for (int i = 0; i < rank; i++)
4033 args [i] = TypeManager.int32_type;
4035 ModuleBuilder mb = CodeGen.ModuleBuilder;
4036 get = mb.GetArrayMethod (
4038 CallingConventions.HasThis| CallingConventions.Standard,
4040 ig.Emit (OpCodes.Call, get);
4043 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
4045 ((IAssignMethod)variable).EmitAssign (ec, conv);
4046 statement.Emit (ec);
4047 ig.MarkLabel (ec.LoopBegin);
4048 for (dim = rank - 1; dim >= 0; dim--){
4049 dim_count [dim].EmitThis ();
4050 dim_count [dim].EmitThis ();
4051 dim_count [dim].EmitLoad ();
4052 ig.Emit (OpCodes.Ldc_I4_1);
4053 ig.Emit (OpCodes.Add);
4054 dim_count [dim].EmitStore ();
4056 ig.MarkLabel (test [dim]);
4057 dim_count [dim].EmitThis ();
4058 dim_count [dim].EmitLoad ();
4059 dim_len [dim].EmitThis ();
4060 dim_len [dim].EmitLoad ();
4061 ig.Emit (OpCodes.Blt, loop [dim]);
4064 ig.MarkLabel (ec.LoopEnd);
4069 protected override bool DoEmit (EmitContext ec)
4073 ILGenerator ig = ec.ig;
4075 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4076 bool old_inloop = ec.InLoop;
4077 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
4078 ec.LoopBegin = ig.DefineLabel ();
4079 ec.LoopEnd = ig.DefineLabel ();
4081 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
4084 ret_val = EmitCollectionForeach (ec);
4086 ret_val = EmitArrayForeach (ec);
4088 ec.LoopBegin = old_begin;
4089 ec.LoopEnd = old_end;
4090 ec.InLoop = old_inloop;
4091 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;