2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Diagnostics;
18 namespace Mono.CSharp {
20 using System.Collections;
22 public abstract class Statement {
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (EmitContext ec)
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
49 if (warn && (RootContext.WarningLevel >= 2))
50 Report.Warning (162, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
59 protected void CheckObsolete (Type type)
61 ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type);
62 if (obsolete_attr == null)
65 AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, loc);
69 /// Return value indicates whether all code paths emitted return.
71 protected abstract void DoEmit (EmitContext ec);
74 /// Utility wrapper routine for Error, just to beautify the code
76 public void Error (int error, string format, params object[] args)
78 Error (error, String.Format (format, args));
81 public void Error (int error, string s)
83 if (!Location.IsNull (loc))
84 Report.Error (error, loc, s);
86 Report.Error (error, s);
90 /// Return value indicates whether all code paths emitted return.
92 public virtual void Emit (EmitContext ec)
99 public sealed class EmptyStatement : Statement {
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value = new EmptyStatement ();
105 public override bool Resolve (EmitContext ec)
110 protected override void DoEmit (EmitContext ec)
115 public class If : Statement {
117 public Statement TrueStatement;
118 public Statement FalseStatement;
122 public If (Expression expr, Statement trueStatement, Location l)
125 TrueStatement = trueStatement;
129 public If (Expression expr,
130 Statement trueStatement,
131 Statement falseStatement,
135 TrueStatement = trueStatement;
136 FalseStatement = falseStatement;
140 public override bool Resolve (EmitContext ec)
144 Report.Debug (1, "START IF BLOCK", loc);
146 expr = Expression.ResolveBoolean (ec, expr, loc);
152 Assign ass = expr as Assign;
153 if (ass != null && ass.Source is Constant) {
154 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
158 // Dead code elimination
160 if (expr is BoolConstant){
161 bool take = ((BoolConstant) expr).Value;
164 if (!TrueStatement.Resolve (ec))
167 if ((FalseStatement != null) &&
168 !FalseStatement.ResolveUnreachable (ec, true))
170 FalseStatement = null;
172 if (!TrueStatement.ResolveUnreachable (ec, true))
174 TrueStatement = null;
176 if ((FalseStatement != null) &&
177 !FalseStatement.Resolve (ec))
184 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
186 ok &= TrueStatement.Resolve (ec);
188 is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
190 ec.CurrentBranching.CreateSibling ();
192 if (FalseStatement != null)
193 ok &= FalseStatement.Resolve (ec);
195 ec.EndFlowBranching ();
197 Report.Debug (1, "END IF BLOCK", loc);
202 protected override void DoEmit (EmitContext ec)
204 ILGenerator ig = ec.ig;
205 Label false_target = ig.DefineLabel ();
209 // If we're a boolean expression, Resolve() already
210 // eliminated dead code for us.
212 if (expr is BoolConstant){
213 bool take = ((BoolConstant) expr).Value;
216 TrueStatement.Emit (ec);
217 else if (FalseStatement != null)
218 FalseStatement.Emit (ec);
223 expr.EmitBranchable (ec, false_target, false);
225 TrueStatement.Emit (ec);
227 if (FalseStatement != null){
228 bool branch_emitted = false;
230 end = ig.DefineLabel ();
232 ig.Emit (OpCodes.Br, end);
233 branch_emitted = true;
236 ig.MarkLabel (false_target);
237 FalseStatement.Emit (ec);
242 ig.MarkLabel (false_target);
247 public class Do : Statement {
248 public Expression expr;
249 public readonly Statement EmbeddedStatement;
252 public Do (Statement statement, Expression boolExpr, Location l)
255 EmbeddedStatement = statement;
259 public override bool Resolve (EmitContext ec)
263 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
265 if (!EmbeddedStatement.Resolve (ec))
268 expr = Expression.ResolveBoolean (ec, expr, loc);
271 else if (expr is BoolConstant){
272 bool res = ((BoolConstant) expr).Value;
278 ec.CurrentBranching.Infinite = infinite;
279 ec.EndFlowBranching ();
284 protected override void DoEmit (EmitContext ec)
286 ILGenerator ig = ec.ig;
287 Label loop = ig.DefineLabel ();
288 Label old_begin = ec.LoopBegin;
289 Label old_end = ec.LoopEnd;
291 ec.LoopBegin = ig.DefineLabel ();
292 ec.LoopEnd = ig.DefineLabel ();
295 EmbeddedStatement.Emit (ec);
296 ig.MarkLabel (ec.LoopBegin);
299 // Dead code elimination
301 if (expr is BoolConstant){
302 bool res = ((BoolConstant) expr).Value;
305 ec.ig.Emit (OpCodes.Br, loop);
307 expr.EmitBranchable (ec, loop, true);
309 ig.MarkLabel (ec.LoopEnd);
311 ec.LoopBegin = old_begin;
312 ec.LoopEnd = old_end;
316 public class While : Statement {
317 public Expression expr;
318 public readonly Statement Statement;
319 bool infinite, empty;
321 public While (Expression boolExpr, Statement statement, Location l)
323 this.expr = boolExpr;
324 Statement = statement;
328 public override bool Resolve (EmitContext ec)
332 expr = Expression.ResolveBoolean (ec, expr, loc);
337 // Inform whether we are infinite or not
339 if (expr is BoolConstant){
340 BoolConstant bc = (BoolConstant) expr;
342 if (bc.Value == false){
343 if (!Statement.ResolveUnreachable (ec, true))
351 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
353 ec.CurrentBranching.CreateSibling ();
355 if (!Statement.Resolve (ec))
358 ec.CurrentBranching.Infinite = infinite;
359 ec.EndFlowBranching ();
364 protected override void DoEmit (EmitContext ec)
369 ILGenerator ig = ec.ig;
370 Label old_begin = ec.LoopBegin;
371 Label old_end = ec.LoopEnd;
373 ec.LoopBegin = ig.DefineLabel ();
374 ec.LoopEnd = ig.DefineLabel ();
377 // Inform whether we are infinite or not
379 if (expr is BoolConstant){
380 ig.MarkLabel (ec.LoopBegin);
382 ig.Emit (OpCodes.Br, ec.LoopBegin);
385 // Inform that we are infinite (ie, `we return'), only
386 // if we do not `break' inside the code.
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 expr.EmitBranchable (ec, while_loop, true);
401 ig.MarkLabel (ec.LoopEnd);
404 ec.LoopBegin = old_begin;
405 ec.LoopEnd = old_end;
409 public class For : Statement {
411 readonly Statement InitStatement;
412 readonly Statement Increment;
413 readonly Statement Statement;
414 bool 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 if (!Statement.ResolveUnreachable (ec, true))
448 if ((Increment != null) &&
449 !Increment.ResolveUnreachable (ec, false))
459 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
461 ec.CurrentBranching.CreateSibling ();
463 if (!Statement.Resolve (ec))
466 if (Increment != null){
467 if (!Increment.Resolve (ec))
471 ec.CurrentBranching.Infinite = infinite;
472 ec.EndFlowBranching ();
477 protected override void DoEmit (EmitContext ec)
482 ILGenerator ig = ec.ig;
483 Label old_begin = ec.LoopBegin;
484 Label old_end = ec.LoopEnd;
485 Label loop = ig.DefineLabel ();
486 Label test = ig.DefineLabel ();
488 if (InitStatement != null && InitStatement != EmptyStatement.Value)
489 InitStatement.Emit (ec);
491 ec.LoopBegin = ig.DefineLabel ();
492 ec.LoopEnd = ig.DefineLabel ();
494 ig.Emit (OpCodes.Br, test);
498 ig.MarkLabel (ec.LoopBegin);
499 if (Increment != EmptyStatement.Value)
504 // If test is null, there is no test, and we are just
509 // The Resolve code already catches the case for
510 // Test == BoolConstant (false) so we know that
513 if (Test is BoolConstant)
514 ig.Emit (OpCodes.Br, loop);
516 Test.EmitBranchable (ec, loop, true);
519 ig.Emit (OpCodes.Br, loop);
520 ig.MarkLabel (ec.LoopEnd);
522 ec.LoopBegin = old_begin;
523 ec.LoopEnd = old_end;
527 public class StatementExpression : Statement {
528 ExpressionStatement expr;
530 public StatementExpression (ExpressionStatement expr, Location l)
536 public override bool Resolve (EmitContext ec)
539 expr = expr.ResolveStatement (ec);
543 protected override void DoEmit (EmitContext ec)
545 expr.EmitStatement (ec);
548 public override string ToString ()
550 return "StatementExpression (" + expr + ")";
555 /// Implements the return statement
557 public class Return : Statement {
558 public Expression Expr;
560 public Return (Expression expr, Location l)
568 public override bool Resolve (EmitContext ec)
570 AnonymousContainer am = ec.CurrentAnonymousMethod;
571 if ((am != null) && am.IsIterator && ec.InIterator) {
572 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
573 "statement to return a value, or yield break to end the iteration");
577 if (ec.ReturnType == null){
579 if (ec.CurrentAnonymousMethod != null){
580 Report.Error (1662, loc, String.Format (
581 "Anonymous method could not be converted to delegate " +
582 "since the return value does not match the delegate value"));
584 Error (127, "Return with a value not allowed here");
589 Error (126, "An object of type `{0}' is expected " +
590 "for the return statement",
591 TypeManager.CSharpName (ec.ReturnType));
595 Expr = Expr.Resolve (ec);
599 if (Expr.Type != ec.ReturnType) {
600 Expr = Convert.ImplicitConversionRequired (
601 ec, Expr, ec.ReturnType, loc);
607 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
609 if (ec.CurrentBranching.InTryOrCatch (true)) {
610 ec.CurrentBranching.AddFinallyVector (vector);
612 } else if (ec.InFinally) {
613 Error (157, "Control can not leave the body of the finally block");
616 vector.CheckOutParameters (ec.CurrentBranching);
619 ec.NeedReturnLabel ();
621 ec.CurrentBranching.CurrentUsageVector.Return ();
625 protected override void DoEmit (EmitContext ec)
631 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
635 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
637 ec.ig.Emit (OpCodes.Ret);
641 public class Goto : Statement {
643 LabeledStatement label;
645 public override bool Resolve (EmitContext ec)
647 label = ec.CurrentBranching.LookupLabel (target, loc);
651 // If this is a forward goto.
652 if (!label.IsDefined)
653 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
655 ec.CurrentBranching.CurrentUsageVector.Goto ();
656 label.AddReference ();
661 public Goto (string label, Location l)
667 public string Target {
673 protected override void DoEmit (EmitContext ec)
675 Label l = label.LabelTarget (ec);
676 ec.ig.Emit (OpCodes.Br, l);
680 public class LabeledStatement : Statement {
681 public readonly Location Location;
687 FlowBranching.UsageVector vectors;
689 public LabeledStatement (string label_name, Location l)
694 public Label LabelTarget (EmitContext ec)
699 label = ec.ig.DefineLabel ();
705 public bool IsDefined {
711 public bool HasBeenReferenced {
717 public void AddUsageVector (FlowBranching.UsageVector vector)
719 vector = vector.Clone ();
720 vector.Next = vectors;
724 public override bool Resolve (EmitContext ec)
726 ec.CurrentBranching.Label (vectors);
731 protected override void DoEmit (EmitContext ec)
733 if (ig != null && ig != ec.ig) {
734 Report.Error (1632, "Control cannot leave body of anonymous method");
738 ec.ig.MarkLabel (label);
741 public void AddReference ()
749 /// `goto default' statement
751 public class GotoDefault : Statement {
753 public GotoDefault (Location l)
758 public override bool Resolve (EmitContext ec)
760 ec.CurrentBranching.CurrentUsageVector.Goto ();
764 protected override void DoEmit (EmitContext ec)
766 if (ec.Switch == null){
767 Report.Error (153, loc, "goto default is only valid in a switch statement");
771 if (!ec.Switch.GotDefault){
772 Report.Error (159, loc, "No default target on switch statement");
775 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
780 /// `goto case' statement
782 public class GotoCase : Statement {
786 public GotoCase (Expression e, Location l)
792 public override bool Resolve (EmitContext ec)
794 if (ec.Switch == null){
795 Report.Error (153, loc, "goto case is only valid in a switch statement");
799 expr = expr.Resolve (ec);
803 if (!(expr is Constant)){
804 Report.Error (159, loc, "Target expression for goto case is not constant");
808 object val = Expression.ConvertIntLiteral (
809 (Constant) expr, ec.Switch.SwitchType, loc);
814 sl = (SwitchLabel) ec.Switch.Elements [val];
819 "No such label 'case " + val + "': for the goto case");
823 ec.CurrentBranching.CurrentUsageVector.Goto ();
827 protected override void DoEmit (EmitContext ec)
829 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
833 public class Throw : Statement {
836 public Throw (Expression expr, Location l)
842 public override bool Resolve (EmitContext ec)
844 ec.CurrentBranching.CurrentUsageVector.Throw ();
847 expr = expr.Resolve (ec);
851 ExprClass eclass = expr.eclass;
853 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
854 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
855 expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
861 if ((t != TypeManager.exception_type) &&
862 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
863 !(expr is NullLiteral)) {
865 "The type caught or thrown must be derived " +
866 "from System.Exception");
873 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
878 Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
884 protected override void DoEmit (EmitContext ec)
887 ec.ig.Emit (OpCodes.Rethrow);
891 ec.ig.Emit (OpCodes.Throw);
896 public class Break : Statement {
898 public Break (Location l)
905 public override bool Resolve (EmitContext ec)
907 if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
908 Error (139, "No enclosing loop or switch to continue to");
910 } else if (ec.InFinally && ec.CurrentBranching.BreakCrossesTryCatchBoundary()) {
911 Error (157, "Control can not leave the body of the finally block");
913 } else if (ec.CurrentBranching.InTryOrCatch (false))
914 ec.CurrentBranching.AddFinallyVector (
915 ec.CurrentBranching.CurrentUsageVector);
916 else if (ec.CurrentBranching.InLoop () || ec.CurrentBranching.InSwitch ())
917 ec.CurrentBranching.AddBreakVector (
918 ec.CurrentBranching.CurrentUsageVector);
920 crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
923 ec.NeedReturnLabel ();
925 ec.CurrentBranching.CurrentUsageVector.Break ();
929 protected override void DoEmit (EmitContext ec)
931 ILGenerator ig = ec.ig;
934 ig.Emit (OpCodes.Leave, ec.LoopEnd);
936 ig.Emit (OpCodes.Br, ec.LoopEnd);
941 public class Continue : Statement {
943 public Continue (Location l)
950 public override bool Resolve (EmitContext ec)
952 if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
953 Error (139, "No enclosing loop to continue to");
955 } else if (ec.InFinally) {
956 Error (157, "Control can not leave the body of the finally block");
958 } else if (ec.CurrentBranching.InTryOrCatch (false))
959 ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector);
961 crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
963 ec.CurrentBranching.CurrentUsageVector.Goto ();
967 protected override void DoEmit (EmitContext ec)
969 Label begin = ec.LoopBegin;
972 ec.ig.Emit (OpCodes.Leave, begin);
974 ec.ig.Emit (OpCodes.Br, begin);
979 // The information about a user-perceived local variable
981 public class LocalInfo {
982 public Expression Type;
985 // Most of the time a variable will be stored in a LocalBuilder
987 // But sometimes, it will be stored in a field (variables that have been
988 // hoisted by iterators or by anonymous methods). The context of the field will
989 // be stored in the EmitContext
992 public LocalBuilder LocalBuilder;
993 public FieldBuilder FieldBuilder;
995 public Type VariableType;
996 public readonly string Name;
997 public readonly Location Location;
998 public readonly Block Block;
1000 public VariableInfo VariableInfo;
1009 CompilerGenerated = 64
1012 public enum ReadOnlyContext: byte {
1019 ReadOnlyContext ro_context;
1021 public LocalInfo (Expression type, string name, Block block, Location l)
1029 public LocalInfo (TypeContainer tc, Block block, Location l)
1031 VariableType = tc.TypeBuilder;
1036 public bool IsThisAssigned (EmitContext ec, Location loc)
1038 if (VariableInfo == null)
1039 throw new Exception ();
1041 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1044 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1047 public bool IsAssigned (EmitContext ec)
1049 if (VariableInfo == null)
1050 throw new Exception ();
1052 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1055 public bool Resolve (EmitContext ec)
1057 if (VariableType == null) {
1058 TypeExpr texpr = Type.ResolveAsTypeTerminal (ec);
1062 VariableType = texpr.Type;
1065 if (VariableType == TypeManager.void_type) {
1066 Report.Error (1547, Location,
1067 "Keyword 'void' cannot be used in this context");
1071 if (VariableType.IsAbstract && VariableType.IsSealed) {
1072 Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
1075 // TODO: breaks the build
1076 // if (VariableType.IsPointer && !ec.InUnsafe)
1077 // Expression.UnsafeError (Location);
1083 // Whether the variable is Fixed (because its Pinned or its a value type)
1085 public bool IsFixed {
1087 if (((flags & Flags.Pinned) != 0) || TypeManager.IsValueType (VariableType))
1094 public bool IsCaptured {
1096 return (flags & Flags.Captured) != 0;
1100 flags |= Flags.Captured;
1104 public bool AddressTaken {
1106 return (flags & Flags.AddressTaken) != 0;
1110 flags |= Flags.AddressTaken;
1114 public bool CompilerGenerated {
1116 return (flags & Flags.CompilerGenerated) != 0;
1120 flags |= Flags.CompilerGenerated;
1124 public override string ToString ()
1126 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1127 Name, Type, VariableInfo, Location);
1132 return (flags & Flags.Used) != 0;
1135 flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
1139 public bool ReadOnly {
1141 return (flags & Flags.ReadOnly) != 0;
1145 public void SetReadOnlyContext (ReadOnlyContext context)
1147 flags |= Flags.ReadOnly;
1148 ro_context = context;
1151 public string GetReadOnlyContext ()
1154 throw new InternalErrorException ("Variable is not readonly");
1156 switch (ro_context) {
1157 case ReadOnlyContext.Fixed:
1158 return "fixed variable";
1159 case ReadOnlyContext.Foreach:
1160 return "foreach iteration variable";
1161 case ReadOnlyContext.Using:
1162 return "using variable";
1164 throw new NotImplementedException ();
1168 // Whether the variable is pinned, if Pinned the variable has been
1169 // allocated in a pinned slot with DeclareLocal.
1171 public bool Pinned {
1173 return (flags & Flags.Pinned) != 0;
1176 flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
1180 public bool IsThis {
1182 return (flags & Flags.IsThis) != 0;
1185 flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
1191 /// Block represents a C# block.
1195 /// This class is used in a number of places: either to represent
1196 /// explicit blocks that the programmer places or implicit blocks.
1198 /// Implicit blocks are used as labels or to introduce variable
1201 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1202 /// they contain extra information that is not necessary on normal blocks.
1204 public class Block : Statement {
1205 public Block Parent;
1206 public readonly Location StartLocation;
1207 public Location EndLocation = Location.Null;
1209 public readonly ToplevelBlock Toplevel;
1216 VariablesInitialized = 8,
1225 public bool Implicit {
1227 return (flags & Flags.Implicit) != 0;
1231 public bool Unchecked {
1233 return (flags & Flags.Unchecked) != 0;
1236 flags |= Flags.Unchecked;
1240 public bool Unsafe {
1242 return (flags & Flags.Unsafe) != 0;
1245 flags |= Flags.Unsafe;
1249 public bool HasVarargs {
1252 return Parent.HasVarargs;
1254 return (flags & Flags.HasVarargs) != 0;
1257 flags |= Flags.HasVarargs;
1262 // The statements in this block
1264 ArrayList statements;
1268 // An array of Blocks. We keep track of children just
1269 // to generate the local variable declarations.
1271 // Statements and child statements are handled through the
1277 // Labels. (label, block) pairs.
1282 // Keeps track of (name, type) pairs
1284 Hashtable variables;
1287 // Keeps track of constants
1288 Hashtable constants;
1291 // Temporary variables.
1293 ArrayList temporary_variables;
1296 // If this is a switch section, the enclosing switch block.
1300 protected static int id;
1304 public Block (Block parent)
1305 : this (parent, (Flags) 0, Location.Null, Location.Null)
1308 public Block (Block parent, Flags flags)
1309 : this (parent, flags, Location.Null, Location.Null)
1312 public Block (Block parent, Location start, Location end)
1313 : this (parent, (Flags) 0, start, end)
1316 public Block (Block parent, Flags flags, Location start, Location end)
1319 parent.AddChild (this);
1321 this.Parent = parent;
1323 this.StartLocation = start;
1324 this.EndLocation = end;
1327 statements = new ArrayList ();
1329 if ((flags & Flags.IsToplevel) != 0)
1330 Toplevel = (ToplevelBlock) this;
1332 Toplevel = parent.Toplevel;
1334 if (parent != null && Implicit) {
1335 if (parent.known_variables == null)
1336 parent.known_variables = new Hashtable ();
1337 // share with parent
1338 known_variables = parent.known_variables;
1343 public Block CreateSwitchBlock (Location start)
1345 Block new_block = new Block (this, start, start);
1346 new_block.switch_block = this;
1356 void AddChild (Block b)
1358 if (children == null)
1359 children = new ArrayList ();
1364 public void SetEndLocation (Location loc)
1370 /// Adds a label to the current block.
1374 /// false if the name already exists in this block. true
1378 public bool AddLabel (string name, LabeledStatement target, Location loc)
1380 if (switch_block != null)
1381 return switch_block.AddLabel (name, target, loc);
1384 while (cur != null) {
1385 if (cur.DoLookupLabel (name) != null) {
1387 140, loc, "The label '{0}' is a duplicate",
1398 while (cur != null) {
1399 if (cur.DoLookupLabel (name) != null) {
1402 "The label '{0}' shadows another label " +
1403 "by the same name in a containing scope.",
1408 if (children != null) {
1409 foreach (Block b in children) {
1410 LabeledStatement s = b.DoLookupLabel (name);
1416 "The label '{0}' shadows another " +
1417 "label by the same name in a " +
1418 "containing scope.",
1429 labels = new Hashtable ();
1431 labels.Add (name, target);
1435 public LabeledStatement LookupLabel (string name)
1437 LabeledStatement s = DoLookupLabel (name);
1441 if (children == null)
1444 foreach (Block child in children) {
1445 if (!child.Implicit)
1448 s = child.LookupLabel (name);
1456 LabeledStatement DoLookupLabel (string name)
1458 if (switch_block != null)
1459 return switch_block.LookupLabel (name);
1462 if (labels.Contains (name))
1463 return ((LabeledStatement) labels [name]);
1468 LocalInfo this_variable = null;
1471 // Returns the "this" instance variable of this block.
1472 // See AddThisVariable() for more information.
1474 public LocalInfo ThisVariable {
1476 for (Block b = this; b != null; b = b.Parent) {
1477 if (b.this_variable != null)
1478 return b.this_variable;
1485 Hashtable known_variables;
1488 // Marks a variable with name @name as being used in this or a child block.
1489 // If a variable name has been used in a child block, it's illegal to
1490 // declare a variable with the same name in the current block.
1492 void AddKnownVariable (string name, LocalInfo info)
1494 if (known_variables == null)
1495 known_variables = new Hashtable ();
1497 known_variables [name] = info;
1500 LocalInfo GetKnownVariableInfo (string name)
1502 if (known_variables == null)
1504 return (LocalInfo) known_variables [name];
1507 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1509 LocalInfo kvi = GetKnownVariableInfo (name);
1510 if (kvi == null || kvi.Block == this)
1513 if (known_variables != kvi.Block.known_variables) {
1514 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1515 Report.Error (135, loc, "'{0}' has a different meaning in a child block", name);
1520 // this block and kvi.Block are the same textual block.
1521 // However, different variables are extant.
1523 // Check if the variable is in scope in both blocks. We use
1524 // an indirect check that depends on AddVariable doing its
1525 // part in maintaining the invariant-meaning-in-block property.
1527 if (e is LocalVariableReference || (e is Constant && GetLocalInfo (name) != null))
1530 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1531 Report.Error (136, loc, "'{0}' has a different meaning later in the block", name);
1536 // This is used by non-static `struct' constructors which do not have an
1537 // initializer - in this case, the constructor must initialize all of the
1538 // struct's fields. To do this, we add a "this" variable and use the flow
1539 // analysis code to ensure that it's been fully initialized before control
1540 // leaves the constructor.
1542 public LocalInfo AddThisVariable (TypeContainer tc, Location l)
1544 if (this_variable != null)
1545 return this_variable;
1547 if (variables == null)
1548 variables = new Hashtable ();
1550 this_variable = new LocalInfo (tc, this, l);
1551 this_variable.Used = true;
1552 this_variable.IsThis = true;
1554 variables.Add ("this", this_variable);
1556 return this_variable;
1559 public LocalInfo AddVariable (Expression type, string name, Location l)
1561 if (variables == null)
1562 variables = new Hashtable ();
1564 LocalInfo vi = GetLocalInfo (name);
1566 Report.SymbolRelatedToPreviousError (vi.Location, name);
1567 if (known_variables == vi.Block.known_variables)
1568 Report.Error (128, l,
1569 "A local variable '{0}' is already declared in this scope", name);
1571 Report.Error (136, l,
1572 "'{0}' hides the declaration of local variable '{0}' in a parent scope", name);
1576 vi = GetKnownVariableInfo (name);
1578 Report.SymbolRelatedToPreviousError (vi.Location, name);
1579 Report.Error (136, l,
1580 "A child block already has a declaration of local variable '{0}':" +
1581 " allowing this declaration would violate 'invariant meaning in a block'",
1587 Parameter p = Toplevel.Parameters.GetParameterByName (name, out idx);
1589 Report.SymbolRelatedToPreviousError (p.Location, name);
1590 Report.Error (136, l, "'{0}' hides a method parameter", name);
1594 vi = new LocalInfo (type, name, this, l);
1596 variables.Add (name, vi);
1598 for (Block b = this; b != null; b = b.Parent)
1599 b.AddKnownVariable (name, vi);
1601 if ((flags & Flags.VariablesInitialized) != 0)
1602 throw new Exception ();
1604 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1608 public bool AddConstant (Expression type, string name, Expression value, Location l)
1610 if (AddVariable (type, name, l) == null)
1613 if (constants == null)
1614 constants = new Hashtable ();
1616 constants.Add (name, value);
1620 static int next_temp_id = 0;
1622 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1624 if (temporary_variables == null)
1625 temporary_variables = new ArrayList ();
1627 int id = ++next_temp_id;
1628 string name = "$s_" + id.ToString ();
1630 LocalInfo li = new LocalInfo (te, name, this, loc);
1631 li.CompilerGenerated = true;
1632 temporary_variables.Add (li);
1636 public Hashtable Variables {
1642 public LocalInfo GetLocalInfo (string name)
1644 for (Block b = this; b != null; b = b.Parent) {
1645 if (b.variables != null) {
1646 LocalInfo ret = b.variables [name] as LocalInfo;
1654 public Expression GetVariableType (string name)
1656 LocalInfo vi = GetLocalInfo (name);
1664 public Expression GetConstantExpression (string name)
1666 for (Block b = this; b != null; b = b.Parent) {
1667 if (b.constants != null) {
1668 Expression ret = b.constants [name] as Expression;
1677 /// True if the variable named @name is a constant
1679 public bool IsConstant (string name)
1681 Expression e = null;
1683 e = GetConstantExpression (name);
1689 /// A list of labels that were not used within this block
1691 public string [] GetUnreferenced ()
1693 // FIXME: Implement me
1697 public void AddStatement (Statement s)
1700 flags |= Flags.BlockUsed;
1705 return (flags & Flags.BlockUsed) != 0;
1711 flags |= Flags.BlockUsed;
1714 public bool HasRet {
1716 return (flags & Flags.HasRet) != 0;
1720 public bool IsDestructor {
1722 return (flags & Flags.IsDestructor) != 0;
1726 public void SetDestructor ()
1728 flags |= Flags.IsDestructor;
1731 VariableMap param_map, local_map;
1733 public VariableMap ParameterMap {
1735 if ((flags & Flags.VariablesInitialized) == 0)
1736 throw new Exception ("Variables have not been initialized yet");
1742 public VariableMap LocalMap {
1744 if ((flags & Flags.VariablesInitialized) == 0)
1745 throw new Exception ("Variables have not been initialized yet");
1752 /// Emits the variable declarations and labels.
1755 /// tc: is our typecontainer (to resolve type references)
1756 /// ig: is the code generator:
1758 public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
1760 bool old_unsafe = ec.InUnsafe;
1762 // If some parent block was unsafe, we remain unsafe even if this block
1763 // isn't explicitly marked as such.
1764 ec.InUnsafe |= Unsafe;
1767 // Compute the VariableMap's.
1769 // Unfortunately, we don't know the type when adding variables with
1770 // AddVariable(), so we need to compute this info here.
1774 if (variables != null) {
1775 foreach (LocalInfo li in variables.Values)
1778 locals = new LocalInfo [variables.Count];
1779 variables.Values.CopyTo (locals, 0);
1781 locals = new LocalInfo [0];
1784 local_map = new VariableMap (Parent.LocalMap, locals);
1786 local_map = new VariableMap (locals);
1788 param_map = new VariableMap (ip);
1789 flags |= Flags.VariablesInitialized;
1791 bool old_check_state = ec.ConstantCheckState;
1792 ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
1795 // Process this block variables
1797 if (variables != null){
1798 foreach (DictionaryEntry de in variables){
1799 string name = (string) de.Key;
1800 LocalInfo vi = (LocalInfo) de.Value;
1802 if (vi.VariableType == null)
1805 Type variable_type = vi.VariableType;
1807 if (variable_type.IsPointer){
1809 // Am not really convinced that this test is required (Microsoft does it)
1810 // but the fact is that you would not be able to use the pointer variable
1813 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1818 if (constants == null)
1821 Expression cv = (Expression) constants [name];
1825 ec.CurrentBlock = this;
1826 Expression e = cv.Resolve (ec);
1828 Constant ce = e as Constant;
1830 Report.Error (133, vi.Location,
1831 "The expression being assigned to '{0}' must be constant", name);
1835 if (e.Type != variable_type){
1836 e = Const.ChangeType (vi.Location, ce, variable_type);
1841 constants.Remove (name);
1842 constants.Add (name, e);
1845 ec.ConstantCheckState = old_check_state;
1848 // Now, handle the children
1850 if (children != null){
1851 foreach (Block b in children)
1852 b.ResolveMeta (toplevel, ec, ip);
1854 ec.InUnsafe = old_unsafe;
1858 // Emits the local variable declarations for a block
1860 public void EmitMeta (EmitContext ec)
1862 ILGenerator ig = ec.ig;
1864 if (variables != null){
1865 bool have_captured_vars = ec.HaveCapturedVariables ();
1867 foreach (DictionaryEntry de in variables){
1868 LocalInfo vi = (LocalInfo) de.Value;
1870 if (have_captured_vars && ec.IsCaptured (vi))
1875 // This is needed to compile on both .NET 1.x and .NET 2.x
1876 // the later introduced `DeclareLocal (Type t, bool pinned)'
1878 vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
1879 else if (!vi.IsThis)
1880 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1884 if (temporary_variables != null) {
1885 AnonymousContainer am = ec.CurrentAnonymousMethod;
1886 TypeBuilder scope = null;
1887 if ((am != null) && am.IsIterator) {
1888 scope = am.Scope.ScopeTypeBuilder;
1890 throw new InternalErrorException ();
1892 foreach (LocalInfo vi in temporary_variables) {
1893 if (scope != null) {
1894 if (vi.FieldBuilder == null)
1895 vi.FieldBuilder = scope.DefineField (
1896 vi.Name, vi.VariableType, FieldAttributes.Assembly);
1898 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1902 if (children != null){
1903 foreach (Block b in children)
1908 void UsageWarning (FlowBranching.UsageVector vector)
1912 if ((variables != null) && (RootContext.WarningLevel >= 3)) {
1913 foreach (DictionaryEntry de in variables){
1914 LocalInfo vi = (LocalInfo) de.Value;
1919 name = (string) de.Key;
1921 if (vector.IsAssigned (vi.VariableInfo)){
1922 Report.Warning (219, vi.Location, "The variable '{0}' is assigned but its value is never used", name);
1924 Report.Warning (168, vi.Location, "The variable '{0}' is declared but never used", name);
1930 bool unreachable_shown;
1933 public override bool Resolve (EmitContext ec)
1935 Block prev_block = ec.CurrentBlock;
1938 int errors = Report.Errors;
1940 ec.CurrentBlock = this;
1941 ec.StartFlowBranching (this);
1943 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1946 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1947 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1948 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1949 // responsible for handling the situation.
1951 int statement_count = statements.Count;
1952 for (int ix = 0; ix < statement_count; ix++){
1953 Statement s = (Statement) statements [ix];
1957 ((Block) s).unreachable = true;
1959 if (!unreachable_shown && (RootContext.WarningLevel >= 2)) {
1961 162, loc, "Unreachable code detected");
1962 unreachable_shown = true;
1966 if (!s.Resolve (ec)) {
1968 statements [ix] = EmptyStatement.Value;
1972 if (unreachable && !(s is LabeledStatement) && !(s is Block))
1973 statements [ix] = EmptyStatement.Value;
1975 num_statements = ix + 1;
1976 if (s is LabeledStatement)
1977 unreachable = false;
1979 unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
1982 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
1983 ec.CurrentBranching, statement_count, num_statements);
1985 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
1987 ec.CurrentBlock = prev_block;
1989 // If we're a non-static `struct' constructor which doesn't have an
1990 // initializer, then we must initialize all of the struct's fields.
1991 if ((this_variable != null) &&
1992 (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) &&
1993 !this_variable.IsThisAssigned (ec, loc))
1996 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
1997 foreach (LabeledStatement label in labels.Values)
1998 if (!label.HasBeenReferenced)
1999 Report.Warning (164, label.Location,
2000 "This label has not been referenced");
2003 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2005 if ((vector.Reachability.Returns == FlowBranching.FlowReturns.Always) ||
2006 (vector.Reachability.Throws == FlowBranching.FlowReturns.Always) ||
2007 (vector.Reachability.Reachable == FlowBranching.FlowReturns.Never))
2008 flags |= Flags.HasRet;
2010 if (ok && (errors == Report.Errors)) {
2011 if (RootContext.WarningLevel >= 3)
2012 UsageWarning (vector);
2018 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2020 unreachable_shown = true;
2023 if (warn && (RootContext.WarningLevel >= 2))
2024 Report.Warning (162, loc, "Unreachable code detected");
2026 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2027 bool ok = Resolve (ec);
2028 ec.KillFlowBranching ();
2033 protected override void DoEmit (EmitContext ec)
2035 for (int ix = 0; ix < num_statements; ix++){
2036 Statement s = (Statement) statements [ix];
2038 // Check whether we are the last statement in a
2041 if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block))
2042 ec.IsLastStatement = true;
2044 ec.IsLastStatement = false;
2050 public override void Emit (EmitContext ec)
2052 Block prev_block = ec.CurrentBlock;
2054 ec.CurrentBlock = this;
2056 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2057 bool is_lexical_block = !Implicit && (Parent != null);
2059 if (emit_debug_info) {
2060 if (is_lexical_block)
2063 if (variables != null) {
2064 foreach (DictionaryEntry de in variables) {
2065 string name = (string) de.Key;
2066 LocalInfo vi = (LocalInfo) de.Value;
2068 if (vi.LocalBuilder == null)
2071 ec.DefineLocalVariable (name, vi.LocalBuilder);
2076 ec.Mark (StartLocation, true);
2078 ec.Mark (EndLocation, true);
2080 if (emit_debug_info && is_lexical_block)
2083 ec.CurrentBlock = prev_block;
2087 // Returns true if we ar ea child of `b'.
2089 public bool IsChildOf (Block b)
2091 Block current = this;
2094 if (current.Parent == b)
2096 current = current.Parent;
2097 } while (current != null);
2101 public override string ToString ()
2103 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2108 // A toplevel block contains extra information, the split is done
2109 // only to separate information that would otherwise bloat the more
2110 // lightweight Block.
2112 // In particular, this was introduced when the support for Anonymous
2113 // Methods was implemented.
2115 public class ToplevelBlock : Block {
2117 // Pointer to the host of this anonymous method, or null
2118 // if we are the topmost block
2120 ToplevelBlock container;
2121 CaptureContext capture_context;
2122 FlowBranching top_level_branching;
2124 Hashtable capture_contexts;
2128 // The parameters for the block.
2130 public readonly Parameters Parameters;
2132 public void RegisterCaptureContext (CaptureContext cc)
2134 if (capture_contexts == null)
2135 capture_contexts = new Hashtable ();
2136 capture_contexts [cc] = cc;
2139 public void CompleteContexts ()
2141 if (capture_contexts == null)
2144 foreach (CaptureContext cc in capture_contexts.Keys){
2149 public CaptureContext ToplevelBlockCaptureContext {
2151 return capture_context;
2155 public ToplevelBlock Container {
2161 protected void AddChild (ToplevelBlock block)
2163 if (children == null)
2164 children = new ArrayList ();
2166 children.Add (block);
2170 // Parent is only used by anonymous blocks to link back to their
2173 public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
2174 this (container, (Flags) 0, parameters, start)
2178 public ToplevelBlock (Parameters parameters, Location start) :
2179 this (null, (Flags) 0, parameters, start)
2183 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2184 this (null, flags, parameters, start)
2188 public ToplevelBlock (ToplevelBlock container, Flags flags, Parameters parameters, Location start) :
2189 base (null, flags | Flags.IsToplevel, start, Location.Null)
2191 Parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2192 this.container = container;
2194 if (container != null)
2195 container.AddChild (this);
2198 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2202 public void SetHaveAnonymousMethods (Location loc, AnonymousContainer host)
2204 if (capture_context == null)
2205 capture_context = new CaptureContext (this, loc, host);
2208 public CaptureContext CaptureContext {
2210 return capture_context;
2214 public FlowBranching TopLevelBranching {
2216 return top_level_branching;
2221 // This is used if anonymous methods are used inside an iterator
2222 // (see 2test-22.cs for an example).
2224 // The AnonymousMethod is created while parsing - at a time when we don't
2225 // know yet that we're inside an iterator, so it's `Container' is initially
2226 // null. Later on, when resolving the iterator, we need to move the
2227 // anonymous method into that iterator.
2229 public void ReParent (ToplevelBlock new_parent, AnonymousContainer new_host)
2231 foreach (ToplevelBlock block in children) {
2232 if (block.CaptureContext == null)
2235 block.container = new_parent;
2236 block.CaptureContext.ReParent (new_parent, new_host);
2241 // Returns a `ParameterReference' for the given name, or null if there
2242 // is no such parameter
2244 public ParameterReference GetParameterReference (string name, Location loc)
2249 for (ToplevelBlock t = this; t != null; t = t.Container) {
2250 Parameters pars = t.Parameters;
2251 par = pars.GetParameterByName (name, out idx);
2253 return new ParameterReference (pars, this, idx, name, loc);
2259 // Whether the parameter named `name' is local to this block,
2260 // or false, if the parameter belongs to an encompassing block.
2262 public bool IsLocalParameter (string name)
2264 return Parameters.GetParameterByName (name) != null;
2268 // Whether the `name' is a parameter reference
2270 public bool IsParameterReference (string name)
2272 for (ToplevelBlock t = this; t != null; t = t.Container) {
2273 if (t.IsLocalParameter (name))
2279 public bool ResolveMeta (EmitContext ec, InternalParameters ip)
2281 int errors = Report.Errors;
2283 if (top_level_branching != null)
2286 ResolveMeta (this, ec, ip);
2288 top_level_branching = ec.StartFlowBranching (this);
2290 return Report.Errors == errors;
2294 public class SwitchLabel {
2297 public Location loc;
2301 Label il_label_code;
2302 bool il_label_code_set;
2305 // if expr == null, then it is the default case.
2307 public SwitchLabel (Expression expr, Location l)
2313 public Expression Label {
2319 public object Converted {
2325 public Label GetILLabel (EmitContext ec)
2328 il_label = ec.ig.DefineLabel ();
2329 il_label_set = true;
2334 public Label GetILLabelCode (EmitContext ec)
2336 if (!il_label_code_set){
2337 il_label_code = ec.ig.DefineLabel ();
2338 il_label_code_set = true;
2340 return il_label_code;
2344 // Resolves the expression, reduces it to a literal if possible
2345 // and then converts it to the requested type.
2347 public bool ResolveAndReduce (EmitContext ec, Type required_type)
2352 Expression e = label.Resolve (ec);
2357 if (!(e is Constant)){
2358 Report.Error (150, loc, "A constant value is expected, got: " + e);
2362 if (e is StringConstant || e is NullLiteral){
2363 if (required_type == TypeManager.string_type){
2369 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
2370 if (converted == null)
2377 public class SwitchSection {
2378 // An array of SwitchLabels.
2379 public readonly ArrayList Labels;
2380 public readonly Block Block;
2382 public SwitchSection (ArrayList labels, Block block)
2389 public class Switch : Statement {
2390 public readonly ArrayList Sections;
2391 public Expression Expr;
2394 /// Maps constants whose type type SwitchType to their SwitchLabels.
2396 public Hashtable Elements;
2399 /// The governing switch type
2401 public Type SwitchType;
2406 Label default_target;
2407 Expression new_expr;
2409 SwitchSection constant_section;
2410 SwitchSection default_section;
2413 // The types allowed to be implicitly cast from
2414 // on the governing type
2416 static Type [] allowed_types;
2418 public Switch (Expression e, ArrayList sects, Location l)
2425 public bool GotDefault {
2427 return default_section != null;
2431 public Label DefaultTarget {
2433 return default_target;
2438 // Determines the governing type for a switch. The returned
2439 // expression might be the expression from the switch, or an
2440 // expression that includes any potential conversions to the
2441 // integral types or to string.
2443 Expression SwitchGoverningType (EmitContext ec, Type t)
2445 if (t == TypeManager.int32_type ||
2446 t == TypeManager.uint32_type ||
2447 t == TypeManager.char_type ||
2448 t == TypeManager.byte_type ||
2449 t == TypeManager.sbyte_type ||
2450 t == TypeManager.ushort_type ||
2451 t == TypeManager.short_type ||
2452 t == TypeManager.uint64_type ||
2453 t == TypeManager.int64_type ||
2454 t == TypeManager.string_type ||
2455 t == TypeManager.bool_type ||
2456 t.IsSubclassOf (TypeManager.enum_type))
2459 if (allowed_types == null){
2460 allowed_types = new Type [] {
2461 TypeManager.int32_type,
2462 TypeManager.uint32_type,
2463 TypeManager.sbyte_type,
2464 TypeManager.byte_type,
2465 TypeManager.short_type,
2466 TypeManager.ushort_type,
2467 TypeManager.int64_type,
2468 TypeManager.uint64_type,
2469 TypeManager.char_type,
2470 TypeManager.bool_type,
2471 TypeManager.string_type
2476 // Try to find a *user* defined implicit conversion.
2478 // If there is no implicit conversion, or if there are multiple
2479 // conversions, we have to report an error
2481 Expression converted = null;
2482 foreach (Type tt in allowed_types){
2485 e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
2490 // Ignore over-worked ImplicitUserConversions that do
2491 // an implicit conversion in addition to the user conversion.
2494 UserCast ue = e as UserCast;
2496 if (ue.Source != Expr)
2500 if (converted != null){
2501 Report.ExtraInformation (
2503 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
2504 TypeManager.CSharpName (Expr.Type)));
2513 static string Error152 {
2515 return "The label '{0}:' already occurs in this switch statement";
2520 // Performs the basic sanity checks on the switch statement
2521 // (looks for duplicate keys and non-constant expressions).
2523 // It also returns a hashtable with the keys that we will later
2524 // use to compute the switch tables
2526 bool CheckSwitch (EmitContext ec)
2530 Elements = new Hashtable ();
2532 if (TypeManager.IsEnumType (SwitchType)){
2533 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2535 compare_type = SwitchType;
2537 foreach (SwitchSection ss in Sections){
2538 foreach (SwitchLabel sl in ss.Labels){
2539 if (!sl.ResolveAndReduce (ec, SwitchType)){
2544 if (sl.Label == null){
2545 if (default_section != null){
2546 Report.Error (152, sl.loc, Error152, "default");
2549 default_section = ss;
2553 object key = sl.Converted;
2555 if (key is Constant)
2556 key = ((Constant) key).GetValue ();
2559 key = NullLiteral.Null;
2561 string lname = null;
2562 if (compare_type == TypeManager.uint64_type){
2563 ulong v = (ulong) key;
2565 if (Elements.Contains (v))
2566 lname = v.ToString ();
2568 Elements.Add (v, sl);
2569 } else if (compare_type == TypeManager.int64_type){
2570 long v = (long) key;
2572 if (Elements.Contains (v))
2573 lname = v.ToString ();
2575 Elements.Add (v, sl);
2576 } else if (compare_type == TypeManager.uint32_type){
2577 uint v = (uint) key;
2579 if (Elements.Contains (v))
2580 lname = v.ToString ();
2582 Elements.Add (v, sl);
2583 } else if (compare_type == TypeManager.char_type){
2584 char v = (char) key;
2586 if (Elements.Contains (v))
2587 lname = v.ToString ();
2589 Elements.Add (v, sl);
2590 } else if (compare_type == TypeManager.byte_type){
2591 byte v = (byte) key;
2593 if (Elements.Contains (v))
2594 lname = v.ToString ();
2596 Elements.Add (v, sl);
2597 } else if (compare_type == TypeManager.sbyte_type){
2598 sbyte v = (sbyte) key;
2600 if (Elements.Contains (v))
2601 lname = v.ToString ();
2603 Elements.Add (v, sl);
2604 } else if (compare_type == TypeManager.short_type){
2605 short v = (short) key;
2607 if (Elements.Contains (v))
2608 lname = v.ToString ();
2610 Elements.Add (v, sl);
2611 } else if (compare_type == TypeManager.ushort_type){
2612 ushort v = (ushort) key;
2614 if (Elements.Contains (v))
2615 lname = v.ToString ();
2617 Elements.Add (v, sl);
2618 } else if (compare_type == TypeManager.string_type){
2619 if (key is NullLiteral){
2620 if (Elements.Contains (NullLiteral.Null))
2623 Elements.Add (NullLiteral.Null, null);
2625 string s = (string) key;
2627 if (Elements.Contains (s))
2630 Elements.Add (s, sl);
2632 } else if (compare_type == TypeManager.int32_type) {
2635 if (Elements.Contains (v))
2636 lname = v.ToString ();
2638 Elements.Add (v, sl);
2639 } else if (compare_type == TypeManager.bool_type) {
2640 bool v = (bool) key;
2642 if (Elements.Contains (v))
2643 lname = v.ToString ();
2645 Elements.Add (v, sl);
2649 throw new Exception ("Unknown switch type!" +
2650 SwitchType + " " + compare_type);
2654 Report.Error (152, sl.loc, Error152, "case " + lname);
2665 void EmitObjectInteger (ILGenerator ig, object k)
2668 IntConstant.EmitInt (ig, (int) k);
2669 else if (k is Constant) {
2670 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2673 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2676 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2678 IntConstant.EmitInt (ig, (int) (long) k);
2679 ig.Emit (OpCodes.Conv_I8);
2682 LongConstant.EmitLong (ig, (long) k);
2684 else if (k is ulong)
2686 if ((ulong) k < (1L<<32))
2688 IntConstant.EmitInt (ig, (int) (long) k);
2689 ig.Emit (OpCodes.Conv_U8);
2693 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2697 IntConstant.EmitInt (ig, (int) ((char) k));
2698 else if (k is sbyte)
2699 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2701 IntConstant.EmitInt (ig, (int) ((byte) k));
2702 else if (k is short)
2703 IntConstant.EmitInt (ig, (int) ((short) k));
2704 else if (k is ushort)
2705 IntConstant.EmitInt (ig, (int) ((ushort) k));
2707 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2709 throw new Exception ("Unhandled case");
2712 // structure used to hold blocks of keys while calculating table switch
2713 class KeyBlock : IComparable
2715 public KeyBlock (long _nFirst)
2717 nFirst = nLast = _nFirst;
2721 public ArrayList rgKeys = null;
2722 // how many items are in the bucket
2723 public int Size = 1;
2726 get { return (int) (nLast - nFirst + 1); }
2728 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2730 return kbLast.nLast - kbFirst.nFirst + 1;
2732 public int CompareTo (object obj)
2734 KeyBlock kb = (KeyBlock) obj;
2735 int nLength = Length;
2736 int nLengthOther = kb.Length;
2737 if (nLengthOther == nLength)
2738 return (int) (kb.nFirst - nFirst);
2739 return nLength - nLengthOther;
2744 /// This method emits code for a lookup-based switch statement (non-string)
2745 /// Basically it groups the cases into blocks that are at least half full,
2746 /// and then spits out individual lookup opcodes for each block.
2747 /// It emits the longest blocks first, and short blocks are just
2748 /// handled with direct compares.
2750 /// <param name="ec"></param>
2751 /// <param name="val"></param>
2752 /// <returns></returns>
2753 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
2755 int cElements = Elements.Count;
2756 object [] rgKeys = new object [cElements];
2757 Elements.Keys.CopyTo (rgKeys, 0);
2758 Array.Sort (rgKeys);
2760 // initialize the block list with one element per key
2761 ArrayList rgKeyBlocks = new ArrayList ();
2762 foreach (object key in rgKeys)
2763 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
2766 // iteratively merge the blocks while they are at least half full
2767 // there's probably a really cool way to do this with a tree...
2768 while (rgKeyBlocks.Count > 1)
2770 ArrayList rgKeyBlocksNew = new ArrayList ();
2771 kbCurr = (KeyBlock) rgKeyBlocks [0];
2772 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2774 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2775 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
2778 kbCurr.nLast = kb.nLast;
2779 kbCurr.Size += kb.Size;
2783 // start a new block
2784 rgKeyBlocksNew.Add (kbCurr);
2788 rgKeyBlocksNew.Add (kbCurr);
2789 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2791 rgKeyBlocks = rgKeyBlocksNew;
2794 // initialize the key lists
2795 foreach (KeyBlock kb in rgKeyBlocks)
2796 kb.rgKeys = new ArrayList ();
2798 // fill the key lists
2800 if (rgKeyBlocks.Count > 0) {
2801 kbCurr = (KeyBlock) rgKeyBlocks [0];
2802 foreach (object key in rgKeys)
2804 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
2805 System.Convert.ToInt64 (key) > kbCurr.nLast;
2807 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2808 kbCurr.rgKeys.Add (key);
2812 // sort the blocks so we can tackle the largest ones first
2813 rgKeyBlocks.Sort ();
2815 // okay now we can start...
2816 ILGenerator ig = ec.ig;
2817 Label lblEnd = ig.DefineLabel (); // at the end ;-)
2818 Label lblDefault = ig.DefineLabel ();
2820 Type typeKeys = null;
2821 if (rgKeys.Length > 0)
2822 typeKeys = rgKeys [0].GetType (); // used for conversions
2826 if (TypeManager.IsEnumType (SwitchType))
2827 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2829 compare_type = SwitchType;
2831 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2833 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2834 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2837 foreach (object key in kb.rgKeys)
2839 ig.Emit (OpCodes.Ldloc, val);
2840 EmitObjectInteger (ig, key);
2841 SwitchLabel sl = (SwitchLabel) Elements [key];
2842 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
2847 // TODO: if all the keys in the block are the same and there are
2848 // no gaps/defaults then just use a range-check.
2849 if (compare_type == TypeManager.int64_type ||
2850 compare_type == TypeManager.uint64_type)
2852 // TODO: optimize constant/I4 cases
2854 // check block range (could be > 2^31)
2855 ig.Emit (OpCodes.Ldloc, val);
2856 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2857 ig.Emit (OpCodes.Blt, lblDefault);
2858 ig.Emit (OpCodes.Ldloc, val);
2859 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
2860 ig.Emit (OpCodes.Bgt, lblDefault);
2863 ig.Emit (OpCodes.Ldloc, val);
2866 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2867 ig.Emit (OpCodes.Sub);
2869 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
2874 ig.Emit (OpCodes.Ldloc, val);
2875 int nFirst = (int) kb.nFirst;
2878 IntConstant.EmitInt (ig, nFirst);
2879 ig.Emit (OpCodes.Sub);
2881 else if (nFirst < 0)
2883 IntConstant.EmitInt (ig, -nFirst);
2884 ig.Emit (OpCodes.Add);
2888 // first, build the list of labels for the switch
2890 int cJumps = kb.Length;
2891 Label [] rgLabels = new Label [cJumps];
2892 for (int iJump = 0; iJump < cJumps; iJump++)
2894 object key = kb.rgKeys [iKey];
2895 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
2897 SwitchLabel sl = (SwitchLabel) Elements [key];
2898 rgLabels [iJump] = sl.GetILLabel (ec);
2902 rgLabels [iJump] = lblDefault;
2904 // emit the switch opcode
2905 ig.Emit (OpCodes.Switch, rgLabels);
2908 // mark the default for this block
2910 ig.MarkLabel (lblDefault);
2913 // TODO: find the default case and emit it here,
2914 // to prevent having to do the following jump.
2915 // make sure to mark other labels in the default section
2917 // the last default just goes to the end
2918 ig.Emit (OpCodes.Br, lblDefault);
2920 // now emit the code for the sections
2921 bool fFoundDefault = false;
2922 foreach (SwitchSection ss in Sections)
2924 foreach (SwitchLabel sl in ss.Labels)
2926 ig.MarkLabel (sl.GetILLabel (ec));
2927 ig.MarkLabel (sl.GetILLabelCode (ec));
2928 if (sl.Label == null)
2930 ig.MarkLabel (lblDefault);
2931 fFoundDefault = true;
2935 //ig.Emit (OpCodes.Br, lblEnd);
2938 if (!fFoundDefault) {
2939 ig.MarkLabel (lblDefault);
2941 ig.MarkLabel (lblEnd);
2944 // This simple emit switch works, but does not take advantage of the
2946 // TODO: remove non-string logic from here
2947 // TODO: binary search strings?
2949 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
2951 ILGenerator ig = ec.ig;
2952 Label end_of_switch = ig.DefineLabel ();
2953 Label next_test = ig.DefineLabel ();
2954 Label null_target = ig.DefineLabel ();
2955 bool first_test = true;
2956 bool pending_goto_end = false;
2957 bool null_marked = false;
2960 ig.Emit (OpCodes.Ldloc, val);
2962 if (Elements.Contains (NullLiteral.Null)){
2963 ig.Emit (OpCodes.Brfalse, null_target);
2965 ig.Emit (OpCodes.Brfalse, default_target);
2967 ig.Emit (OpCodes.Ldloc, val);
2968 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
2969 ig.Emit (OpCodes.Stloc, val);
2971 int section_count = Sections.Count;
2972 for (int section = 0; section < section_count; section++){
2973 SwitchSection ss = (SwitchSection) Sections [section];
2975 if (ss == default_section)
2978 Label sec_begin = ig.DefineLabel ();
2980 ig.Emit (OpCodes.Nop);
2982 if (pending_goto_end)
2983 ig.Emit (OpCodes.Br, end_of_switch);
2985 int label_count = ss.Labels.Count;
2987 for (int label = 0; label < label_count; label++){
2988 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
2989 ig.MarkLabel (sl.GetILLabel (ec));
2992 ig.MarkLabel (next_test);
2993 next_test = ig.DefineLabel ();
2996 // If we are the default target
2998 if (sl.Label != null){
2999 object lit = sl.Converted;
3001 if (lit is NullLiteral){
3003 if (label_count == 1)
3004 ig.Emit (OpCodes.Br, next_test);
3008 StringConstant str = (StringConstant) lit;
3010 ig.Emit (OpCodes.Ldloc, val);
3011 ig.Emit (OpCodes.Ldstr, str.Value);
3012 if (label_count == 1)
3013 ig.Emit (OpCodes.Bne_Un, next_test);
3015 if (label+1 == label_count)
3016 ig.Emit (OpCodes.Bne_Un, next_test);
3018 ig.Emit (OpCodes.Beq, sec_begin);
3023 ig.MarkLabel (null_target);
3026 ig.MarkLabel (sec_begin);
3027 foreach (SwitchLabel sl in ss.Labels)
3028 ig.MarkLabel (sl.GetILLabelCode (ec));
3031 pending_goto_end = !ss.Block.HasRet;
3034 ig.MarkLabel (next_test);
3035 ig.MarkLabel (default_target);
3037 ig.MarkLabel (null_target);
3038 if (default_section != null)
3039 default_section.Block.Emit (ec);
3040 ig.MarkLabel (end_of_switch);
3043 SwitchSection FindSection (SwitchLabel label)
3045 foreach (SwitchSection ss in Sections){
3046 foreach (SwitchLabel sl in ss.Labels){
3055 public override bool Resolve (EmitContext ec)
3057 Expr = Expr.Resolve (ec);
3061 new_expr = SwitchGoverningType (ec, Expr.Type);
3062 if (new_expr == null){
3063 Report.Error (151, loc, "An integer type or string was expected for switch");
3068 SwitchType = new_expr.Type;
3070 if (!CheckSwitch (ec))
3073 Switch old_switch = ec.Switch;
3075 ec.Switch.SwitchType = SwitchType;
3077 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3078 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3080 is_constant = new_expr is Constant;
3082 object key = ((Constant) new_expr).GetValue ();
3083 SwitchLabel label = (SwitchLabel) Elements [key];
3085 constant_section = FindSection (label);
3086 if (constant_section == null)
3087 constant_section = default_section;
3091 foreach (SwitchSection ss in Sections){
3093 ec.CurrentBranching.CreateSibling (
3094 null, FlowBranching.SiblingType.SwitchSection);
3098 if (is_constant && (ss != constant_section)) {
3099 // If we're a constant switch, we're only emitting
3100 // one single section - mark all the others as
3102 ec.CurrentBranching.CurrentUsageVector.Goto ();
3103 if (!ss.Block.ResolveUnreachable (ec, true))
3106 if (!ss.Block.Resolve (ec))
3111 if (default_section == null)
3112 ec.CurrentBranching.CreateSibling (
3113 null, FlowBranching.SiblingType.SwitchSection);
3115 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3116 ec.Switch = old_switch;
3118 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
3124 protected override void DoEmit (EmitContext ec)
3126 ILGenerator ig = ec.ig;
3128 // Store variable for comparission purposes
3131 value = ig.DeclareLocal (SwitchType);
3133 ig.Emit (OpCodes.Stloc, value);
3137 default_target = ig.DefineLabel ();
3140 // Setup the codegen context
3142 Label old_end = ec.LoopEnd;
3143 Switch old_switch = ec.Switch;
3145 ec.LoopEnd = ig.DefineLabel ();
3150 if (constant_section != null)
3151 constant_section.Block.Emit (ec);
3152 } else if (SwitchType == TypeManager.string_type)
3153 SimpleSwitchEmit (ec, value);
3155 TableSwitchEmit (ec, value);
3157 // Restore context state.
3158 ig.MarkLabel (ec.LoopEnd);
3161 // Restore the previous context
3163 ec.LoopEnd = old_end;
3164 ec.Switch = old_switch;
3168 public abstract class ExceptionStatement : Statement
3170 public abstract void EmitFinally (EmitContext ec);
3172 protected bool emit_finally = true;
3173 ArrayList parent_vectors;
3175 protected void DoEmitFinally (EmitContext ec)
3178 ec.ig.BeginFinallyBlock ();
3179 else if (ec.InIterator)
3180 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3184 protected void ResolveFinally (FlowBranchingException branching)
3186 emit_finally = branching.EmitFinally;
3188 branching.Parent.StealFinallyClauses (ref parent_vectors);
3192 public class Lock : ExceptionStatement {
3194 Statement Statement;
3197 public Lock (Expression expr, Statement stmt, Location l)
3204 public override bool Resolve (EmitContext ec)
3206 expr = expr.Resolve (ec);
3210 if (expr.Type.IsValueType){
3211 Error (185, "lock statement requires the expression to be " +
3212 " a reference type (type is: `{0}'",
3213 TypeManager.CSharpName (expr.Type));
3217 FlowBranchingException branching = ec.StartFlowBranching (this);
3218 bool ok = Statement.Resolve (ec);
3220 ec.KillFlowBranching ();
3224 ResolveFinally (branching);
3226 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3227 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3228 // Unfortunately, System.Reflection.Emit automatically emits
3229 // a leave to the end of the finally block.
3230 // This is a problem if `returns' is true since we may jump
3231 // to a point after the end of the method.
3232 // As a workaround, emit an explicit ret here.
3233 ec.NeedReturnLabel ();
3239 protected override void DoEmit (EmitContext ec)
3241 Type type = expr.Type;
3243 ILGenerator ig = ec.ig;
3244 temp = ig.DeclareLocal (type);
3247 ig.Emit (OpCodes.Dup);
3248 ig.Emit (OpCodes.Stloc, temp);
3249 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3253 ig.BeginExceptionBlock ();
3254 Statement.Emit (ec);
3259 ig.EndExceptionBlock ();
3262 public override void EmitFinally (EmitContext ec)
3264 ILGenerator ig = ec.ig;
3265 ig.Emit (OpCodes.Ldloc, temp);
3266 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3270 public class Unchecked : Statement {
3271 public readonly Block Block;
3273 public Unchecked (Block b)
3279 public override bool Resolve (EmitContext ec)
3281 bool previous_state = ec.CheckState;
3282 bool previous_state_const = ec.ConstantCheckState;
3284 ec.CheckState = false;
3285 ec.ConstantCheckState = false;
3286 bool ret = Block.Resolve (ec);
3287 ec.CheckState = previous_state;
3288 ec.ConstantCheckState = previous_state_const;
3293 protected override void DoEmit (EmitContext ec)
3295 bool previous_state = ec.CheckState;
3296 bool previous_state_const = ec.ConstantCheckState;
3298 ec.CheckState = false;
3299 ec.ConstantCheckState = false;
3301 ec.CheckState = previous_state;
3302 ec.ConstantCheckState = previous_state_const;
3306 public class Checked : Statement {
3307 public readonly Block Block;
3309 public Checked (Block b)
3312 b.Unchecked = false;
3315 public override bool Resolve (EmitContext ec)
3317 bool previous_state = ec.CheckState;
3318 bool previous_state_const = ec.ConstantCheckState;
3320 ec.CheckState = true;
3321 ec.ConstantCheckState = true;
3322 bool ret = Block.Resolve (ec);
3323 ec.CheckState = previous_state;
3324 ec.ConstantCheckState = previous_state_const;
3329 protected override void DoEmit (EmitContext ec)
3331 bool previous_state = ec.CheckState;
3332 bool previous_state_const = ec.ConstantCheckState;
3334 ec.CheckState = true;
3335 ec.ConstantCheckState = true;
3337 ec.CheckState = previous_state;
3338 ec.ConstantCheckState = previous_state_const;
3342 public class Unsafe : Statement {
3343 public readonly Block Block;
3345 public Unsafe (Block b)
3348 Block.Unsafe = true;
3351 public override bool Resolve (EmitContext ec)
3353 bool previous_state = ec.InUnsafe;
3357 val = Block.Resolve (ec);
3358 ec.InUnsafe = previous_state;
3363 protected override void DoEmit (EmitContext ec)
3365 bool previous_state = ec.InUnsafe;
3369 ec.InUnsafe = previous_state;
3376 public class Fixed : Statement {
3378 ArrayList declarators;
3379 Statement statement;
3384 abstract class Emitter
3386 protected LocalInfo vi;
3387 protected Expression converted;
3389 protected Emitter (Expression expr, LocalInfo li)
3395 public abstract void Emit (EmitContext ec);
3396 public abstract void EmitExit (ILGenerator ig);
3399 class ExpressionEmitter: Emitter {
3400 public ExpressionEmitter (Expression converted, LocalInfo li) :
3401 base (converted, li)
3405 public override void Emit (EmitContext ec) {
3407 // Store pointer in pinned location
3409 converted.Emit (ec);
3410 ec.ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3413 public override void EmitExit (ILGenerator ig)
3415 ig.Emit (OpCodes.Ldc_I4_0);
3416 ig.Emit (OpCodes.Conv_U);
3417 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3421 class StringEmitter: Emitter {
3422 LocalBuilder pinned_string;
3425 public StringEmitter (Expression expr, LocalInfo li, Location loc):
3431 public override void Emit (EmitContext ec)
3433 ILGenerator ig = ec.ig;
3434 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3436 converted.Emit (ec);
3437 ig.Emit (OpCodes.Stloc, pinned_string);
3439 Expression sptr = new StringPtr (pinned_string, loc);
3440 converted = Convert.ImplicitConversionRequired (
3441 ec, sptr, vi.VariableType, loc);
3443 if (converted == null)
3446 converted.Emit (ec);
3447 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3450 public override void EmitExit(ILGenerator ig)
3452 ig.Emit (OpCodes.Ldnull);
3453 ig.Emit (OpCodes.Stloc, pinned_string);
3457 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3460 declarators = decls;
3465 public override bool Resolve (EmitContext ec)
3468 Expression.UnsafeError (loc);
3472 TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
3476 expr_type = texpr.Type;
3478 CheckObsolete (expr_type);
3480 data = new Emitter [declarators.Count];
3482 if (!expr_type.IsPointer){
3483 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
3488 foreach (Pair p in declarators){
3489 LocalInfo vi = (LocalInfo) p.First;
3490 Expression e = (Expression) p.Second;
3492 vi.VariableInfo.SetAssigned (ec);
3493 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
3496 // The rules for the possible declarators are pretty wise,
3497 // but the production on the grammar is more concise.
3499 // So we have to enforce these rules here.
3501 // We do not resolve before doing the case 1 test,
3502 // because the grammar is explicit in that the token &
3503 // is present, so we need to test for this particular case.
3507 Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement");
3512 // Case 1: & object.
3514 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3515 Expression child = ((Unary) e).Expr;
3517 if (child is ParameterReference || child is LocalVariableReference){
3520 "No need to use fixed statement for parameters or " +
3521 "local variable declarations (address is already " +
3526 ec.InFixedInitializer = true;
3528 ec.InFixedInitializer = false;
3532 child = ((Unary) e).Expr;
3534 if (!TypeManager.VerifyUnManaged (child.Type, loc))
3537 data [i] = new ExpressionEmitter (e, vi);
3543 ec.InFixedInitializer = true;
3545 ec.InFixedInitializer = false;
3552 if (e.Type.IsArray){
3553 Type array_type = TypeManager.GetElementType (e.Type);
3556 // Provided that array_type is unmanaged,
3558 if (!TypeManager.VerifyUnManaged (array_type, loc))
3562 // and T* is implicitly convertible to the
3563 // pointer type given in the fixed statement.
3565 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
3567 Expression converted = Convert.ImplicitConversionRequired (
3568 ec, array_ptr, vi.VariableType, loc);
3569 if (converted == null)
3572 data [i] = new ExpressionEmitter (converted, vi);
3581 if (e.Type == TypeManager.string_type){
3582 data [i] = new StringEmitter (e, vi, loc);
3587 // Case 4: fixed buffer
3588 FieldExpr fe = e as FieldExpr;
3590 IFixedBuffer ff = AttributeTester.GetFixedBuffer (fe.FieldInfo);
3592 Expression fixed_buffer_ptr = new FixedBufferPtr (fe, ff.ElementType, loc);
3594 Expression converted = Convert.ImplicitConversionRequired (
3595 ec, fixed_buffer_ptr, vi.VariableType, loc);
3596 if (converted == null)
3599 data [i] = new ExpressionEmitter (converted, vi);
3607 // For other cases, flag a `this is already fixed expression'
3609 if (e is LocalVariableReference || e is ParameterReference ||
3610 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
3612 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
3616 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
3620 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
3622 if (!statement.Resolve (ec)) {
3623 ec.KillFlowBranching ();
3627 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3628 has_ret = reachability.IsUnreachable;
3633 protected override void DoEmit (EmitContext ec)
3635 for (int i = 0; i < data.Length; i++) {
3639 statement.Emit (ec);
3644 ILGenerator ig = ec.ig;
3647 // Clear the pinned variable
3649 for (int i = 0; i < data.Length; i++) {
3650 data [i].EmitExit (ig);
3655 public class Catch: Statement {
3656 public readonly string Name;
3657 public readonly Block Block;
3659 Expression type_expr;
3662 public Catch (Expression type, string name, Block block, Location l)
3670 public Type CatchType {
3676 public bool IsGeneral {
3678 return type_expr == null;
3682 protected override void DoEmit(EmitContext ec)
3686 public override bool Resolve (EmitContext ec)
3688 bool was_catch = ec.InCatch;
3691 if (type_expr != null) {
3692 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec);
3696 type = te.ResolveType (ec);
3698 CheckObsolete (type);
3700 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3701 Error (155, "The type caught or thrown must be derived from System.Exception");
3707 return Block.Resolve (ec);
3710 ec.InCatch = was_catch;
3715 public class Try : ExceptionStatement {
3716 public readonly Block Fini, Block;
3717 public readonly ArrayList Specific;
3718 public readonly Catch General;
3720 bool need_exc_block;
3723 // specific, general and fini might all be null.
3725 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3727 if (specific == null && general == null){
3728 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3732 this.Specific = specific;
3733 this.General = general;
3738 public override bool Resolve (EmitContext ec)
3742 FlowBranchingException branching = ec.StartFlowBranching (this);
3744 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3746 if (!Block.Resolve (ec))
3749 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3751 Report.Debug (1, "START OF CATCH BLOCKS", vector);
3753 Type[] prevCatches = new Type [Specific.Count];
3755 foreach (Catch c in Specific){
3756 ec.CurrentBranching.CreateSibling (
3757 c.Block, FlowBranching.SiblingType.Catch);
3759 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3761 if (c.Name != null) {
3762 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
3764 throw new Exception ();
3766 vi.VariableInfo = null;
3769 if (!c.Resolve (ec))
3772 Type resolvedType = c.CatchType;
3773 for (int ii = 0; ii < last_index; ++ii) {
3774 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
3775 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName);
3780 prevCatches [last_index++] = resolvedType;
3781 need_exc_block = true;
3784 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
3786 if (General != null){
3787 ec.CurrentBranching.CreateSibling (
3788 General.Block, FlowBranching.SiblingType.Catch);
3790 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3792 if (!General.Resolve (ec))
3795 need_exc_block = true;
3798 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
3802 ec.CurrentBranching.CreateSibling (
3803 Fini, FlowBranching.SiblingType.Finally);
3805 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3806 bool was_finally = ec.InFinally;
3807 ec.InFinally = true;
3808 if (!Fini.Resolve (ec))
3810 ec.InFinally = was_finally;
3813 need_exc_block = true;
3816 if (ec.InIterator) {
3817 ResolveFinally (branching);
3818 need_exc_block |= emit_finally;
3820 emit_finally = Fini != null;
3822 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3824 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3826 Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
3828 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3829 // Unfortunately, System.Reflection.Emit automatically emits
3830 // a leave to the end of the finally block. This is a problem
3831 // if `returns' is true since we may jump to a point after the
3832 // end of the method.
3833 // As a workaround, emit an explicit ret here.
3834 ec.NeedReturnLabel ();
3840 protected override void DoEmit (EmitContext ec)
3842 ILGenerator ig = ec.ig;
3845 ig.BeginExceptionBlock ();
3848 foreach (Catch c in Specific){
3851 ig.BeginCatchBlock (c.CatchType);
3853 if (c.Name != null){
3854 vi = c.Block.GetLocalInfo (c.Name);
3856 throw new Exception ("Variable does not exist in this block");
3858 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3860 ig.Emit (OpCodes.Pop);
3865 if (General != null){
3866 ig.BeginCatchBlock (TypeManager.object_type);
3867 ig.Emit (OpCodes.Pop);
3868 General.Block.Emit (ec);
3873 ig.EndExceptionBlock ();
3876 public override void EmitFinally (EmitContext ec)
3882 public bool HasCatch
3885 return General != null || Specific.Count > 0;
3890 public class Using : ExceptionStatement {
3891 object expression_or_block;
3892 Statement Statement;
3897 Expression [] resolved_vars;
3898 Expression [] converted_vars;
3899 ExpressionStatement [] assign;
3900 LocalBuilder local_copy;
3902 public Using (object expression_or_block, Statement stmt, Location l)
3904 this.expression_or_block = expression_or_block;
3910 // Resolves for the case of using using a local variable declaration.
3912 bool ResolveLocalVariableDecls (EmitContext ec)
3916 TypeExpr texpr = expr.ResolveAsTypeTerminal (ec);
3920 expr_type = texpr.Type;
3923 // The type must be an IDisposable or an implicit conversion
3926 converted_vars = new Expression [var_list.Count];
3927 resolved_vars = new Expression [var_list.Count];
3928 assign = new ExpressionStatement [var_list.Count];
3930 bool need_conv = !TypeManager.ImplementsInterface (
3931 expr_type, TypeManager.idisposable_type);
3933 foreach (DictionaryEntry e in var_list){
3934 Expression var = (Expression) e.Key;
3936 var = var.ResolveLValue (ec, new EmptyExpression (), loc);
3940 resolved_vars [i] = var;
3947 converted_vars [i] = Convert.ImplicitConversionRequired (
3948 ec, var, TypeManager.idisposable_type, loc);
3950 if (converted_vars [i] == null)
3957 foreach (DictionaryEntry e in var_list){
3958 Expression var = resolved_vars [i];
3959 Expression new_expr = (Expression) e.Value;
3962 a = new Assign (var, new_expr, loc);
3968 converted_vars [i] = var;
3969 assign [i] = (ExpressionStatement) a;
3976 bool ResolveExpression (EmitContext ec)
3978 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3979 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
3980 Report.Error (1674, loc, "'{0}': type used in a using statement must be implicitly convertible to 'System.IDisposable'",
3981 TypeManager.CSharpName (expr_type));
3990 // Emits the code for the case of using using a local variable declaration.
3992 void EmitLocalVariableDecls (EmitContext ec)
3994 ILGenerator ig = ec.ig;
3997 for (i = 0; i < assign.Length; i++) {
3998 assign [i].EmitStatement (ec);
4001 ig.BeginExceptionBlock ();
4003 Statement.Emit (ec);
4005 var_list.Reverse ();
4010 void EmitLocalVariableDeclFinally (EmitContext ec)
4012 ILGenerator ig = ec.ig;
4014 int i = assign.Length;
4015 for (int ii = 0; ii < var_list.Count; ++ii){
4016 Expression var = resolved_vars [--i];
4017 Label skip = ig.DefineLabel ();
4019 ig.BeginFinallyBlock ();
4021 if (!var.Type.IsValueType) {
4023 ig.Emit (OpCodes.Brfalse, skip);
4024 converted_vars [i].Emit (ec);
4025 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4027 Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4029 if (!(ml is MethodGroupExpr)) {
4031 ig.Emit (OpCodes.Box, var.Type);
4032 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4034 MethodInfo mi = null;
4036 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4037 if (TypeManager.GetArgumentTypes (mk).Length == 0) {
4044 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4048 IMemoryLocation mloc = (IMemoryLocation) var;
4050 mloc.AddressOf (ec, AddressOp.Load);
4051 ig.Emit (OpCodes.Call, mi);
4055 ig.MarkLabel (skip);
4058 ig.EndExceptionBlock ();
4060 ig.BeginFinallyBlock ();
4065 void EmitExpression (EmitContext ec)
4068 // Make a copy of the expression and operate on that.
4070 ILGenerator ig = ec.ig;
4071 local_copy = ig.DeclareLocal (expr_type);
4076 ig.Emit (OpCodes.Stloc, local_copy);
4079 ig.BeginExceptionBlock ();
4081 Statement.Emit (ec);
4085 ig.EndExceptionBlock ();
4088 void EmitExpressionFinally (EmitContext ec)
4090 ILGenerator ig = ec.ig;
4091 if (!local_copy.LocalType.IsValueType) {
4092 Label skip = ig.DefineLabel ();
4093 ig.Emit (OpCodes.Ldloc, local_copy);
4094 ig.Emit (OpCodes.Brfalse, skip);
4095 ig.Emit (OpCodes.Ldloc, local_copy);
4096 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4097 ig.MarkLabel (skip);
4099 Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, local_copy.LocalType, "Dispose", Mono.CSharp.Location.Null);
4101 if (!(ml is MethodGroupExpr)) {
4102 ig.Emit (OpCodes.Ldloc, local_copy);
4103 ig.Emit (OpCodes.Box, local_copy.LocalType);
4104 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4106 MethodInfo mi = null;
4108 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4109 if (TypeManager.GetArgumentTypes (mk).Length == 0) {
4116 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4120 ig.Emit (OpCodes.Ldloca, local_copy);
4121 ig.Emit (OpCodes.Call, mi);
4126 public override bool Resolve (EmitContext ec)
4128 if (expression_or_block is DictionaryEntry){
4129 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4130 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4132 if (!ResolveLocalVariableDecls (ec))
4135 } else if (expression_or_block is Expression){
4136 expr = (Expression) expression_or_block;
4138 expr = expr.Resolve (ec);
4142 expr_type = expr.Type;
4144 if (!ResolveExpression (ec))
4148 FlowBranchingException branching = ec.StartFlowBranching (this);
4150 bool ok = Statement.Resolve (ec);
4153 ec.KillFlowBranching ();
4157 ResolveFinally (branching);
4158 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
4160 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
4161 // Unfortunately, System.Reflection.Emit automatically emits a leave
4162 // to the end of the finally block. This is a problem if `returns'
4163 // is true since we may jump to a point after the end of the method.
4164 // As a workaround, emit an explicit ret here.
4165 ec.NeedReturnLabel ();
4171 protected override void DoEmit (EmitContext ec)
4173 if (expression_or_block is DictionaryEntry)
4174 EmitLocalVariableDecls (ec);
4175 else if (expression_or_block is Expression)
4176 EmitExpression (ec);
4179 public override void EmitFinally (EmitContext ec)
4181 if (expression_or_block is DictionaryEntry)
4182 EmitLocalVariableDeclFinally (ec);
4183 else if (expression_or_block is Expression)
4184 EmitExpressionFinally (ec);
4189 /// Implementation of the foreach C# statement
4191 public class Foreach : Statement {
4193 Expression variable;
4195 Statement statement;
4197 CollectionForeach collection;
4199 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4200 Statement stmt, Location l)
4203 this.variable = var;
4209 public override bool Resolve (EmitContext ec)
4211 expr = expr.Resolve (ec);
4215 if (expr is NullLiteral) {
4216 Report.Error (186, expr.Location, "Use of null is not valid in this context");
4220 TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
4224 Type var_type = texpr.Type;
4227 // We need an instance variable. Not sure this is the best
4228 // way of doing this.
4230 // FIXME: When we implement propertyaccess, will those turn
4231 // out to return values in ExprClass? I think they should.
4233 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4234 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4235 CollectionForeach.error1579 (expr.Type, loc);
4239 if (expr.Type.IsArray) {
4240 array = new ArrayForeach (var_type, variable, expr, statement, loc);
4241 return array.Resolve (ec);
4243 collection = new CollectionForeach (
4244 var_type, variable, expr, statement, loc);
4245 return collection.Resolve (ec);
4249 protected override void DoEmit (EmitContext ec)
4251 ILGenerator ig = ec.ig;
4253 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4254 ec.LoopBegin = ig.DefineLabel ();
4255 ec.LoopEnd = ig.DefineLabel ();
4257 if (collection != null)
4258 collection.Emit (ec);
4262 ec.LoopBegin = old_begin;
4263 ec.LoopEnd = old_end;
4266 protected class TemporaryVariable : Expression, IMemoryLocation
4270 public TemporaryVariable (Type type, Location loc)
4274 eclass = ExprClass.Value;
4277 public override Expression DoResolve (EmitContext ec)
4282 TypeExpr te = new TypeExpression (type, loc);
4283 li = ec.CurrentBlock.AddTemporaryVariable (te, loc);
4284 if (!li.Resolve (ec))
4287 AnonymousContainer am = ec.CurrentAnonymousMethod;
4288 if ((am != null) && am.IsIterator)
4289 ec.CaptureVariable (li);
4294 public override void Emit (EmitContext ec)
4296 ILGenerator ig = ec.ig;
4298 if (li.FieldBuilder != null) {
4299 ig.Emit (OpCodes.Ldarg_0);
4300 ig.Emit (OpCodes.Ldfld, li.FieldBuilder);
4302 ig.Emit (OpCodes.Ldloc, li.LocalBuilder);
4306 public void EmitLoadAddress (EmitContext ec)
4308 ILGenerator ig = ec.ig;
4310 if (li.FieldBuilder != null) {
4311 ig.Emit (OpCodes.Ldarg_0);
4312 ig.Emit (OpCodes.Ldflda, li.FieldBuilder);
4314 ig.Emit (OpCodes.Ldloca, li.LocalBuilder);
4318 public void Store (EmitContext ec, Expression right_side)
4320 if (li.FieldBuilder != null)
4321 ec.ig.Emit (OpCodes.Ldarg_0);
4323 right_side.Emit (ec);
4324 if (li.FieldBuilder != null) {
4325 ec.ig.Emit (OpCodes.Stfld, li.FieldBuilder);
4327 ec.ig.Emit (OpCodes.Stloc, li.LocalBuilder);
4331 public void EmitThis (EmitContext ec)
4333 if (li.FieldBuilder != null) {
4334 ec.ig.Emit (OpCodes.Ldarg_0);
4338 public void EmitStore (ILGenerator ig)
4340 if (li.FieldBuilder != null)
4341 ig.Emit (OpCodes.Stfld, li.FieldBuilder);
4343 ig.Emit (OpCodes.Stloc, li.LocalBuilder);
4346 public void AddressOf (EmitContext ec, AddressOp mode)
4348 EmitLoadAddress (ec);
4352 protected class ArrayCounter : TemporaryVariable
4354 public ArrayCounter (Location loc)
4355 : base (TypeManager.int32_type, loc)
4358 public void Initialize (EmitContext ec)
4361 ec.ig.Emit (OpCodes.Ldc_I4_0);
4365 public void Increment (EmitContext ec)
4369 ec.ig.Emit (OpCodes.Ldc_I4_1);
4370 ec.ig.Emit (OpCodes.Add);
4375 protected class ArrayForeach : Statement
4377 Expression variable, expr, conv;
4378 Statement statement;
4381 TemporaryVariable[] lengths;
4382 ArrayCounter[] counter;
4385 TemporaryVariable copy;
4388 public ArrayForeach (Type var_type, Expression var,
4389 Expression expr, Statement stmt, Location l)
4391 this.var_type = var_type;
4392 this.variable = var;
4398 public override bool Resolve (EmitContext ec)
4400 array_type = expr.Type;
4401 rank = array_type.GetArrayRank ();
4403 copy = new TemporaryVariable (array_type, loc);
4406 counter = new ArrayCounter [rank];
4407 lengths = new TemporaryVariable [rank];
4409 ArrayList list = new ArrayList ();
4410 for (int i = 0; i < rank; i++) {
4411 counter [i] = new ArrayCounter (loc);
4412 counter [i].Resolve (ec);
4414 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4415 lengths [i].Resolve (ec);
4417 list.Add (counter [i]);
4420 access = new ElementAccess (copy, list, loc).Resolve (ec);
4424 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
4430 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4431 ec.CurrentBranching.CreateSibling ();
4433 variable = variable.ResolveLValue (ec, conv, loc);
4434 if (variable == null)
4437 if (!statement.Resolve (ec))
4440 ec.EndFlowBranching ();
4445 protected override void DoEmit (EmitContext ec)
4447 ILGenerator ig = ec.ig;
4449 copy.Store (ec, expr);
4451 Label[] test = new Label [rank];
4452 Label[] loop = new Label [rank];
4454 for (int i = 0; i < rank; i++) {
4455 test [i] = ig.DefineLabel ();
4456 loop [i] = ig.DefineLabel ();
4458 lengths [i].EmitThis (ec);
4459 ((ArrayAccess) access).EmitGetLength (ec, i);
4460 lengths [i].EmitStore (ig);
4463 for (int i = 0; i < rank; i++) {
4464 counter [i].Initialize (ec);
4466 ig.Emit (OpCodes.Br, test [i]);
4467 ig.MarkLabel (loop [i]);
4470 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
4472 statement.Emit (ec);
4474 ig.MarkLabel (ec.LoopBegin);
4476 for (int i = rank - 1; i >= 0; i--){
4477 counter [i].Increment (ec);
4479 ig.MarkLabel (test [i]);
4480 counter [i].Emit (ec);
4481 lengths [i].Emit (ec);
4482 ig.Emit (OpCodes.Blt, loop [i]);
4485 ig.MarkLabel (ec.LoopEnd);
4489 protected class CollectionForeach : ExceptionStatement
4491 Expression variable, expr;
4492 Statement statement;
4494 TemporaryVariable enumerator;
4498 MethodGroupExpr get_enumerator;
4499 PropertyExpr get_current;
4500 MethodInfo move_next;
4501 Type var_type, enumerator_type;
4504 public CollectionForeach (Type var_type, Expression var,
4505 Expression expr, Statement stmt, Location l)
4507 this.var_type = var_type;
4508 this.variable = var;
4514 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
4516 Type [] args = TypeManager.GetArgumentTypes (mi);
4518 if (args.Length != 0)
4522 if (TypeManager.IsOverride (mi))
4525 // Check whether GetEnumerator is public
4526 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
4529 if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4531 // Apply the same optimization as MS: skip the GetEnumerator
4532 // returning an IEnumerator, and use the one returning a
4533 // CharEnumerator instead. This allows us to avoid the
4534 // try-finally block and the boxing.
4539 // Ok, we can access it, now make sure that we can do something
4540 // with this `GetEnumerator'
4543 Type return_type = mi.ReturnType;
4544 if (mi.ReturnType == TypeManager.ienumerator_type ||
4545 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4546 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4548 // If it is not an interface, lets try to find the methods ourselves.
4549 // For example, if we have:
4550 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4551 // We can avoid the iface call. This is a runtime perf boost.
4552 // even bigger if we have a ValueType, because we avoid the cost
4555 // We have to make sure that both methods exist for us to take
4556 // this path. If one of the methods does not exist, we will just
4557 // use the interface. Sadly, this complex if statement is the only
4558 // way I could do this without a goto
4561 if (return_type.IsInterface ||
4562 !FetchMoveNext (ec, return_type) ||
4563 !FetchGetCurrent (ec, return_type)) {
4564 move_next = TypeManager.bool_movenext_void;
4565 get_current = new PropertyExpr (
4566 ec, TypeManager.ienumerator_getcurrent, loc);
4571 // Ok, so they dont return an IEnumerable, we will have to
4572 // find if they support the GetEnumerator pattern.
4575 if (!FetchMoveNext (ec, return_type))
4578 if (!FetchGetCurrent (ec, return_type))
4582 enumerator_type = return_type;
4583 is_disposable = !enumerator_type.IsSealed ||
4584 TypeManager.ImplementsInterface (
4585 enumerator_type, TypeManager.idisposable_type);
4591 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4593 bool FetchMoveNext (EmitContext ec, Type t)
4595 MemberList move_next_list;
4597 move_next_list = TypeContainer.FindMembers (
4598 t, MemberTypes.Method,
4599 BindingFlags.Public | BindingFlags.Instance,
4600 Type.FilterName, "MoveNext");
4601 if (move_next_list.Count == 0)
4604 foreach (MemberInfo m in move_next_list){
4605 MethodInfo mi = (MethodInfo) m;
4608 args = TypeManager.GetArgumentTypes (mi);
4609 if ((args != null) && (args.Length == 0) &&
4610 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
4620 // Retrieves a `public T get_Current ()' method from the Type `t'
4622 bool FetchGetCurrent (EmitContext ec, Type t)
4624 PropertyExpr pe = Expression.MemberLookup (
4625 ec, t, "Current", MemberTypes.Property,
4626 Expression.AllBindingFlags, loc) as PropertyExpr;
4635 // Retrieves a `public void Dispose ()' method from the Type `t'
4637 static MethodInfo FetchMethodDispose (Type t)
4639 MemberList dispose_list;
4641 dispose_list = TypeContainer.FindMembers (
4642 t, MemberTypes.Method,
4643 BindingFlags.Public | BindingFlags.Instance,
4644 Type.FilterName, "Dispose");
4645 if (dispose_list.Count == 0)
4648 foreach (MemberInfo m in dispose_list){
4649 MethodInfo mi = (MethodInfo) m;
4652 args = TypeManager.GetArgumentTypes (mi);
4653 if (args != null && args.Length == 0){
4654 if (mi.ReturnType == TypeManager.void_type)
4661 static public void error1579 (Type t, Location loc)
4663 Report.Error (1579, loc, "foreach statement cannot operate on " +
4664 "variables of type `{0}' because that class does " +
4665 "not provide a GetEnumerator method or it is " +
4666 "inaccessible", t.FullName);
4669 bool TryType (EmitContext ec, Type t)
4671 MethodGroupExpr mg = Expression.MemberLookup (
4672 ec, t, "GetEnumerator", MemberTypes.Method,
4673 Expression.AllBindingFlags, loc) as MethodGroupExpr;
4677 foreach (MethodBase mb in mg.Methods) {
4678 if (!GetEnumeratorFilter (ec, (MethodInfo) mb))
4681 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) mb };
4682 get_enumerator = new MethodGroupExpr (mi, loc);
4684 if (t != expr.Type) {
4685 expr = Convert.ExplicitConversion (
4688 throw new InternalErrorException ();
4691 get_enumerator.InstanceExpression = expr;
4692 get_enumerator.IsBase = t != expr.Type;
4700 bool ProbeCollectionType (EmitContext ec, Type t)
4702 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
4703 if (TryType (ec, tt))
4709 // Now try to find the method in the interfaces
4712 Type [] ifaces = t.GetInterfaces ();
4714 foreach (Type i in ifaces){
4715 if (TryType (ec, i))
4720 // Since TypeBuilder.GetInterfaces only returns the interface
4721 // types for this type, we have to keep looping, but once
4722 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4723 // done, because it returns all the types
4725 if ((t is TypeBuilder))
4734 public override bool Resolve (EmitContext ec)
4736 enumerator_type = TypeManager.ienumerator_type;
4737 is_disposable = true;
4739 if (!ProbeCollectionType (ec, expr.Type)) {
4740 error1579 (expr.Type, loc);
4744 enumerator = new TemporaryVariable (enumerator_type, loc);
4745 enumerator.Resolve (ec);
4747 init = new Invocation (get_enumerator, new ArrayList (), loc);
4748 init = init.Resolve (ec);
4752 Expression move_next_expr;
4754 MemberInfo[] mi = new MemberInfo[] { move_next };
4755 MethodGroupExpr mg = new MethodGroupExpr (mi, loc);
4756 mg.InstanceExpression = enumerator;
4758 move_next_expr = new Invocation (mg, new ArrayList (), loc);
4761 get_current.InstanceExpression = enumerator;
4763 Statement block = new CollectionForeachStatement (
4764 var_type, variable, get_current, statement, loc);
4766 loop = new While (move_next_expr, block, loc);
4770 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4771 ec.CurrentBranching.CreateSibling ();
4773 FlowBranchingException branching = null;
4775 branching = ec.StartFlowBranching (this);
4777 if (!loop.Resolve (ec))
4780 if (is_disposable) {
4781 ResolveFinally (branching);
4782 ec.EndFlowBranching ();
4784 emit_finally = true;
4786 ec.EndFlowBranching ();
4791 protected override void DoEmit (EmitContext ec)
4793 ILGenerator ig = ec.ig;
4795 enumerator.Store (ec, init);
4798 // Protect the code in a try/finalize block, so that
4799 // if the beast implement IDisposable, we get rid of it
4801 if (is_disposable && emit_finally)
4802 ig.BeginExceptionBlock ();
4807 // Now the finally block
4809 if (is_disposable) {
4812 ig.EndExceptionBlock ();
4817 public override void EmitFinally (EmitContext ec)
4819 ILGenerator ig = ec.ig;
4821 if (enumerator_type.IsValueType) {
4822 enumerator.Emit (ec);
4824 MethodInfo mi = FetchMethodDispose (enumerator_type);
4826 enumerator.EmitLoadAddress (ec);
4827 ig.Emit (OpCodes.Call, mi);
4829 enumerator.Emit (ec);
4830 ig.Emit (OpCodes.Box, enumerator_type);
4831 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4834 Label call_dispose = ig.DefineLabel ();
4836 enumerator.Emit (ec);
4837 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
4838 ig.Emit (OpCodes.Dup);
4839 ig.Emit (OpCodes.Brtrue_S, call_dispose);
4840 ig.Emit (OpCodes.Pop);
4842 Label end_finally = ig.DefineLabel ();
4843 ig.Emit (OpCodes.Br, end_finally);
4845 ig.MarkLabel (call_dispose);
4846 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4847 ig.MarkLabel (end_finally);
4852 protected class CollectionForeachStatement : Statement
4855 Expression variable, current, conv;
4856 Statement statement;
4859 public CollectionForeachStatement (Type type, Expression variable,
4860 Expression current, Statement statement,
4864 this.variable = variable;
4865 this.current = current;
4866 this.statement = statement;
4870 public override bool Resolve (EmitContext ec)
4872 current = current.Resolve (ec);
4873 if (current == null)
4876 conv = Convert.ExplicitConversion (ec, current, type, loc);
4880 assign = new Assign (variable, conv, loc);
4881 if (assign.Resolve (ec) == null)
4884 if (!statement.Resolve (ec))
4890 protected override void DoEmit (EmitContext ec)
4892 assign.EmitStatement (ec);
4893 statement.Emit (ec);