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 sealed class EmptyStatement : Statement {
100 private EmptyStatement () {}
102 public static readonly EmptyStatement Value = new EmptyStatement ();
104 public override bool Resolve (EmitContext ec)
109 protected override bool DoEmit (EmitContext ec)
115 public class If : Statement {
117 public Statement TrueStatement;
118 public Statement FalseStatement;
120 public If (Expression expr, Statement trueStatement, Location l)
123 TrueStatement = trueStatement;
127 public If (Expression expr,
128 Statement trueStatement,
129 Statement falseStatement,
133 TrueStatement = trueStatement;
134 FalseStatement = falseStatement;
138 public override bool Resolve (EmitContext ec)
140 Report.Debug (1, "START IF BLOCK", loc);
142 expr = Expression.ResolveBoolean (ec, expr, loc);
147 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
149 if (!TrueStatement.Resolve (ec)) {
150 ec.KillFlowBranching ();
154 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
156 if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
157 ec.KillFlowBranching ();
161 ec.EndFlowBranching ();
163 Report.Debug (1, "END IF BLOCK", loc);
168 protected override bool DoEmit (EmitContext ec)
170 ILGenerator ig = ec.ig;
171 Label false_target = ig.DefineLabel ();
173 bool is_true_ret, is_false_ret;
176 // Dead code elimination
178 if (expr is BoolConstant){
179 bool take = ((BoolConstant) expr).Value;
182 if (FalseStatement != null){
183 Warning_DeadCodeFound (FalseStatement.loc);
185 return TrueStatement.Emit (ec);
187 Warning_DeadCodeFound (TrueStatement.loc);
188 if (FalseStatement != null)
189 return FalseStatement.Emit (ec);
193 EmitBoolExpression (ec, expr, false_target, false);
195 is_true_ret = TrueStatement.Emit (ec);
196 is_false_ret = is_true_ret;
198 if (FalseStatement != null){
199 bool branch_emitted = false;
201 end = ig.DefineLabel ();
203 ig.Emit (OpCodes.Br, end);
204 branch_emitted = true;
207 ig.MarkLabel (false_target);
208 is_false_ret = FalseStatement.Emit (ec);
213 ig.MarkLabel (false_target);
214 is_false_ret = false;
217 return is_true_ret && is_false_ret;
221 public class Do : Statement {
222 public Expression expr;
223 public readonly Statement EmbeddedStatement;
224 bool infinite, may_return;
226 public Do (Statement statement, Expression boolExpr, Location l)
229 EmbeddedStatement = statement;
233 public override bool Resolve (EmitContext ec)
237 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
239 if (!EmbeddedStatement.Resolve (ec))
242 expr = Expression.ResolveBoolean (ec, expr, loc);
245 else if (expr is BoolConstant){
246 bool res = ((BoolConstant) expr).Value;
252 ec.CurrentBranching.Infinite = infinite;
253 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
254 may_return = reachability.Returns != FlowBranching.FlowReturns.Never;
259 protected override bool DoEmit (EmitContext ec)
261 ILGenerator ig = ec.ig;
262 Label loop = ig.DefineLabel ();
263 Label old_begin = ec.LoopBegin;
264 Label old_end = ec.LoopEnd;
265 bool old_inloop = ec.InLoop;
266 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
268 ec.LoopBegin = ig.DefineLabel ();
269 ec.LoopEnd = ig.DefineLabel ();
271 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
274 EmbeddedStatement.Emit (ec);
275 ig.MarkLabel (ec.LoopBegin);
278 // Dead code elimination
280 if (expr is BoolConstant){
281 bool res = ((BoolConstant) expr).Value;
284 ec.ig.Emit (OpCodes.Br, loop);
286 EmitBoolExpression (ec, expr, loop, true);
288 ig.MarkLabel (ec.LoopEnd);
290 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
291 ec.LoopBegin = old_begin;
292 ec.LoopEnd = old_end;
293 ec.InLoop = old_inloop;
296 return may_return == false;
302 public class While : Statement {
303 public Expression expr;
304 public readonly Statement Statement;
305 bool may_return, empty, infinite;
307 public While (Expression boolExpr, Statement statement, Location l)
309 this.expr = boolExpr;
310 Statement = statement;
314 public override bool Resolve (EmitContext ec)
318 expr = Expression.ResolveBoolean (ec, expr, loc);
322 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
325 // Inform whether we are infinite or not
327 if (expr is BoolConstant){
328 BoolConstant bc = (BoolConstant) expr;
330 if (bc.Value == false){
331 Warning_DeadCodeFound (Statement.loc);
337 // We are not infinite, so the loop may or may not be executed.
339 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
342 if (!Statement.Resolve (ec))
346 ec.KillFlowBranching ();
348 ec.CurrentBranching.Infinite = infinite;
349 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
350 may_return = reachability.Returns != FlowBranching.FlowReturns.Never;
356 protected override bool DoEmit (EmitContext ec)
361 ILGenerator ig = ec.ig;
362 Label old_begin = ec.LoopBegin;
363 Label old_end = ec.LoopEnd;
364 bool old_inloop = ec.InLoop;
365 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
368 ec.LoopBegin = ig.DefineLabel ();
369 ec.LoopEnd = ig.DefineLabel ();
371 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
374 // Inform whether we are infinite or not
376 if (expr is BoolConstant){
377 BoolConstant bc = (BoolConstant) expr;
379 ig.MarkLabel (ec.LoopBegin);
381 ig.Emit (OpCodes.Br, ec.LoopBegin);
384 // Inform that we are infinite (ie, `we return'), only
385 // if we do not `break' inside the code.
387 ret = may_return == false;
388 ig.MarkLabel (ec.LoopEnd);
390 Label while_loop = ig.DefineLabel ();
392 ig.Emit (OpCodes.Br, ec.LoopBegin);
393 ig.MarkLabel (while_loop);
397 ig.MarkLabel (ec.LoopBegin);
399 EmitBoolExpression (ec, expr, while_loop, true);
400 ig.MarkLabel (ec.LoopEnd);
405 ec.LoopBegin = old_begin;
406 ec.LoopEnd = old_end;
407 ec.InLoop = old_inloop;
408 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
414 public class For : Statement {
416 readonly Statement InitStatement;
417 readonly Statement Increment;
418 readonly Statement Statement;
419 bool may_return, infinite, empty;
421 public For (Statement initStatement,
427 InitStatement = initStatement;
429 Increment = increment;
430 Statement = statement;
434 public override bool Resolve (EmitContext ec)
438 if (InitStatement != null){
439 if (!InitStatement.Resolve (ec))
444 Test = Expression.ResolveBoolean (ec, Test, loc);
447 else if (Test is BoolConstant){
448 BoolConstant bc = (BoolConstant) Test;
450 if (bc.Value == false){
451 Warning_DeadCodeFound (Statement.loc);
459 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
461 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
463 if (!Statement.Resolve (ec))
466 if (Increment != null){
467 if (!Increment.Resolve (ec))
472 ec.KillFlowBranching ();
474 ec.CurrentBranching.Infinite = infinite;
475 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
476 may_return = reachability.Returns != FlowBranching.FlowReturns.Never;
482 protected override bool DoEmit (EmitContext ec)
487 ILGenerator ig = ec.ig;
488 Label old_begin = ec.LoopBegin;
489 Label old_end = ec.LoopEnd;
490 bool old_inloop = ec.InLoop;
491 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
492 Label loop = ig.DefineLabel ();
493 Label test = ig.DefineLabel ();
495 if (InitStatement != null && InitStatement != EmptyStatement.Value)
496 InitStatement.Emit (ec);
498 ec.LoopBegin = ig.DefineLabel ();
499 ec.LoopEnd = ig.DefineLabel ();
501 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
503 ig.Emit (OpCodes.Br, test);
507 ig.MarkLabel (ec.LoopBegin);
508 if (Increment != EmptyStatement.Value)
513 // If test is null, there is no test, and we are just
518 // The Resolve code already catches the case for Test == BoolConstant (false)
519 // so we know that this is true
521 if (Test is BoolConstant)
522 ig.Emit (OpCodes.Br, loop);
524 EmitBoolExpression (ec, Test, loop, true);
526 ig.Emit (OpCodes.Br, loop);
527 ig.MarkLabel (ec.LoopEnd);
529 ec.LoopBegin = old_begin;
530 ec.LoopEnd = old_end;
531 ec.InLoop = old_inloop;
532 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
535 // Inform whether we are infinite or not
538 if (Test is BoolConstant){
539 BoolConstant bc = (BoolConstant) Test;
542 return may_return == false;
546 return may_return == false;
550 public class StatementExpression : Statement {
551 ExpressionStatement expr;
553 public StatementExpression (ExpressionStatement expr, Location l)
559 public override bool Resolve (EmitContext ec)
561 expr = expr.ResolveStatement (ec);
565 protected override bool DoEmit (EmitContext ec)
567 ILGenerator ig = ec.ig;
569 expr.EmitStatement (ec);
574 public override string ToString ()
576 return "StatementExpression (" + expr + ")";
581 /// Implements the return statement
583 public class Return : Statement {
584 public Expression Expr;
586 public Return (Expression expr, Location l)
592 public override bool Resolve (EmitContext ec)
595 Expr = Expr.Resolve (ec);
601 Report.Error (-206, loc, "Return statement not allowed inside iterators");
605 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
607 if (ec.CurrentBranching.InTryBlock ())
608 ec.CurrentBranching.AddFinallyVector (vector);
610 vector.CheckOutParameters (ec.CurrentBranching);
612 ec.CurrentBranching.CurrentUsageVector.Return ();
616 protected override bool DoEmit (EmitContext ec)
619 Report.Error (157, loc, "Control can not leave the body of the finally block");
623 if (ec.ReturnType == null){
625 Report.Error (127, loc, "Return with a value not allowed here");
630 Report.Error (126, loc, "An object of type `" +
631 TypeManager.CSharpName (ec.ReturnType) + "' is " +
632 "expected for the return statement");
636 if (Expr.Type != ec.ReturnType)
637 Expr = Convert.ImplicitConversionRequired (
638 ec, Expr, ec.ReturnType, loc);
645 if (ec.InTry || ec.InCatch)
646 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
649 if (ec.InTry || ec.InCatch) {
650 if (!ec.HasReturnLabel) {
651 ec.ReturnLabel = ec.ig.DefineLabel ();
652 ec.HasReturnLabel = true;
654 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
656 ec.ig.Emit (OpCodes.Ret);
657 ec.NeedExplicitReturn = false;
664 public class Goto : Statement {
667 LabeledStatement label;
669 public override bool Resolve (EmitContext ec)
671 label = block.LookupLabel (target);
675 "No such label `" + target + "' in this scope");
679 // If this is a forward goto.
680 if (!label.IsDefined)
681 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
683 ec.CurrentBranching.CurrentUsageVector.Goto ();
688 public Goto (Block parent_block, string label, Location l)
690 block = parent_block;
695 public string Target {
701 protected override bool DoEmit (EmitContext ec)
703 Label l = label.LabelTarget (ec);
704 ec.ig.Emit (OpCodes.Br, l);
710 public class LabeledStatement : Statement {
711 public readonly Location Location;
719 public LabeledStatement (string label_name, Location l)
721 this.label_name = label_name;
725 public Label LabelTarget (EmitContext ec)
729 label = ec.ig.DefineLabel ();
735 public bool IsDefined {
741 public bool HasBeenReferenced {
747 public void AddUsageVector (FlowBranching.UsageVector vector)
750 vectors = new ArrayList ();
752 vectors.Add (vector.Clone ());
755 public override bool Resolve (EmitContext ec)
757 ec.CurrentBranching.Label (vectors);
764 protected override bool DoEmit (EmitContext ec)
767 ec.ig.MarkLabel (label);
775 /// `goto default' statement
777 public class GotoDefault : Statement {
779 public GotoDefault (Location l)
784 public override bool Resolve (EmitContext ec)
786 ec.CurrentBranching.CurrentUsageVector.Goto ();
790 protected override bool DoEmit (EmitContext ec)
792 if (ec.Switch == null){
793 Report.Error (153, loc, "goto default is only valid in a switch statement");
797 if (!ec.Switch.GotDefault){
798 Report.Error (159, loc, "No default target on switch statement");
801 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
807 /// `goto case' statement
809 public class GotoCase : Statement {
813 public GotoCase (Expression e, Location l)
819 public override bool Resolve (EmitContext ec)
821 if (ec.Switch == null){
822 Report.Error (153, loc, "goto case is only valid in a switch statement");
826 expr = expr.Resolve (ec);
830 if (!(expr is Constant)){
831 Report.Error (159, loc, "Target expression for goto case is not constant");
835 object val = Expression.ConvertIntLiteral (
836 (Constant) expr, ec.Switch.SwitchType, loc);
841 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
846 "No such label 'case " + val + "': for the goto case");
850 label = sl.ILLabelCode;
852 ec.CurrentBranching.CurrentUsageVector.Goto ();
856 protected override bool DoEmit (EmitContext ec)
858 ec.ig.Emit (OpCodes.Br, label);
863 public class Throw : Statement {
866 public Throw (Expression expr, Location l)
872 public override bool Resolve (EmitContext ec)
875 expr = expr.Resolve (ec);
879 ExprClass eclass = expr.eclass;
881 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
882 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
883 expr.Error_UnexpectedKind ("value, variable, property or indexer access ");
889 if ((t != TypeManager.exception_type) &&
890 !t.IsSubclassOf (TypeManager.exception_type) &&
891 !(expr is NullLiteral)) {
892 Report.Error (155, loc,
893 "The type caught or thrown must be derived " +
894 "from System.Exception");
899 ec.CurrentBranching.CurrentUsageVector.Throw ();
903 protected override bool DoEmit (EmitContext ec)
907 ec.ig.Emit (OpCodes.Rethrow);
911 "A throw statement with no argument is only " +
912 "allowed in a catch clause");
919 ec.ig.Emit (OpCodes.Throw);
925 public class Break : Statement {
927 public Break (Location l)
932 public override bool Resolve (EmitContext ec)
934 ec.CurrentBranching.CurrentUsageVector.Break ();
938 protected override bool DoEmit (EmitContext ec)
940 ILGenerator ig = ec.ig;
942 if (ec.InLoop == false && ec.Switch == null){
943 Report.Error (139, loc, "No enclosing loop or switch to continue to");
947 if (ec.InTry || ec.InCatch)
948 ig.Emit (OpCodes.Leave, ec.LoopEnd);
950 ig.Emit (OpCodes.Br, ec.LoopEnd);
956 public class Continue : Statement {
958 public Continue (Location l)
963 public override bool Resolve (EmitContext ec)
965 ec.CurrentBranching.CurrentUsageVector.Goto ();
969 protected override bool DoEmit (EmitContext ec)
971 Label begin = ec.LoopBegin;
974 Report.Error (139, loc, "No enclosing loop to continue to");
979 // UGH: Non trivial. This Br might cross a try/catch boundary
983 // try { ... } catch { continue; }
987 // try {} catch { while () { continue; }}
989 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
990 ec.ig.Emit (OpCodes.Leave, begin);
991 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
992 throw new Exception ("Should never happen");
994 ec.ig.Emit (OpCodes.Br, begin);
999 public class LocalInfo {
1000 public Expression Type;
1003 // Most of the time a variable will be stored in a LocalBuilder
1005 // But sometimes, it will be stored in a field. The context of the field will
1006 // be stored in the EmitContext
1009 public LocalBuilder LocalBuilder;
1010 public FieldBuilder FieldBuilder;
1012 public Type VariableType;
1013 public readonly string Name;
1014 public readonly Location Location;
1015 public readonly Block Block;
1017 public VariableInfo VariableInfo;
1028 public LocalInfo (Expression type, string name, Block block, Location l)
1036 public LocalInfo (TypeContainer tc, Block block, Location l)
1038 VariableType = tc.TypeBuilder;
1043 public bool IsThisAssigned (EmitContext ec, Location loc)
1045 if (VariableInfo == null)
1046 throw new Exception ();
1048 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1051 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1054 public bool Resolve (DeclSpace decl)
1056 if (VariableType == null)
1057 VariableType = decl.ResolveType (Type, false, Location);
1059 if (VariableType == null)
1065 public void MakePinned ()
1067 TypeManager.MakePinned (LocalBuilder);
1068 flags |= Flags.Fixed;
1071 public bool IsFixed {
1073 if (((flags & Flags.Fixed) != 0) || TypeManager.IsValueType (VariableType))
1080 public override string ToString ()
1082 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1083 Name, Type, VariableInfo, Location);
1088 return (flags & Flags.Used) != 0;
1091 flags = value ? (flags | Flags.Used) : (flags & ~Flags.Used);
1095 public bool Assigned {
1097 return (flags & Flags.Assigned) != 0;
1100 flags = value ? (flags | Flags.Assigned) : (flags & ~Flags.Assigned);
1104 public bool ReadOnly {
1106 return (flags & Flags.ReadOnly) != 0;
1109 flags = value ? (flags | Flags.ReadOnly) : (flags & ~Flags.ReadOnly);
1118 /// Block represents a C# block.
1122 /// This class is used in a number of places: either to represent
1123 /// explicit blocks that the programmer places or implicit blocks.
1125 /// Implicit blocks are used as labels or to introduce variable
1128 public class Block : Statement {
1129 public readonly Block Parent;
1130 public readonly Location StartLocation;
1131 public Location EndLocation = Location.Null;
1134 public enum Flags : byte {
1138 VariablesInitialized = 8,
1143 public bool Implicit {
1145 return (flags & Flags.Implicit) != 0;
1149 public bool Unchecked {
1151 return (flags & Flags.Unchecked) != 0;
1154 flags |= Flags.Unchecked;
1159 // The statements in this block
1161 ArrayList statements;
1164 // An array of Blocks. We keep track of children just
1165 // to generate the local variable declarations.
1167 // Statements and child statements are handled through the
1173 // Labels. (label, block) pairs.
1178 // Keeps track of (name, type) pairs
1180 Hashtable variables;
1183 // Keeps track of constants
1184 Hashtable constants;
1187 // If this is a switch section, the enclosing switch block.
1195 public Block (Block parent)
1196 : this (parent, (Flags) 0, Location.Null, Location.Null)
1199 public Block (Block parent, Flags flags)
1200 : this (parent, flags, Location.Null, Location.Null)
1203 public Block (Block parent, Flags flags, Parameters parameters)
1204 : this (parent, flags, parameters, Location.Null, Location.Null)
1207 public Block (Block parent, Location start, Location end)
1208 : this (parent, (Flags) 0, start, end)
1211 public Block (Block parent, Parameters parameters, Location start, Location end)
1212 : this (parent, (Flags) 0, parameters, start, end)
1215 public Block (Block parent, Flags flags, Location start, Location end)
1216 : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
1219 public Block (Block parent, Flags flags, Parameters parameters,
1220 Location start, Location end)
1223 parent.AddChild (this);
1225 this.Parent = parent;
1227 this.parameters = parameters;
1228 this.StartLocation = start;
1229 this.EndLocation = end;
1232 statements = new ArrayList ();
1235 public Block CreateSwitchBlock (Location start)
1237 Block new_block = new Block (this, start, start);
1238 new_block.switch_block = this;
1248 void AddChild (Block b)
1250 if (children == null)
1251 children = new ArrayList ();
1256 public void SetEndLocation (Location loc)
1262 /// Adds a label to the current block.
1266 /// false if the name already exists in this block. true
1270 public bool AddLabel (string name, LabeledStatement target)
1272 if (switch_block != null)
1273 return switch_block.AddLabel (name, target);
1276 labels = new Hashtable ();
1277 if (labels.Contains (name))
1280 labels.Add (name, target);
1284 public LabeledStatement LookupLabel (string name)
1286 Hashtable l = new Hashtable ();
1288 return LookupLabel (name, l);
1292 // Lookups a label in the current block, parents and children.
1293 // It skips during child recurssion on `source'
1295 LabeledStatement LookupLabel (string name, Hashtable seen)
1297 if (switch_block != null)
1298 return switch_block.LookupLabel (name, seen);
1300 if (seen [this] != null)
1306 if (labels.Contains (name))
1307 return ((LabeledStatement) labels [name]);
1309 if (children != null){
1310 foreach (Block b in children){
1311 LabeledStatement s = b.LookupLabel (name, seen);
1318 return Parent.LookupLabel (name, seen);
1323 LocalInfo this_variable = null;
1326 // Returns the "this" instance variable of this block.
1327 // See AddThisVariable() for more information.
1329 public LocalInfo ThisVariable {
1331 if (this_variable != null)
1332 return this_variable;
1333 else if (Parent != null)
1334 return Parent.ThisVariable;
1340 Hashtable child_variable_names;
1343 // Marks a variable with name @name as being used in a child block.
1344 // If a variable name has been used in a child block, it's illegal to
1345 // declare a variable with the same name in the current block.
1347 public void AddChildVariableName (string name)
1349 if (child_variable_names == null)
1350 child_variable_names = new Hashtable ();
1352 if (!child_variable_names.Contains (name))
1353 child_variable_names.Add (name, true);
1357 // Marks all variables from block @block and all its children as being
1358 // used in a child block.
1360 public void AddChildVariableNames (Block block)
1362 if (block.Variables != null) {
1363 foreach (string name in block.Variables.Keys)
1364 AddChildVariableName (name);
1367 if (block.children != null) {
1368 foreach (Block child in block.children)
1369 AddChildVariableNames (child);
1372 if (block.child_variable_names != null) {
1373 foreach (string name in block.child_variable_names.Keys)
1374 AddChildVariableName (name);
1379 // Checks whether a variable name has already been used in a child block.
1381 public bool IsVariableNameUsedInChildBlock (string name)
1383 if (child_variable_names == null)
1386 return child_variable_names.Contains (name);
1390 // This is used by non-static `struct' constructors which do not have an
1391 // initializer - in this case, the constructor must initialize all of the
1392 // struct's fields. To do this, we add a "this" variable and use the flow
1393 // analysis code to ensure that it's been fully initialized before control
1394 // leaves the constructor.
1396 public LocalInfo AddThisVariable (TypeContainer tc, Location l)
1398 if (this_variable != null)
1399 return this_variable;
1401 if (variables == null)
1402 variables = new Hashtable ();
1404 this_variable = new LocalInfo (tc, this, l);
1406 variables.Add ("this", this_variable);
1408 return this_variable;
1411 public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
1413 if (variables == null)
1414 variables = new Hashtable ();
1416 LocalInfo vi = GetLocalInfo (name);
1418 if (vi.Block != this)
1419 Report.Error (136, l, "A local variable named `" + name + "' " +
1420 "cannot be declared in this scope since it would " +
1421 "give a different meaning to `" + name + "', which " +
1422 "is already used in a `parent or current' scope to " +
1423 "denote something else");
1425 Report.Error (128, l, "A local variable `" + name + "' is already " +
1426 "defined in this scope");
1430 if (IsVariableNameUsedInChildBlock (name)) {
1431 Report.Error (136, l, "A local variable named `" + name + "' " +
1432 "cannot be declared in this scope since it would " +
1433 "give a different meaning to `" + name + "', which " +
1434 "is already used in a `child' scope to denote something " +
1441 Parameter p = pars.GetParameterByName (name, out idx);
1443 Report.Error (136, l, "A local variable named `" + name + "' " +
1444 "cannot be declared in this scope since it would " +
1445 "give a different meaning to `" + name + "', which " +
1446 "is already used in a `parent or current' scope to " +
1447 "denote something else");
1452 vi = new LocalInfo (type, name, this, l);
1454 variables.Add (name, vi);
1456 if ((flags & Flags.VariablesInitialized) != 0)
1457 throw new Exception ();
1459 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1463 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1465 if (AddVariable (type, name, pars, l) == null)
1468 if (constants == null)
1469 constants = new Hashtable ();
1471 constants.Add (name, value);
1475 public Hashtable Variables {
1481 public LocalInfo GetLocalInfo (string name)
1483 for (Block b = this; b != null; b = b.Parent) {
1484 if (b.variables != null) {
1485 LocalInfo ret = b.variables [name] as LocalInfo;
1493 public Expression GetVariableType (string name)
1495 LocalInfo vi = GetLocalInfo (name);
1503 public Expression GetConstantExpression (string name)
1505 for (Block b = this; b != null; b = b.Parent) {
1506 if (b.constants != null) {
1507 Expression ret = b.constants [name] as Expression;
1516 /// True if the variable named @name is a constant
1518 public bool IsConstant (string name)
1520 Expression e = null;
1522 e = GetConstantExpression (name);
1528 /// Use to fetch the statement associated with this label
1530 public Statement this [string name] {
1532 return (Statement) labels [name];
1536 Parameters parameters = null;
1537 public Parameters Parameters {
1540 while (b.Parent != null)
1542 return b.parameters;
1547 /// A list of labels that were not used within this block
1549 public string [] GetUnreferenced ()
1551 // FIXME: Implement me
1555 public void AddStatement (Statement s)
1558 flags |= Flags.BlockUsed;
1563 return (flags & Flags.BlockUsed) != 0;
1569 flags |= Flags.BlockUsed;
1572 VariableMap param_map, local_map;
1574 public VariableMap ParameterMap {
1576 if ((flags & Flags.VariablesInitialized) == 0)
1577 throw new Exception ();
1583 public VariableMap LocalMap {
1585 if ((flags & Flags.VariablesInitialized) == 0)
1586 throw new Exception ();
1592 public bool LiftVariable (LocalInfo local_info)
1598 /// Emits the variable declarations and labels.
1601 /// tc: is our typecontainer (to resolve type references)
1602 /// ig: is the code generator:
1604 public void EmitMeta (EmitContext ec, InternalParameters ip)
1606 DeclSpace ds = ec.DeclSpace;
1607 ILGenerator ig = ec.ig;
1610 // Compute the VariableMap's.
1612 // Unfortunately, we don't know the type when adding variables with
1613 // AddVariable(), so we need to compute this info here.
1617 if (variables != null) {
1618 foreach (LocalInfo li in variables.Values)
1619 li.Resolve (ec.DeclSpace);
1621 locals = new LocalInfo [variables.Count];
1622 variables.Values.CopyTo (locals, 0);
1624 locals = new LocalInfo [0];
1627 local_map = new VariableMap (Parent.LocalMap, locals);
1629 local_map = new VariableMap (locals);
1631 param_map = new VariableMap (ip);
1632 flags |= Flags.VariablesInitialized;
1634 bool old_check_state = ec.ConstantCheckState;
1635 ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
1636 bool remap_locals = ec.RemapToProxy;
1639 // Process this block variables
1641 if (variables != null){
1642 foreach (DictionaryEntry de in variables){
1643 string name = (string) de.Key;
1644 LocalInfo vi = (LocalInfo) de.Value;
1646 if (vi.VariableType == null)
1649 Type variable_type = vi.VariableType;
1651 if (variable_type.IsPointer){
1653 // Am not really convinced that this test is required (Microsoft does it)
1654 // but the fact is that you would not be able to use the pointer variable
1657 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1663 vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
1665 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1667 if (constants == null)
1670 Expression cv = (Expression) constants [name];
1674 ec.CurrentBlock = this;
1675 Expression e = cv.Resolve (ec);
1679 Constant ce = e as Constant;
1681 Report.Error (133, vi.Location,
1682 "The expression being assigned to `" +
1683 name + "' must be constant (" + e + ")");
1687 if (e.Type != variable_type){
1688 e = Const.ChangeType (vi.Location, ce, variable_type);
1693 constants.Remove (name);
1694 constants.Add (name, e);
1697 ec.ConstantCheckState = old_check_state;
1700 // Now, handle the children
1702 if (children != null){
1703 foreach (Block b in children)
1704 b.EmitMeta (ec, ip);
1708 public void UsageWarning ()
1712 if (variables != null){
1713 foreach (DictionaryEntry de in variables){
1714 LocalInfo vi = (LocalInfo) de.Value;
1719 name = (string) de.Key;
1723 219, vi.Location, "The variable `" + name +
1724 "' is assigned but its value is never used");
1727 168, vi.Location, "The variable `" +
1729 "' is declared but never used");
1734 if (children != null)
1735 foreach (Block b in children)
1739 public override bool Resolve (EmitContext ec)
1741 Block prev_block = ec.CurrentBlock;
1744 ec.CurrentBlock = this;
1745 ec.StartFlowBranching (this);
1747 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1749 bool unreachable = false, warning_shown = false;
1751 int statement_count = statements.Count;
1752 for (int ix = 0; ix < statement_count; ix++){
1753 Statement s = (Statement) statements [ix];
1755 if (unreachable && !(s is LabeledStatement)) {
1756 if (!warning_shown && s != EmptyStatement.Value) {
1757 warning_shown = true;
1758 Warning_DeadCodeFound (s.loc);
1761 statements [ix] = EmptyStatement.Value;
1765 if (s.Resolve (ec) == false) {
1767 statements [ix] = EmptyStatement.Value;
1771 if (s is LabeledStatement)
1772 unreachable = false;
1774 unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
1777 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
1779 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
1780 ec.CurrentBlock = prev_block;
1782 // If we're a non-static `struct' constructor which doesn't have an
1783 // initializer, then we must initialize all of the struct's fields.
1784 if ((this_variable != null) &&
1785 (reachability.Throws != FlowBranching.FlowReturns.Always) &&
1786 !this_variable.IsThisAssigned (ec, loc))
1789 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
1790 foreach (LabeledStatement label in labels.Values)
1791 if (!label.HasBeenReferenced)
1792 Report.Warning (164, label.Location,
1793 "This label has not been referenced");
1796 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, reachability);
1798 if ((reachability.Returns == FlowBranching.FlowReturns.Always) ||
1799 (reachability.Throws == FlowBranching.FlowReturns.Always) ||
1800 (reachability.Reachable == FlowBranching.FlowReturns.Never))
1801 flags |= Flags.HasRet;
1806 protected override bool DoEmit (EmitContext ec)
1808 int statement_count = statements.Count;
1809 for (int ix = 0; ix < statement_count; ix++){
1810 Statement s = (Statement) statements [ix];
1814 return (flags & Flags.HasRet) != 0;
1817 public override bool Emit (EmitContext ec)
1819 Block prev_block = ec.CurrentBlock;
1821 ec.CurrentBlock = this;
1823 bool emit_debug_info = (CodeGen.SymbolWriter != null);
1824 bool is_lexical_block = !Implicit && (Parent != null);
1826 if (emit_debug_info) {
1827 if (is_lexical_block)
1828 ec.ig.BeginScope ();
1830 if (variables != null) {
1831 foreach (DictionaryEntry de in variables) {
1832 string name = (string) de.Key;
1833 LocalInfo vi = (LocalInfo) de.Value;
1835 if (vi.LocalBuilder == null)
1838 vi.LocalBuilder.SetLocalSymInfo (name);
1843 ec.Mark (StartLocation, true);
1844 bool retval = DoEmit (ec);
1845 ec.Mark (EndLocation, true);
1847 if (emit_debug_info && is_lexical_block)
1850 ec.CurrentBlock = prev_block;
1856 public class SwitchLabel {
1859 public Location loc;
1860 public Label ILLabel;
1861 public Label ILLabelCode;
1864 // if expr == null, then it is the default case.
1866 public SwitchLabel (Expression expr, Location l)
1872 public Expression Label {
1878 public object Converted {
1885 // Resolves the expression, reduces it to a literal if possible
1886 // and then converts it to the requested type.
1888 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1890 ILLabel = ec.ig.DefineLabel ();
1891 ILLabelCode = ec.ig.DefineLabel ();
1896 Expression e = label.Resolve (ec);
1901 if (!(e is Constant)){
1902 Report.Error (150, loc, "A constant value is expected, got: " + e);
1906 if (e is StringConstant || e is NullLiteral){
1907 if (required_type == TypeManager.string_type){
1909 ILLabel = ec.ig.DefineLabel ();
1914 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1915 if (converted == null)
1922 public class SwitchSection {
1923 // An array of SwitchLabels.
1924 public readonly ArrayList Labels;
1925 public readonly Block Block;
1927 public SwitchSection (ArrayList labels, Block block)
1934 public class Switch : Statement {
1935 public readonly ArrayList Sections;
1936 public Expression Expr;
1939 /// Maps constants whose type type SwitchType to their SwitchLabels.
1941 public Hashtable Elements;
1944 /// The governing switch type
1946 public Type SwitchType;
1952 Label default_target;
1953 Expression new_expr;
1956 // The types allowed to be implicitly cast from
1957 // on the governing type
1959 static Type [] allowed_types;
1961 public Switch (Expression e, ArrayList sects, Location l)
1968 public bool GotDefault {
1974 public Label DefaultTarget {
1976 return default_target;
1981 // Determines the governing type for a switch. The returned
1982 // expression might be the expression from the switch, or an
1983 // expression that includes any potential conversions to the
1984 // integral types or to string.
1986 Expression SwitchGoverningType (EmitContext ec, Type t)
1988 if (t == TypeManager.int32_type ||
1989 t == TypeManager.uint32_type ||
1990 t == TypeManager.char_type ||
1991 t == TypeManager.byte_type ||
1992 t == TypeManager.sbyte_type ||
1993 t == TypeManager.ushort_type ||
1994 t == TypeManager.short_type ||
1995 t == TypeManager.uint64_type ||
1996 t == TypeManager.int64_type ||
1997 t == TypeManager.string_type ||
1998 t == TypeManager.bool_type ||
1999 t.IsSubclassOf (TypeManager.enum_type))
2002 if (allowed_types == null){
2003 allowed_types = new Type [] {
2004 TypeManager.sbyte_type,
2005 TypeManager.byte_type,
2006 TypeManager.short_type,
2007 TypeManager.ushort_type,
2008 TypeManager.int32_type,
2009 TypeManager.uint32_type,
2010 TypeManager.int64_type,
2011 TypeManager.uint64_type,
2012 TypeManager.char_type,
2013 TypeManager.bool_type,
2014 TypeManager.string_type
2019 // Try to find a *user* defined implicit conversion.
2021 // If there is no implicit conversion, or if there are multiple
2022 // conversions, we have to report an error
2024 Expression converted = null;
2025 foreach (Type tt in allowed_types){
2028 e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
2032 if (converted != null){
2033 Report.Error (-12, loc, "More than one conversion to an integral " +
2034 " type exists for type `" +
2035 TypeManager.CSharpName (Expr.Type)+"'");
2043 void error152 (string n)
2046 152, "The label `" + n + ":' " +
2047 "is already present on this switch statement");
2051 // Performs the basic sanity checks on the switch statement
2052 // (looks for duplicate keys and non-constant expressions).
2054 // It also returns a hashtable with the keys that we will later
2055 // use to compute the switch tables
2057 bool CheckSwitch (EmitContext ec)
2061 Elements = new Hashtable ();
2063 got_default = false;
2065 if (TypeManager.IsEnumType (SwitchType)){
2066 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2068 compare_type = SwitchType;
2070 foreach (SwitchSection ss in Sections){
2071 foreach (SwitchLabel sl in ss.Labels){
2072 if (!sl.ResolveAndReduce (ec, SwitchType)){
2077 if (sl.Label == null){
2079 error152 ("default");
2086 object key = sl.Converted;
2088 if (key is Constant)
2089 key = ((Constant) key).GetValue ();
2092 key = NullLiteral.Null;
2094 string lname = null;
2095 if (compare_type == TypeManager.uint64_type){
2096 ulong v = (ulong) key;
2098 if (Elements.Contains (v))
2099 lname = v.ToString ();
2101 Elements.Add (v, sl);
2102 } else if (compare_type == TypeManager.int64_type){
2103 long v = (long) key;
2105 if (Elements.Contains (v))
2106 lname = v.ToString ();
2108 Elements.Add (v, sl);
2109 } else if (compare_type == TypeManager.uint32_type){
2110 uint v = (uint) key;
2112 if (Elements.Contains (v))
2113 lname = v.ToString ();
2115 Elements.Add (v, sl);
2116 } else if (compare_type == TypeManager.char_type){
2117 char v = (char) key;
2119 if (Elements.Contains (v))
2120 lname = v.ToString ();
2122 Elements.Add (v, sl);
2123 } else if (compare_type == TypeManager.byte_type){
2124 byte v = (byte) key;
2126 if (Elements.Contains (v))
2127 lname = v.ToString ();
2129 Elements.Add (v, sl);
2130 } else if (compare_type == TypeManager.sbyte_type){
2131 sbyte v = (sbyte) key;
2133 if (Elements.Contains (v))
2134 lname = v.ToString ();
2136 Elements.Add (v, sl);
2137 } else if (compare_type == TypeManager.short_type){
2138 short v = (short) key;
2140 if (Elements.Contains (v))
2141 lname = v.ToString ();
2143 Elements.Add (v, sl);
2144 } else if (compare_type == TypeManager.ushort_type){
2145 ushort v = (ushort) key;
2147 if (Elements.Contains (v))
2148 lname = v.ToString ();
2150 Elements.Add (v, sl);
2151 } else if (compare_type == TypeManager.string_type){
2152 if (key is NullLiteral){
2153 if (Elements.Contains (NullLiteral.Null))
2156 Elements.Add (NullLiteral.Null, null);
2158 string s = (string) key;
2160 if (Elements.Contains (s))
2163 Elements.Add (s, sl);
2165 } else if (compare_type == TypeManager.int32_type) {
2168 if (Elements.Contains (v))
2169 lname = v.ToString ();
2171 Elements.Add (v, sl);
2172 } else if (compare_type == TypeManager.bool_type) {
2173 bool v = (bool) key;
2175 if (Elements.Contains (v))
2176 lname = v.ToString ();
2178 Elements.Add (v, sl);
2182 throw new Exception ("Unknown switch type!" +
2183 SwitchType + " " + compare_type);
2187 error152 ("case + " + lname);
2198 void EmitObjectInteger (ILGenerator ig, object k)
2201 IntConstant.EmitInt (ig, (int) k);
2202 else if (k is Constant) {
2203 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2206 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2209 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2211 IntConstant.EmitInt (ig, (int) (long) k);
2212 ig.Emit (OpCodes.Conv_I8);
2215 LongConstant.EmitLong (ig, (long) k);
2217 else if (k is ulong)
2219 if ((ulong) k < (1L<<32))
2221 IntConstant.EmitInt (ig, (int) (long) k);
2222 ig.Emit (OpCodes.Conv_U8);
2226 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2230 IntConstant.EmitInt (ig, (int) ((char) k));
2231 else if (k is sbyte)
2232 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2234 IntConstant.EmitInt (ig, (int) ((byte) k));
2235 else if (k is short)
2236 IntConstant.EmitInt (ig, (int) ((short) k));
2237 else if (k is ushort)
2238 IntConstant.EmitInt (ig, (int) ((ushort) k));
2240 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2242 throw new Exception ("Unhandled case");
2245 // structure used to hold blocks of keys while calculating table switch
2246 class KeyBlock : IComparable
2248 public KeyBlock (long _nFirst)
2250 nFirst = nLast = _nFirst;
2254 public ArrayList rgKeys = null;
2257 get { return (int) (nLast - nFirst + 1); }
2259 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2261 return kbLast.nLast - kbFirst.nFirst + 1;
2263 public int CompareTo (object obj)
2265 KeyBlock kb = (KeyBlock) obj;
2266 int nLength = Length;
2267 int nLengthOther = kb.Length;
2268 if (nLengthOther == nLength)
2269 return (int) (kb.nFirst - nFirst);
2270 return nLength - nLengthOther;
2275 /// This method emits code for a lookup-based switch statement (non-string)
2276 /// Basically it groups the cases into blocks that are at least half full,
2277 /// and then spits out individual lookup opcodes for each block.
2278 /// It emits the longest blocks first, and short blocks are just
2279 /// handled with direct compares.
2281 /// <param name="ec"></param>
2282 /// <param name="val"></param>
2283 /// <returns></returns>
2284 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
2286 int cElements = Elements.Count;
2287 object [] rgKeys = new object [cElements];
2288 Elements.Keys.CopyTo (rgKeys, 0);
2289 Array.Sort (rgKeys);
2291 // initialize the block list with one element per key
2292 ArrayList rgKeyBlocks = new ArrayList ();
2293 foreach (object key in rgKeys)
2294 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
2297 // iteratively merge the blocks while they are at least half full
2298 // there's probably a really cool way to do this with a tree...
2299 while (rgKeyBlocks.Count > 1)
2301 ArrayList rgKeyBlocksNew = new ArrayList ();
2302 kbCurr = (KeyBlock) rgKeyBlocks [0];
2303 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2305 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2306 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
2309 kbCurr.nLast = kb.nLast;
2313 // start a new block
2314 rgKeyBlocksNew.Add (kbCurr);
2318 rgKeyBlocksNew.Add (kbCurr);
2319 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2321 rgKeyBlocks = rgKeyBlocksNew;
2324 // initialize the key lists
2325 foreach (KeyBlock kb in rgKeyBlocks)
2326 kb.rgKeys = new ArrayList ();
2328 // fill the key lists
2330 if (rgKeyBlocks.Count > 0) {
2331 kbCurr = (KeyBlock) rgKeyBlocks [0];
2332 foreach (object key in rgKeys)
2334 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
2335 System.Convert.ToInt64 (key) > kbCurr.nLast;
2337 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2338 kbCurr.rgKeys.Add (key);
2342 // sort the blocks so we can tackle the largest ones first
2343 rgKeyBlocks.Sort ();
2345 // okay now we can start...
2346 ILGenerator ig = ec.ig;
2347 Label lblEnd = ig.DefineLabel (); // at the end ;-)
2348 Label lblDefault = ig.DefineLabel ();
2350 Type typeKeys = null;
2351 if (rgKeys.Length > 0)
2352 typeKeys = rgKeys [0].GetType (); // used for conversions
2356 if (TypeManager.IsEnumType (SwitchType))
2357 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2359 compare_type = SwitchType;
2361 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2363 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2364 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2367 foreach (object key in kb.rgKeys)
2369 ig.Emit (OpCodes.Ldloc, val);
2370 EmitObjectInteger (ig, key);
2371 SwitchLabel sl = (SwitchLabel) Elements [key];
2372 ig.Emit (OpCodes.Beq, sl.ILLabel);
2377 // TODO: if all the keys in the block are the same and there are
2378 // no gaps/defaults then just use a range-check.
2379 if (compare_type == TypeManager.int64_type ||
2380 compare_type == TypeManager.uint64_type)
2382 // TODO: optimize constant/I4 cases
2384 // check block range (could be > 2^31)
2385 ig.Emit (OpCodes.Ldloc, val);
2386 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2387 ig.Emit (OpCodes.Blt, lblDefault);
2388 ig.Emit (OpCodes.Ldloc, val);
2389 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
2390 ig.Emit (OpCodes.Bgt, lblDefault);
2393 ig.Emit (OpCodes.Ldloc, val);
2396 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2397 ig.Emit (OpCodes.Sub);
2399 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
2404 ig.Emit (OpCodes.Ldloc, val);
2405 int nFirst = (int) kb.nFirst;
2408 IntConstant.EmitInt (ig, nFirst);
2409 ig.Emit (OpCodes.Sub);
2411 else if (nFirst < 0)
2413 IntConstant.EmitInt (ig, -nFirst);
2414 ig.Emit (OpCodes.Add);
2418 // first, build the list of labels for the switch
2420 int cJumps = kb.Length;
2421 Label [] rgLabels = new Label [cJumps];
2422 for (int iJump = 0; iJump < cJumps; iJump++)
2424 object key = kb.rgKeys [iKey];
2425 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
2427 SwitchLabel sl = (SwitchLabel) Elements [key];
2428 rgLabels [iJump] = sl.ILLabel;
2432 rgLabels [iJump] = lblDefault;
2434 // emit the switch opcode
2435 ig.Emit (OpCodes.Switch, rgLabels);
2438 // mark the default for this block
2440 ig.MarkLabel (lblDefault);
2443 // TODO: find the default case and emit it here,
2444 // to prevent having to do the following jump.
2445 // make sure to mark other labels in the default section
2447 // the last default just goes to the end
2448 ig.Emit (OpCodes.Br, lblDefault);
2450 // now emit the code for the sections
2451 bool fFoundDefault = false;
2452 bool fAllReturn = true;
2453 foreach (SwitchSection ss in Sections)
2455 foreach (SwitchLabel sl in ss.Labels)
2457 ig.MarkLabel (sl.ILLabel);
2458 ig.MarkLabel (sl.ILLabelCode);
2459 if (sl.Label == null)
2461 ig.MarkLabel (lblDefault);
2462 fFoundDefault = true;
2465 bool returns = ss.Block.Emit (ec);
2466 fAllReturn &= returns;
2467 //ig.Emit (OpCodes.Br, lblEnd);
2470 if (!fFoundDefault) {
2471 ig.MarkLabel (lblDefault);
2474 ig.MarkLabel (lblEnd);
2479 // This simple emit switch works, but does not take advantage of the
2481 // TODO: remove non-string logic from here
2482 // TODO: binary search strings?
2484 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
2486 ILGenerator ig = ec.ig;
2487 Label end_of_switch = ig.DefineLabel ();
2488 Label next_test = ig.DefineLabel ();
2489 Label null_target = ig.DefineLabel ();
2490 bool default_found = false;
2491 bool first_test = true;
2492 bool pending_goto_end = false;
2493 bool all_return = true;
2496 ig.Emit (OpCodes.Ldloc, val);
2498 if (Elements.Contains (NullLiteral.Null)){
2499 ig.Emit (OpCodes.Brfalse, null_target);
2501 ig.Emit (OpCodes.Brfalse, default_target);
2503 ig.Emit (OpCodes.Ldloc, val);
2504 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
2505 ig.Emit (OpCodes.Stloc, val);
2507 int section_count = Sections.Count;
2508 for (int section = 0; section < section_count; section++){
2509 SwitchSection ss = (SwitchSection) Sections [section];
2510 Label sec_begin = ig.DefineLabel ();
2512 if (pending_goto_end)
2513 ig.Emit (OpCodes.Br, end_of_switch);
2515 int label_count = ss.Labels.Count;
2517 for (int label = 0; label < label_count; label++){
2518 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
2519 ig.MarkLabel (sl.ILLabel);
2522 ig.MarkLabel (next_test);
2523 next_test = ig.DefineLabel ();
2526 // If we are the default target
2528 if (sl.Label == null){
2529 ig.MarkLabel (default_target);
2530 default_found = true;
2532 object lit = sl.Converted;
2534 if (lit is NullLiteral){
2536 if (label_count == 1)
2537 ig.Emit (OpCodes.Br, next_test);
2541 StringConstant str = (StringConstant) lit;
2543 ig.Emit (OpCodes.Ldloc, val);
2544 ig.Emit (OpCodes.Ldstr, str.Value);
2545 if (label_count == 1)
2546 ig.Emit (OpCodes.Bne_Un, next_test);
2548 if (label+1 == label_count)
2549 ig.Emit (OpCodes.Bne_Un, next_test);
2551 ig.Emit (OpCodes.Beq, sec_begin);
2556 ig.MarkLabel (null_target);
2557 ig.MarkLabel (sec_begin);
2558 foreach (SwitchLabel sl in ss.Labels)
2559 ig.MarkLabel (sl.ILLabelCode);
2561 bool returns = ss.Block.Emit (ec);
2563 pending_goto_end = false;
2566 pending_goto_end = true;
2570 if (!default_found){
2571 ig.MarkLabel (default_target);
2574 ig.MarkLabel (next_test);
2575 ig.MarkLabel (end_of_switch);
2580 public override bool Resolve (EmitContext ec)
2582 Expr = Expr.Resolve (ec);
2586 new_expr = SwitchGoverningType (ec, Expr.Type);
2587 if (new_expr == null){
2588 Report.Error (151, loc, "An integer type or string was expected for switch");
2593 SwitchType = new_expr.Type;
2595 if (!CheckSwitch (ec))
2598 Switch old_switch = ec.Switch;
2600 ec.Switch.SwitchType = SwitchType;
2602 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
2605 foreach (SwitchSection ss in Sections){
2607 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.SwitchSection);
2611 if (ss.Block.Resolve (ec) != true)
2617 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.SwitchSection);
2619 ec.EndFlowBranching ();
2620 ec.Switch = old_switch;
2625 protected override bool DoEmit (EmitContext ec)
2627 // Store variable for comparission purposes
2628 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2630 ec.ig.Emit (OpCodes.Stloc, value);
2632 ILGenerator ig = ec.ig;
2634 default_target = ig.DefineLabel ();
2637 // Setup the codegen context
2639 Label old_end = ec.LoopEnd;
2640 Switch old_switch = ec.Switch;
2642 ec.LoopEnd = ig.DefineLabel ();
2647 if (SwitchType == TypeManager.string_type)
2648 all_return = SimpleSwitchEmit (ec, value);
2650 all_return = TableSwitchEmit (ec, value);
2652 // Restore context state.
2653 ig.MarkLabel (ec.LoopEnd);
2656 // Restore the previous context
2658 ec.LoopEnd = old_end;
2659 ec.Switch = old_switch;
2665 public class Lock : Statement {
2667 Statement Statement;
2669 public Lock (Expression expr, Statement stmt, Location l)
2676 public override bool Resolve (EmitContext ec)
2678 expr = expr.Resolve (ec);
2679 return Statement.Resolve (ec) && expr != null;
2682 protected override bool DoEmit (EmitContext ec)
2684 Type type = expr.Type;
2687 if (type.IsValueType){
2688 Report.Error (185, loc, "lock statement requires the expression to be " +
2689 " a reference type (type is: `" +
2690 TypeManager.CSharpName (type) + "'");
2694 ILGenerator ig = ec.ig;
2695 LocalBuilder temp = ig.DeclareLocal (type);
2698 ig.Emit (OpCodes.Dup);
2699 ig.Emit (OpCodes.Stloc, temp);
2700 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2703 Label end = ig.BeginExceptionBlock ();
2704 bool old_in_try = ec.InTry;
2706 Label finish = ig.DefineLabel ();
2707 val = Statement.Emit (ec);
2708 ec.InTry = old_in_try;
2709 // ig.Emit (OpCodes.Leave, finish);
2711 ig.MarkLabel (finish);
2714 ig.BeginFinallyBlock ();
2715 ig.Emit (OpCodes.Ldloc, temp);
2716 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2717 ig.EndExceptionBlock ();
2723 public class Unchecked : Statement {
2724 public readonly Block Block;
2726 public Unchecked (Block b)
2732 public override bool Resolve (EmitContext ec)
2734 bool previous_state = ec.CheckState;
2735 bool previous_state_const = ec.ConstantCheckState;
2737 ec.CheckState = false;
2738 ec.ConstantCheckState = false;
2739 bool ret = Block.Resolve (ec);
2740 ec.CheckState = previous_state;
2741 ec.ConstantCheckState = previous_state_const;
2746 protected override bool DoEmit (EmitContext ec)
2748 bool previous_state = ec.CheckState;
2749 bool previous_state_const = ec.ConstantCheckState;
2752 ec.CheckState = false;
2753 ec.ConstantCheckState = false;
2754 val = Block.Emit (ec);
2755 ec.CheckState = previous_state;
2756 ec.ConstantCheckState = previous_state_const;
2762 public class Checked : Statement {
2763 public readonly Block Block;
2765 public Checked (Block b)
2768 b.Unchecked = false;
2771 public override bool Resolve (EmitContext ec)
2773 bool previous_state = ec.CheckState;
2774 bool previous_state_const = ec.ConstantCheckState;
2776 ec.CheckState = true;
2777 ec.ConstantCheckState = true;
2778 bool ret = Block.Resolve (ec);
2779 ec.CheckState = previous_state;
2780 ec.ConstantCheckState = previous_state_const;
2785 protected override bool DoEmit (EmitContext ec)
2787 bool previous_state = ec.CheckState;
2788 bool previous_state_const = ec.ConstantCheckState;
2791 ec.CheckState = true;
2792 ec.ConstantCheckState = true;
2793 val = Block.Emit (ec);
2794 ec.CheckState = previous_state;
2795 ec.ConstantCheckState = previous_state_const;
2801 public class Unsafe : Statement {
2802 public readonly Block Block;
2804 public Unsafe (Block b)
2809 public override bool Resolve (EmitContext ec)
2811 bool previous_state = ec.InUnsafe;
2815 val = Block.Resolve (ec);
2816 ec.InUnsafe = previous_state;
2821 protected override bool DoEmit (EmitContext ec)
2823 bool previous_state = ec.InUnsafe;
2827 val = Block.Emit (ec);
2828 ec.InUnsafe = previous_state;
2837 public class Fixed : Statement {
2839 ArrayList declarators;
2840 Statement statement;
2845 public bool is_object;
2846 public LocalInfo vi;
2847 public Expression expr;
2848 public Expression converted;
2851 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
2854 declarators = decls;
2859 public override bool Resolve (EmitContext ec)
2862 Expression.UnsafeError (loc);
2866 expr_type = ec.DeclSpace.ResolveType (type, false, loc);
2867 if (expr_type == null)
2870 if (ec.RemapToProxy){
2871 Report.Error (-210, loc, "Fixed statement not allowed in iterators");
2875 data = new FixedData [declarators.Count];
2877 if (!expr_type.IsPointer){
2878 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
2883 foreach (Pair p in declarators){
2884 LocalInfo vi = (LocalInfo) p.First;
2885 Expression e = (Expression) p.Second;
2887 vi.VariableInfo = null;
2891 // The rules for the possible declarators are pretty wise,
2892 // but the production on the grammar is more concise.
2894 // So we have to enforce these rules here.
2896 // We do not resolve before doing the case 1 test,
2897 // because the grammar is explicit in that the token &
2898 // is present, so we need to test for this particular case.
2902 // Case 1: & object.
2904 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2905 Expression child = ((Unary) e).Expr;
2908 if (child is ParameterReference || child is LocalVariableReference){
2911 "No need to use fixed statement for parameters or " +
2912 "local variable declarations (address is already " +
2917 ec.InFixedInitializer = true;
2919 ec.InFixedInitializer = false;
2923 child = ((Unary) e).Expr;
2925 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2928 data [i].is_object = true;
2930 data [i].converted = null;
2937 ec.InFixedInitializer = true;
2939 ec.InFixedInitializer = false;
2946 if (e.Type.IsArray){
2947 Type array_type = TypeManager.GetElementType (e.Type);
2951 // Provided that array_type is unmanaged,
2953 if (!TypeManager.VerifyUnManaged (array_type, loc))
2957 // and T* is implicitly convertible to the
2958 // pointer type given in the fixed statement.
2960 ArrayPtr array_ptr = new ArrayPtr (e, loc);
2962 Expression converted = Convert.ImplicitConversionRequired (
2963 ec, array_ptr, vi.VariableType, loc);
2964 if (converted == null)
2967 data [i].is_object = false;
2969 data [i].converted = converted;
2979 if (e.Type == TypeManager.string_type){
2980 data [i].is_object = false;
2982 data [i].converted = null;
2988 return statement.Resolve (ec);
2991 protected override bool DoEmit (EmitContext ec)
2993 ILGenerator ig = ec.ig;
2995 bool is_ret = false;
2996 LocalBuilder [] clear_list = new LocalBuilder [data.Length];
2998 for (int i = 0; i < data.Length; i++) {
2999 LocalInfo vi = data [i].vi;
3002 // Case 1: & object.
3004 if (data [i].is_object) {
3006 // Store pointer in pinned location
3008 data [i].expr.Emit (ec);
3009 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3010 clear_list [i] = vi.LocalBuilder;
3017 if (data [i].expr.Type.IsArray){
3019 // Store pointer in pinned location
3021 data [i].converted.Emit (ec);
3023 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3024 clear_list [i] = vi.LocalBuilder;
3031 if (data [i].expr.Type == TypeManager.string_type){
3032 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
3033 TypeManager.MakePinned (pinned_string);
3034 clear_list [i] = pinned_string;
3036 data [i].expr.Emit (ec);
3037 ig.Emit (OpCodes.Stloc, pinned_string);
3039 Expression sptr = new StringPtr (pinned_string, loc);
3040 Expression converted = Convert.ImplicitConversionRequired (
3041 ec, sptr, vi.VariableType, loc);
3043 if (converted == null)
3046 converted.Emit (ec);
3047 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3051 is_ret = statement.Emit (ec);
3056 // Clear the pinned variable
3058 for (int i = 0; i < data.Length; i++) {
3059 LocalInfo vi = data [i].vi;
3061 if (data [i].is_object || data [i].expr.Type.IsArray) {
3062 ig.Emit (OpCodes.Ldc_I4_0);
3063 ig.Emit (OpCodes.Conv_U);
3064 ig.Emit (OpCodes.Stloc, clear_list [i]);
3065 } else if (data [i].expr.Type == TypeManager.string_type){
3066 ig.Emit (OpCodes.Ldnull);
3067 ig.Emit (OpCodes.Stloc, clear_list [i]);
3075 public class Catch {
3076 public readonly string Name;
3077 public readonly Block Block;
3078 public readonly Location Location;
3080 Expression type_expr;
3083 public Catch (Expression type, string name, Block block, Location l)
3091 public Type CatchType {
3097 public bool IsGeneral {
3099 return type_expr == null;
3103 public bool Resolve (EmitContext ec)
3105 if (type_expr != null) {
3106 type = ec.DeclSpace.ResolveType (type_expr, false, Location);
3110 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3111 Report.Error (155, Location,
3112 "The type caught or thrown must be derived " +
3113 "from System.Exception");
3119 if (!Block.Resolve (ec))
3126 public class Try : Statement {
3127 public readonly Block Fini, Block;
3128 public readonly ArrayList Specific;
3129 public readonly Catch General;
3132 // specific, general and fini might all be null.
3134 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3136 if (specific == null && general == null){
3137 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3141 this.Specific = specific;
3142 this.General = general;
3147 public override bool Resolve (EmitContext ec)
3151 ec.StartFlowBranching (FlowBranching.BranchingType.Exception, Block.StartLocation);
3153 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3155 bool old_in_try = ec.InTry;
3158 if (!Block.Resolve (ec))
3161 ec.InTry = old_in_try;
3163 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3165 Report.Debug (1, "START OF CATCH BLOCKS", vector);
3167 foreach (Catch c in Specific){
3168 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Catch);
3169 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3171 if (c.Name != null) {
3172 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
3174 throw new Exception ();
3176 vi.VariableInfo = null;
3179 bool old_in_catch = ec.InCatch;
3182 if (!c.Resolve (ec))
3185 ec.InCatch = old_in_catch;
3188 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
3190 if (General != null){
3191 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Catch);
3192 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3194 bool old_in_catch = ec.InCatch;
3197 if (!General.Resolve (ec))
3200 ec.InCatch = old_in_catch;
3203 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
3207 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Finally);
3208 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3210 bool old_in_finally = ec.InFinally;
3211 ec.InFinally = true;
3213 if (!Fini.Resolve (ec))
3216 ec.InFinally = old_in_finally;
3219 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3221 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3223 Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
3225 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3226 // Unfortunately, System.Reflection.Emit automatically emits a leave
3227 // to the end of the finally block. This is a problem if `returns'
3228 // is true since we may jump to a point after the end of the method.
3229 // As a workaround, emit an explicit ret here.
3230 ec.NeedExplicitReturn = true;
3236 protected override bool DoEmit (EmitContext ec)
3238 ILGenerator ig = ec.ig;
3240 Label finish = ig.DefineLabel ();;
3244 end = ig.BeginExceptionBlock ();
3245 bool old_in_try = ec.InTry;
3247 returns = Block.Emit (ec);
3248 ec.InTry = old_in_try;
3251 // System.Reflection.Emit provides this automatically:
3252 // ig.Emit (OpCodes.Leave, finish);
3254 bool old_in_catch = ec.InCatch;
3256 DeclSpace ds = ec.DeclSpace;
3258 foreach (Catch c in Specific){
3261 ig.BeginCatchBlock (c.CatchType);
3263 if (c.Name != null){
3264 vi = c.Block.GetLocalInfo (c.Name);
3266 throw new Exception ("Variable does not exist in this block");
3268 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3270 ig.Emit (OpCodes.Pop);
3272 if (!c.Block.Emit (ec))
3276 if (General != null){
3277 ig.BeginCatchBlock (TypeManager.object_type);
3278 ig.Emit (OpCodes.Pop);
3279 if (!General.Block.Emit (ec))
3282 ec.InCatch = old_in_catch;
3284 ig.MarkLabel (finish);
3286 ig.BeginFinallyBlock ();
3287 bool old_in_finally = ec.InFinally;
3288 ec.InFinally = true;
3290 ec.InFinally = old_in_finally;
3293 ig.EndExceptionBlock ();
3300 public class Using : Statement {
3301 object expression_or_block;
3302 Statement Statement;
3307 Expression [] converted_vars;
3308 ExpressionStatement [] assign;
3310 public Using (object expression_or_block, Statement stmt, Location l)
3312 this.expression_or_block = expression_or_block;
3318 // Resolves for the case of using using a local variable declaration.
3320 bool ResolveLocalVariableDecls (EmitContext ec)
3322 bool need_conv = false;
3323 expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
3326 if (expr_type == null)
3330 // The type must be an IDisposable or an implicit conversion
3333 converted_vars = new Expression [var_list.Count];
3334 assign = new ExpressionStatement [var_list.Count];
3335 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3336 foreach (DictionaryEntry e in var_list){
3337 Expression var = (Expression) e.Key;
3339 var = var.ResolveLValue (ec, new EmptyExpression ());
3343 converted_vars [i] = Convert.ImplicitConversionRequired (
3344 ec, var, TypeManager.idisposable_type, loc);
3346 if (converted_vars [i] == null)
3354 foreach (DictionaryEntry e in var_list){
3355 LocalVariableReference var = (LocalVariableReference) e.Key;
3356 Expression new_expr = (Expression) e.Value;
3359 a = new Assign (var, new_expr, loc);
3365 converted_vars [i] = var;
3366 assign [i] = (ExpressionStatement) a;
3373 bool ResolveExpression (EmitContext ec)
3375 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3376 conv = Convert.ImplicitConversionRequired (
3377 ec, expr, TypeManager.idisposable_type, loc);
3387 // Emits the code for the case of using using a local variable declaration.
3389 bool EmitLocalVariableDecls (EmitContext ec)
3391 ILGenerator ig = ec.ig;
3394 bool old_in_try = ec.InTry;
3396 for (i = 0; i < assign.Length; i++) {
3397 assign [i].EmitStatement (ec);
3399 ig.BeginExceptionBlock ();
3401 Statement.Emit (ec);
3402 ec.InTry = old_in_try;
3404 bool old_in_finally = ec.InFinally;
3405 ec.InFinally = true;
3406 var_list.Reverse ();
3407 foreach (DictionaryEntry e in var_list){
3408 LocalVariableReference var = (LocalVariableReference) e.Key;
3409 Label skip = ig.DefineLabel ();
3412 ig.BeginFinallyBlock ();
3415 ig.Emit (OpCodes.Brfalse, skip);
3416 converted_vars [i].Emit (ec);
3417 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3418 ig.MarkLabel (skip);
3419 ig.EndExceptionBlock ();
3421 ec.InFinally = old_in_finally;
3426 bool EmitExpression (EmitContext ec)
3429 // Make a copy of the expression and operate on that.
3431 ILGenerator ig = ec.ig;
3432 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
3437 ig.Emit (OpCodes.Stloc, local_copy);
3439 bool old_in_try = ec.InTry;
3441 ig.BeginExceptionBlock ();
3442 Statement.Emit (ec);
3443 ec.InTry = old_in_try;
3445 Label skip = ig.DefineLabel ();
3446 bool old_in_finally = ec.InFinally;
3447 ig.BeginFinallyBlock ();
3448 ig.Emit (OpCodes.Ldloc, local_copy);
3449 ig.Emit (OpCodes.Brfalse, skip);
3450 ig.Emit (OpCodes.Ldloc, local_copy);
3451 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3452 ig.MarkLabel (skip);
3453 ec.InFinally = old_in_finally;
3454 ig.EndExceptionBlock ();
3459 public override bool Resolve (EmitContext ec)
3461 if (expression_or_block is DictionaryEntry){
3462 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
3463 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
3465 if (!ResolveLocalVariableDecls (ec))
3468 } else if (expression_or_block is Expression){
3469 expr = (Expression) expression_or_block;
3471 expr = expr.Resolve (ec);
3475 expr_type = expr.Type;
3477 if (!ResolveExpression (ec))
3481 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
3483 bool ok = Statement.Resolve (ec);
3486 ec.KillFlowBranching ();
3490 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3492 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3493 // Unfortunately, System.Reflection.Emit automatically emits a leave
3494 // to the end of the finally block. This is a problem if `returns'
3495 // is true since we may jump to a point after the end of the method.
3496 // As a workaround, emit an explicit ret here.
3497 ec.NeedExplicitReturn = true;
3503 protected override bool DoEmit (EmitContext ec)
3505 if (expression_or_block is DictionaryEntry)
3506 return EmitLocalVariableDecls (ec);
3507 else if (expression_or_block is Expression)
3508 return EmitExpression (ec);
3515 /// Implementation of the foreach C# statement
3517 public class Foreach : Statement {
3519 Expression variable;
3521 Statement statement;
3522 ForeachHelperMethods hm;
3523 Expression empty, conv;
3524 Type array_type, element_type;
3527 public Foreach (Expression type, LocalVariableReference var, Expression expr,
3528 Statement stmt, Location l)
3531 this.variable = var;
3537 public override bool Resolve (EmitContext ec)
3539 expr = expr.Resolve (ec);
3543 var_type = ec.DeclSpace.ResolveType (type, false, loc);
3544 if (var_type == null)
3548 // We need an instance variable. Not sure this is the best
3549 // way of doing this.
3551 // FIXME: When we implement propertyaccess, will those turn
3552 // out to return values in ExprClass? I think they should.
3554 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3555 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
3556 error1579 (expr.Type);
3560 if (expr.Type.IsArray) {
3561 array_type = expr.Type;
3562 element_type = TypeManager.GetElementType (array_type);
3564 empty = new EmptyExpression (element_type);
3566 hm = ProbeCollectionType (ec, expr.Type);
3568 error1579 (expr.Type);
3572 array_type = expr.Type;
3573 element_type = hm.element_type;
3575 empty = new EmptyExpression (hm.element_type);
3578 ec.StartFlowBranching (FlowBranching.BranchingType.LoopBlock, loc);
3579 ec.CurrentBranching.CreateSibling (FlowBranching.SiblingType.Conditional);
3583 // FIXME: maybe we can apply the same trick we do in the
3584 // array handling to avoid creating empty and conv in some cases.
3586 // Although it is not as important in this case, as the type
3587 // will not likely be object (what the enumerator will return).
3589 conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
3593 variable = variable.ResolveLValue (ec, empty);
3594 if (variable == null)
3597 if (!statement.Resolve (ec))
3600 ec.EndFlowBranching ();
3606 // Retrieves a `public bool MoveNext ()' method from the Type `t'
3608 static MethodInfo FetchMethodMoveNext (Type t)
3610 MemberList move_next_list;
3612 move_next_list = TypeContainer.FindMembers (
3613 t, MemberTypes.Method,
3614 BindingFlags.Public | BindingFlags.Instance,
3615 Type.FilterName, "MoveNext");
3616 if (move_next_list.Count == 0)
3619 foreach (MemberInfo m in move_next_list){
3620 MethodInfo mi = (MethodInfo) m;
3623 args = TypeManager.GetArgumentTypes (mi);
3624 if (args != null && args.Length == 0){
3625 if (mi.ReturnType == TypeManager.bool_type)
3633 // Retrieves a `public T get_Current ()' method from the Type `t'
3635 static MethodInfo FetchMethodGetCurrent (Type t)
3637 MemberList get_current_list;
3639 get_current_list = TypeContainer.FindMembers (
3640 t, MemberTypes.Method,
3641 BindingFlags.Public | BindingFlags.Instance,
3642 Type.FilterName, "get_Current");
3643 if (get_current_list.Count == 0)
3646 foreach (MemberInfo m in get_current_list){
3647 MethodInfo mi = (MethodInfo) m;
3650 args = TypeManager.GetArgumentTypes (mi);
3651 if (args != null && args.Length == 0)
3658 // This struct records the helper methods used by the Foreach construct
3660 class ForeachHelperMethods {
3661 public EmitContext ec;
3662 public MethodInfo get_enumerator;
3663 public MethodInfo move_next;
3664 public MethodInfo get_current;
3665 public Type element_type;
3666 public Type enumerator_type;
3667 public bool is_disposable;
3669 public ForeachHelperMethods (EmitContext ec)
3672 this.element_type = TypeManager.object_type;
3673 this.enumerator_type = TypeManager.ienumerator_type;
3674 this.is_disposable = true;
3678 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
3683 if (!(m is MethodInfo))
3686 if (m.Name != "GetEnumerator")
3689 MethodInfo mi = (MethodInfo) m;
3690 Type [] args = TypeManager.GetArgumentTypes (mi);
3692 if (args.Length != 0)
3695 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
3696 EmitContext ec = hm.ec;
3699 // Check whether GetEnumerator is accessible to us
3701 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
3703 Type declaring = mi.DeclaringType;
3704 if (prot == MethodAttributes.Private){
3705 if (declaring != ec.ContainerType)
3707 } else if (prot == MethodAttributes.FamANDAssem){
3708 // If from a different assembly, false
3709 if (!(mi is MethodBuilder))
3712 // Are we being invoked from the same class, or from a derived method?
3714 if (ec.ContainerType != declaring){
3715 if (!ec.ContainerType.IsSubclassOf (declaring))
3718 } else if (prot == MethodAttributes.FamORAssem){
3719 if (!(mi is MethodBuilder ||
3720 ec.ContainerType == declaring ||
3721 ec.ContainerType.IsSubclassOf (declaring)))
3723 } if (prot == MethodAttributes.Family){
3724 if (!(ec.ContainerType == declaring ||
3725 ec.ContainerType.IsSubclassOf (declaring)))
3729 if ((mi.ReturnType == TypeManager.ienumerator_type) && (declaring == TypeManager.string_type))
3731 // Apply the same optimization as MS: skip the GetEnumerator
3732 // returning an IEnumerator, and use the one returning a
3733 // CharEnumerator instead. This allows us to avoid the
3734 // try-finally block and the boxing.
3739 // Ok, we can access it, now make sure that we can do something
3740 // with this `GetEnumerator'
3743 if (mi.ReturnType == TypeManager.ienumerator_type ||
3744 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
3745 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
3746 if (declaring != TypeManager.string_type) {
3747 hm.move_next = TypeManager.bool_movenext_void;
3748 hm.get_current = TypeManager.object_getcurrent_void;
3754 // Ok, so they dont return an IEnumerable, we will have to
3755 // find if they support the GetEnumerator pattern.
3757 Type return_type = mi.ReturnType;
3759 hm.move_next = FetchMethodMoveNext (return_type);
3760 if (hm.move_next == null)
3762 hm.get_current = FetchMethodGetCurrent (return_type);
3763 if (hm.get_current == null)
3766 hm.element_type = hm.get_current.ReturnType;
3767 hm.enumerator_type = return_type;
3768 hm.is_disposable = !hm.enumerator_type.IsSealed ||
3769 TypeManager.ImplementsInterface (
3770 hm.enumerator_type, TypeManager.idisposable_type);
3776 /// This filter is used to find the GetEnumerator method
3777 /// on which IEnumerator operates
3779 static MemberFilter FilterEnumerator;
3783 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
3786 void error1579 (Type t)
3788 Report.Error (1579, loc,
3789 "foreach statement cannot operate on variables of type `" +
3790 t.FullName + "' because that class does not provide a " +
3791 " GetEnumerator method or it is inaccessible");
3794 static bool TryType (Type t, ForeachHelperMethods hm)
3798 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
3799 BindingFlags.Public | BindingFlags.NonPublic |
3800 BindingFlags.Instance | BindingFlags.DeclaredOnly,
3801 FilterEnumerator, hm);
3806 hm.get_enumerator = (MethodInfo) mi [0];
3811 // Looks for a usable GetEnumerator in the Type, and if found returns
3812 // the three methods that participate: GetEnumerator, MoveNext and get_Current
3814 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
3816 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
3818 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
3819 if (TryType (tt, hm))
3825 // Now try to find the method in the interfaces
3828 Type [] ifaces = t.GetInterfaces ();
3830 foreach (Type i in ifaces){
3831 if (TryType (i, hm))
3836 // Since TypeBuilder.GetInterfaces only returns the interface
3837 // types for this type, we have to keep looping, but once
3838 // we hit a non-TypeBuilder (ie, a Type), then we know we are
3839 // done, because it returns all the types
3841 if ((t is TypeBuilder))
3851 // FIXME: possible optimization.
3852 // We might be able to avoid creating `empty' if the type is the sam
3854 bool EmitCollectionForeach (EmitContext ec)
3856 ILGenerator ig = ec.ig;
3857 VariableStorage enumerator, disposable;
3859 enumerator = new VariableStorage (ec, hm.enumerator_type);
3860 if (hm.is_disposable)
3861 disposable = new VariableStorage (ec, TypeManager.idisposable_type);
3865 enumerator.EmitThis ();
3867 // Instantiate the enumerator
3869 if (expr.Type.IsValueType){
3870 if (expr is IMemoryLocation){
3871 IMemoryLocation ml = (IMemoryLocation) expr;
3873 ml.AddressOf (ec, AddressOp.Load);
3875 throw new Exception ("Expr " + expr + " of type " + expr.Type +
3876 " does not implement IMemoryLocation");
3877 ig.Emit (OpCodes.Call, hm.get_enumerator);
3880 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
3882 enumerator.EmitStore ();
3885 // Protect the code in a try/finalize block, so that
3886 // if the beast implement IDisposable, we get rid of it
3889 bool old_in_try = ec.InTry;
3891 if (hm.is_disposable) {
3892 l = ig.BeginExceptionBlock ();
3896 Label end_try = ig.DefineLabel ();
3898 ig.MarkLabel (ec.LoopBegin);
3899 enumerator.EmitLoad ();
3900 ig.Emit (OpCodes.Callvirt, hm.move_next);
3901 ig.Emit (OpCodes.Brfalse, end_try);
3905 enumerator.EmitLoad ();
3906 ig.Emit (OpCodes.Callvirt, hm.get_current);
3910 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
3912 ((IAssignMethod)variable).EmitAssign (ec, conv);
3914 statement.Emit (ec);
3915 ig.Emit (OpCodes.Br, ec.LoopBegin);
3916 ig.MarkLabel (end_try);
3917 ec.InTry = old_in_try;
3919 // The runtime provides this for us.
3920 // ig.Emit (OpCodes.Leave, end);
3923 // Now the finally block
3925 if (hm.is_disposable) {
3926 Label end_finally = ig.DefineLabel ();
3927 bool old_in_finally = ec.InFinally;
3928 ec.InFinally = true;
3929 ig.BeginFinallyBlock ();
3931 disposable.EmitThis ();
3932 enumerator.EmitThis ();
3933 enumerator.EmitLoad ();
3934 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
3935 disposable.EmitStore ();
3936 disposable.EmitLoad ();
3937 ig.Emit (OpCodes.Brfalse, end_finally);
3938 disposable.EmitThis ();
3939 disposable.EmitLoad ();
3940 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3941 ig.MarkLabel (end_finally);
3942 ec.InFinally = old_in_finally;
3944 // The runtime generates this anyways.
3945 // ig.Emit (OpCodes.Endfinally);
3947 ig.EndExceptionBlock ();
3950 ig.MarkLabel (ec.LoopEnd);
3955 // FIXME: possible optimization.
3956 // We might be able to avoid creating `empty' if the type is the sam
3958 bool EmitArrayForeach (EmitContext ec)
3960 int rank = array_type.GetArrayRank ();
3961 ILGenerator ig = ec.ig;
3963 VariableStorage copy = new VariableStorage (ec, array_type);
3966 // Make our copy of the array
3973 VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
3977 counter.EmitThis ();
3978 ig.Emit (OpCodes.Ldc_I4_0);
3979 counter.EmitStore ();
3980 test = ig.DefineLabel ();
3981 ig.Emit (OpCodes.Br, test);
3983 loop = ig.DefineLabel ();
3984 ig.MarkLabel (loop);
3991 counter.EmitThis ();
3992 counter.EmitLoad ();
3995 // Load the value, we load the value using the underlying type,
3996 // then we use the variable.EmitAssign to load using the proper cast.
3998 ArrayAccess.EmitLoadOpcode (ig, element_type);
4001 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
4003 ((IAssignMethod)variable).EmitAssign (ec, conv);
4005 statement.Emit (ec);
4007 ig.MarkLabel (ec.LoopBegin);
4008 counter.EmitThis ();
4009 counter.EmitThis ();
4010 counter.EmitLoad ();
4011 ig.Emit (OpCodes.Ldc_I4_1);
4012 ig.Emit (OpCodes.Add);
4013 counter.EmitStore ();
4015 ig.MarkLabel (test);
4016 counter.EmitThis ();
4017 counter.EmitLoad ();
4020 ig.Emit (OpCodes.Ldlen);
4021 ig.Emit (OpCodes.Conv_I4);
4022 ig.Emit (OpCodes.Blt, loop);
4024 VariableStorage [] dim_len = new VariableStorage [rank];
4025 VariableStorage [] dim_count = new VariableStorage [rank];
4026 Label [] loop = new Label [rank];
4027 Label [] test = new Label [rank];
4030 for (dim = 0; dim < rank; dim++){
4031 dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
4032 dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
4033 test [dim] = ig.DefineLabel ();
4034 loop [dim] = ig.DefineLabel ();
4037 for (dim = 0; dim < rank; dim++){
4038 dim_len [dim].EmitThis ();
4041 IntLiteral.EmitInt (ig, dim);
4042 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
4043 dim_len [dim].EmitStore ();
4047 for (dim = 0; dim < rank; dim++){
4048 dim_count [dim].EmitThis ();
4049 ig.Emit (OpCodes.Ldc_I4_0);
4050 dim_count [dim].EmitStore ();
4051 ig.Emit (OpCodes.Br, test [dim]);
4052 ig.MarkLabel (loop [dim]);
4059 for (dim = 0; dim < rank; dim++){
4060 dim_count [dim].EmitThis ();
4061 dim_count [dim].EmitLoad ();
4065 // FIXME: Maybe we can cache the computation of `get'?
4067 Type [] args = new Type [rank];
4070 for (int i = 0; i < rank; i++)
4071 args [i] = TypeManager.int32_type;
4073 ModuleBuilder mb = CodeGen.ModuleBuilder;
4074 get = mb.GetArrayMethod (
4076 CallingConventions.HasThis| CallingConventions.Standard,
4078 ig.Emit (OpCodes.Call, get);
4081 ig.Emit (OpCodes.Stfld, ((FieldExpr) variable).FieldInfo);
4083 ((IAssignMethod)variable).EmitAssign (ec, conv);
4084 statement.Emit (ec);
4085 ig.MarkLabel (ec.LoopBegin);
4086 for (dim = rank - 1; dim >= 0; dim--){
4087 dim_count [dim].EmitThis ();
4088 dim_count [dim].EmitThis ();
4089 dim_count [dim].EmitLoad ();
4090 ig.Emit (OpCodes.Ldc_I4_1);
4091 ig.Emit (OpCodes.Add);
4092 dim_count [dim].EmitStore ();
4094 ig.MarkLabel (test [dim]);
4095 dim_count [dim].EmitThis ();
4096 dim_count [dim].EmitLoad ();
4097 dim_len [dim].EmitThis ();
4098 dim_len [dim].EmitLoad ();
4099 ig.Emit (OpCodes.Blt, loop [dim]);
4102 ig.MarkLabel (ec.LoopEnd);
4107 protected override bool DoEmit (EmitContext ec)
4111 ILGenerator ig = ec.ig;
4113 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4114 bool old_inloop = ec.InLoop;
4115 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
4116 ec.LoopBegin = ig.DefineLabel ();
4117 ec.LoopEnd = ig.DefineLabel ();
4119 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
4122 ret_val = EmitCollectionForeach (ec);
4124 ret_val = EmitArrayForeach (ec);
4126 ec.LoopBegin = old_begin;
4127 ec.LoopEnd = old_end;
4128 ec.InLoop = old_inloop;
4129 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;