2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001, 2002 Ximian, Inc.
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
15 namespace Mono.CSharp {
17 using System.Collections;
19 public abstract class Statement {
23 /// Return value indicates whether all code paths emitted return.
25 public abstract bool Emit (EmitContext ec);
28 /// Emits a bool expression.
30 public static Expression EmitBoolExpression (EmitContext ec, Expression e,
31 Label target, bool isTrue, Location loc)
38 if (e.Type != TypeManager.bool_type){
39 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
45 31, loc, "Can not convert the expression to a boolean");
49 if (CodeGen.SymbolWriter != null)
56 if (u.Oper == Unary.Operator.LogicalNot){
59 u.EmitLogicalNot (ec);
68 ec.ig.Emit (OpCodes.Brfalse, target);
70 ec.ig.Emit (OpCodes.Brtrue, target);
73 ec.ig.Emit (OpCodes.Brtrue, target);
75 ec.ig.Emit (OpCodes.Brfalse, target);
82 public class EmptyStatement : Statement {
83 public override bool Emit (EmitContext ec)
89 public class If : Statement {
90 public readonly Expression Expr;
91 public readonly Statement TrueStatement;
92 public readonly Statement FalseStatement;
94 public If (Expression expr, Statement trueStatement, Location l)
97 TrueStatement = trueStatement;
101 public If (Expression expr,
102 Statement trueStatement,
103 Statement falseStatement,
107 TrueStatement = trueStatement;
108 FalseStatement = falseStatement;
112 public override bool Emit (EmitContext ec)
114 ILGenerator ig = ec.ig;
115 Label false_target = ig.DefineLabel ();
117 bool is_true_ret, is_false_ret;
119 if (EmitBoolExpression (ec, Expr, false_target, false, loc) == null)
122 is_true_ret = TrueStatement.Emit (ec);
123 is_false_ret = is_true_ret;
125 if (FalseStatement != null){
126 bool branch_emitted = false;
128 end = ig.DefineLabel ();
130 ig.Emit (OpCodes.Br, end);
131 branch_emitted = true;
134 ig.MarkLabel (false_target);
135 is_false_ret = FalseStatement.Emit (ec);
140 ig.MarkLabel (false_target);
141 is_false_ret = false;
144 return is_true_ret && is_false_ret;
148 public class Do : Statement {
149 public readonly Expression Expr;
150 public readonly Statement EmbeddedStatement;
152 public Do (Statement statement, Expression boolExpr, Location l)
155 EmbeddedStatement = statement;
159 public override bool Emit (EmitContext ec)
161 ILGenerator ig = ec.ig;
162 Label loop = ig.DefineLabel ();
163 Label old_begin = ec.LoopBegin;
164 Label old_end = ec.LoopEnd;
165 bool old_inloop = ec.InLoop;
168 ec.LoopBegin = ig.DefineLabel ();
169 ec.LoopEnd = ig.DefineLabel ();
173 EmbeddedStatement.Emit (ec);
174 ig.MarkLabel (ec.LoopBegin);
175 e = EmitBoolExpression (ec, Expr, loop, true, loc);
176 ig.MarkLabel (ec.LoopEnd);
178 ec.LoopBegin = old_begin;
179 ec.LoopEnd = old_end;
180 ec.InLoop = old_inloop;
183 // Inform whether we are infinite or not
185 if (e is BoolConstant){
186 BoolConstant bc = (BoolConstant) e;
188 if (bc.Value == true)
196 public class While : Statement {
197 public readonly Expression Expr;
198 public readonly Statement Statement;
200 public While (Expression boolExpr, Statement statement, Location l)
203 Statement = statement;
207 public override bool Emit (EmitContext ec)
209 ILGenerator ig = ec.ig;
210 Label old_begin = ec.LoopBegin;
211 Label old_end = ec.LoopEnd;
212 bool old_inloop = ec.InLoop;
215 ec.LoopBegin = ig.DefineLabel ();
216 ec.LoopEnd = ig.DefineLabel ();
219 ig.MarkLabel (ec.LoopBegin);
220 e = EmitBoolExpression (ec, Expr, ec.LoopEnd, false, loc);
222 ig.Emit (OpCodes.Br, ec.LoopBegin);
223 ig.MarkLabel (ec.LoopEnd);
225 ec.LoopBegin = old_begin;
226 ec.LoopEnd = old_end;
227 ec.InLoop = old_inloop;
230 // Inform whether we are infinite or not
232 if (e is BoolConstant){
233 BoolConstant bc = (BoolConstant) e;
235 if (bc.Value == true)
242 public class For : Statement {
243 public readonly Statement InitStatement;
244 public readonly Expression Test;
245 public readonly Statement Increment;
246 public readonly Statement Statement;
248 public For (Statement initStatement,
254 InitStatement = initStatement;
256 Increment = increment;
257 Statement = statement;
261 public override bool Emit (EmitContext ec)
263 ILGenerator ig = ec.ig;
264 Label old_begin = ec.LoopBegin;
265 Label old_end = ec.LoopEnd;
266 bool old_inloop = ec.InLoop;
267 Label loop = ig.DefineLabel ();
270 if (InitStatement != null)
271 if (! (InitStatement is EmptyStatement))
272 InitStatement.Emit (ec);
274 ec.LoopBegin = ig.DefineLabel ();
275 ec.LoopEnd = ig.DefineLabel ();
281 // If test is null, there is no test, and we are just
285 e = EmitBoolExpression (ec, Test, ec.LoopEnd, false, loc);
288 ig.MarkLabel (ec.LoopBegin);
289 if (!(Increment is EmptyStatement))
291 ig.Emit (OpCodes.Br, loop);
292 ig.MarkLabel (ec.LoopEnd);
294 ec.LoopBegin = old_begin;
295 ec.LoopEnd = old_end;
296 ec.InLoop = old_inloop;
299 // Inform whether we are infinite or not
302 if (e is BoolConstant){
303 BoolConstant bc = (BoolConstant) e;
314 public class StatementExpression : Statement {
315 public readonly ExpressionStatement Expr;
317 public StatementExpression (ExpressionStatement expr, Location l)
323 public override bool Emit (EmitContext ec)
325 ILGenerator ig = ec.ig;
328 ne = Expr.Resolve (ec);
330 if (ne is ExpressionStatement)
331 ((ExpressionStatement) ne).EmitStatement (ec);
334 ig.Emit (OpCodes.Pop);
341 public override string ToString ()
343 return "StatementExpression (" + Expr + ")";
348 /// Implements the return statement
350 public class Return : Statement {
351 public Expression Expr;
353 public Return (Expression expr, Location l)
359 public override bool Emit (EmitContext ec)
362 Report.Error (157,loc,"Control can not leave the body of the finally block");
366 if (ec.ReturnType == null){
368 Report.Error (127, loc, "Return with a value not allowed here");
373 Report.Error (126, loc, "An object of type `" +
374 TypeManager.CSharpName (ec.ReturnType) + "' is " +
375 "expected for the return statement");
379 Expr = Expr.Resolve (ec);
383 if (Expr.Type != ec.ReturnType)
384 Expr = Expression.ConvertImplicitRequired (
385 ec, Expr, ec.ReturnType, loc);
392 if (ec.InTry || ec.InCatch)
393 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
396 if (ec.InTry || ec.InCatch)
397 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
399 ec.ig.Emit (OpCodes.Ret);
405 public class Goto : Statement {
409 public Goto (Block parent_block, string label, Location l)
411 block = parent_block;
416 public string Target {
422 public override bool Emit (EmitContext ec)
424 LabeledStatement label = block.LookupLabel (target);
428 // Maybe we should catch this before?
432 "No such label `" + target + "' in this scope");
435 Label l = label.LabelTarget (ec);
436 ec.ig.Emit (OpCodes.Br, l);
442 public class LabeledStatement : Statement {
447 public LabeledStatement (string label_name)
449 this.label_name = label_name;
452 public Label LabelTarget (EmitContext ec)
456 label = ec.ig.DefineLabel ();
462 public override bool Emit (EmitContext ec)
465 ec.ig.MarkLabel (label);
473 /// `goto default' statement
475 public class GotoDefault : Statement {
477 public GotoDefault (Location l)
482 public override bool Emit (EmitContext ec)
484 if (ec.Switch == null){
485 Report.Error (153, loc, "goto default is only valid in a switch statement");
489 if (!ec.Switch.GotDefault){
490 Report.Error (159, loc, "No default target on switch statement");
493 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
499 /// `goto case' statement
501 public class GotoCase : Statement {
504 public GotoCase (Expression e, Location l)
510 public override bool Emit (EmitContext ec)
512 if (ec.Switch == null){
513 Report.Error (153, loc, "goto case is only valid in a switch statement");
517 expr = expr.Resolve (ec);
521 if (!(expr is Constant)){
522 Report.Error (159, loc, "Target expression for goto case is not constant");
526 object val = Expression.ConvertIntLiteral (
527 (Constant) expr, ec.Switch.SwitchType, loc);
532 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
537 "No such label 'case " + val + "': for the goto case");
540 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
545 public class Throw : Statement {
546 public readonly Expression Expr;
548 public Throw (Expression expr, Location l)
554 public override bool Emit (EmitContext ec)
558 ec.ig.Emit (OpCodes.Rethrow);
562 "A throw statement with no argument is only " +
563 "allowed in a catch clause");
568 Expression e = Expr.Resolve (ec);
575 ec.ig.Emit (OpCodes.Throw);
581 public class Break : Statement {
583 public Break (Location l)
588 public override bool Emit (EmitContext ec)
590 ILGenerator ig = ec.ig;
592 if (ec.InLoop == false && ec.Switch == null){
593 Report.Error (139, loc, "No enclosing loop or switch to continue to");
597 ig.Emit (OpCodes.Br, ec.LoopEnd);
602 public class Continue : Statement {
604 public Continue (Location l)
609 public override bool Emit (EmitContext ec)
611 Label begin = ec.LoopBegin;
614 Report.Error (139, loc, "No enclosing loop to continue to");
619 // UGH: Non trivial. This Br might cross a try/catch boundary
623 // try { ... } catch { continue; }
627 // try {} catch { while () { continue; }}
629 ec.ig.Emit (OpCodes.Br, begin);
634 public class VariableInfo {
635 public readonly string Type;
636 public LocalBuilder LocalBuilder;
637 public Type VariableType;
638 public readonly Location Location;
641 public bool Assigned;
642 public bool ReadOnly;
644 public VariableInfo (string type, Location l)
651 public void MakePinned ()
653 TypeManager.MakePinned (LocalBuilder);
658 /// Block represents a C# block.
662 /// This class is used in a number of places: either to represent
663 /// explicit blocks that the programmer places or implicit blocks.
665 /// Implicit blocks are used as labels or to introduce variable
668 public class Block : Statement {
669 public readonly Block Parent;
670 public readonly bool Implicit;
671 public readonly Location StartLocation;
672 public Location EndLocation;
675 // The statements in this block
677 ArrayList statements;
680 // An array of Blocks. We keep track of children just
681 // to generate the local variable declarations.
683 // Statements and child statements are handled through the
689 // Labels. (label, block) pairs.
694 // Keeps track of (name, type) pairs
699 // Keeps track of constants
703 // Maps variable names to ILGenerator.LocalBuilders
705 Hashtable local_builders;
713 public Block (Block parent)
714 : this (parent, false, Location.Null, Location.Null)
717 public Block (Block parent, bool implicit_block)
718 : this (parent, implicit_block, Location.Null, Location.Null)
721 public Block (Block parent, Location start, Location end)
722 : this (parent, false, start, end)
725 public Block (Block parent, bool implicit_block, Location start, Location end)
728 parent.AddChild (this);
730 this.Parent = parent;
731 this.Implicit = implicit_block;
732 this.StartLocation = start;
733 this.EndLocation = end;
736 statements = new ArrayList ();
745 void AddChild (Block b)
747 if (children == null)
748 children = new ArrayList ();
753 public void SetEndLocation (Location loc)
759 /// Adds a label to the current block.
763 /// false if the name already exists in this block. true
767 public bool AddLabel (string name, LabeledStatement target)
770 labels = new Hashtable ();
771 if (labels.Contains (name))
774 labels.Add (name, target);
778 public LabeledStatement LookupLabel (string name)
781 if (labels.Contains (name))
782 return ((LabeledStatement) labels [name]);
786 return Parent.LookupLabel (name);
791 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
793 if (variables == null)
794 variables = new Hashtable ();
796 if (GetVariableType (name) != null)
801 Parameter p = pars.GetParameterByName (name, out idx);
806 VariableInfo vi = new VariableInfo (type, l);
808 variables.Add (name, vi);
810 // Console.WriteLine ("Adding {0} to {1}", name, ID);
814 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
816 if (AddVariable (type, name, pars, l) == null)
819 if (constants == null)
820 constants = new Hashtable ();
822 constants.Add (name, value);
826 public Hashtable Variables {
832 public VariableInfo GetVariableInfo (string name)
834 if (variables != null) {
836 temp = variables [name];
839 return (VariableInfo) temp;
844 return Parent.GetVariableInfo (name);
849 public string GetVariableType (string name)
851 VariableInfo vi = GetVariableInfo (name);
859 public Expression GetConstantExpression (string name)
861 if (constants != null) {
863 temp = constants [name];
866 return (Expression) temp;
870 return Parent.GetConstantExpression (name);
876 /// True if the variable named @name has been defined
879 public bool IsVariableDefined (string name)
881 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
882 if (variables != null) {
883 if (variables.Contains (name))
888 return Parent.IsVariableDefined (name);
894 /// True if the variable named @name is a constant
896 public bool IsConstant (string name)
900 e = GetConstantExpression (name);
906 /// Use to fetch the statement associated with this label
908 public Statement this [string name] {
910 return (Statement) labels [name];
915 /// A list of labels that were not used within this block
917 public string [] GetUnreferenced ()
919 // FIXME: Implement me
923 public void AddStatement (Statement s)
941 /// Emits the variable declarations and labels.
944 /// tc: is our typecontainer (to resolve type references)
945 /// ig: is the code generator:
946 /// toplevel: the toplevel block. This is used for checking
947 /// that no two labels with the same name are used.
949 public void EmitMeta (EmitContext ec, Block toplevel)
951 DeclSpace ds = ec.DeclSpace;
952 ILGenerator ig = ec.ig;
955 // Process this block variables
957 if (variables != null){
958 local_builders = new Hashtable ();
960 foreach (DictionaryEntry de in variables){
961 string name = (string) de.Key;
962 VariableInfo vi = (VariableInfo) de.Value;
965 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
970 vi.LocalBuilder = ig.DeclareLocal (t);
972 if (CodeGen.SymbolWriter != null)
973 vi.LocalBuilder.SetLocalSymInfo (name);
975 if (constants == null)
978 Expression cv = (Expression) constants [name];
982 Expression e = cv.Resolve (ec);
986 if (!(e is Constant)){
987 Report.Error (133, vi.Location,
988 "The expression being assigned to `" +
989 name + "' must be constant (" + e + ")");
993 constants.Remove (name);
994 constants.Add (name, e);
999 // Now, handle the children
1001 if (children != null){
1002 foreach (Block b in children)
1003 b.EmitMeta (ec, toplevel);
1007 public void UsageWarning ()
1011 if (variables != null){
1012 foreach (DictionaryEntry de in variables){
1013 VariableInfo vi = (VariableInfo) de.Value;
1018 name = (string) de.Key;
1022 219, vi.Location, "The variable `" + name +
1023 "' is assigned but its value is never used");
1026 168, vi.Location, "The variable `" +
1028 "' is declared but never used");
1033 if (children != null)
1034 foreach (Block b in children)
1038 public override bool Emit (EmitContext ec)
1040 bool is_ret = false;
1041 Block prev_block = ec.CurrentBlock;
1043 ec.CurrentBlock = this;
1045 if (CodeGen.SymbolWriter != null) {
1046 ec.Mark (StartLocation);
1048 foreach (Statement s in statements) {
1051 is_ret = s.Emit (ec);
1054 ec.Mark (EndLocation);
1056 foreach (Statement s in statements)
1057 is_ret = s.Emit (ec);
1060 ec.CurrentBlock = prev_block;
1065 public class SwitchLabel {
1068 public Location loc;
1069 public Label ILLabel;
1070 public Label ILLabelCode;
1073 // if expr == null, then it is the default case.
1075 public SwitchLabel (Expression expr, Location l)
1081 public Expression Label {
1087 public object Converted {
1094 // Resolves the expression, reduces it to a literal if possible
1095 // and then converts it to the requested type.
1097 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1099 ILLabel = ec.ig.DefineLabel ();
1100 ILLabelCode = ec.ig.DefineLabel ();
1105 Expression e = label.Resolve (ec);
1110 if (!(e is Constant)){
1111 Console.WriteLine ("Value is: " + label);
1112 Report.Error (150, loc, "A constant value is expected");
1116 if (e is StringConstant || e is NullLiteral){
1117 if (required_type == TypeManager.string_type){
1119 ILLabel = ec.ig.DefineLabel ();
1124 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1125 if (converted == null)
1132 public class SwitchSection {
1133 // An array of SwitchLabels.
1134 public readonly ArrayList Labels;
1135 public readonly Block Block;
1137 public SwitchSection (ArrayList labels, Block block)
1144 public class Switch : Statement {
1145 public readonly ArrayList Sections;
1146 public Expression Expr;
1149 /// Maps constants whose type type SwitchType to their SwitchLabels.
1151 public Hashtable Elements;
1154 /// The governing switch type
1156 public Type SwitchType;
1162 Label default_target;
1165 // The types allowed to be implicitly cast from
1166 // on the governing type
1168 static Type [] allowed_types;
1170 public Switch (Expression e, ArrayList sects, Location l)
1177 public bool GotDefault {
1183 public Label DefaultTarget {
1185 return default_target;
1190 // Determines the governing type for a switch. The returned
1191 // expression might be the expression from the switch, or an
1192 // expression that includes any potential conversions to the
1193 // integral types or to string.
1195 Expression SwitchGoverningType (EmitContext ec, Type t)
1197 if (t == TypeManager.int32_type ||
1198 t == TypeManager.uint32_type ||
1199 t == TypeManager.char_type ||
1200 t == TypeManager.byte_type ||
1201 t == TypeManager.sbyte_type ||
1202 t == TypeManager.ushort_type ||
1203 t == TypeManager.short_type ||
1204 t == TypeManager.uint64_type ||
1205 t == TypeManager.int64_type ||
1206 t == TypeManager.string_type ||
1207 t == TypeManager.bool_type ||
1208 t.IsSubclassOf (TypeManager.enum_type))
1211 if (allowed_types == null){
1212 allowed_types = new Type [] {
1213 TypeManager.sbyte_type,
1214 TypeManager.byte_type,
1215 TypeManager.short_type,
1216 TypeManager.ushort_type,
1217 TypeManager.int32_type,
1218 TypeManager.uint32_type,
1219 TypeManager.int64_type,
1220 TypeManager.uint64_type,
1221 TypeManager.char_type,
1222 TypeManager.bool_type,
1223 TypeManager.string_type
1228 // Try to find a *user* defined implicit conversion.
1230 // If there is no implicit conversion, or if there are multiple
1231 // conversions, we have to report an error
1233 Expression converted = null;
1234 foreach (Type tt in allowed_types){
1237 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1241 if (converted != null){
1242 Report.Error (-12, loc, "More than one conversion to an integral " +
1243 " type exists for type `" +
1244 TypeManager.CSharpName (Expr.Type)+"'");
1252 void error152 (string n)
1255 152, "The label `" + n + ":' " +
1256 "is already present on this switch statement");
1260 // Performs the basic sanity checks on the switch statement
1261 // (looks for duplicate keys and non-constant expressions).
1263 // It also returns a hashtable with the keys that we will later
1264 // use to compute the switch tables
1266 bool CheckSwitch (EmitContext ec)
1270 Elements = new Hashtable ();
1272 got_default = false;
1274 if (TypeManager.IsEnumType (SwitchType)){
1275 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1277 compare_type = SwitchType;
1279 foreach (SwitchSection ss in Sections){
1280 foreach (SwitchLabel sl in ss.Labels){
1281 if (!sl.ResolveAndReduce (ec, SwitchType)){
1286 if (sl.Label == null){
1288 error152 ("default");
1295 object key = sl.Converted;
1297 if (key is Constant)
1298 key = ((Constant) key).GetValue ();
1301 key = NullLiteral.Null;
1303 string lname = null;
1304 if (compare_type == TypeManager.uint64_type){
1305 ulong v = (ulong) key;
1307 if (Elements.Contains (v))
1308 lname = v.ToString ();
1310 Elements.Add (v, sl);
1311 } else if (compare_type == TypeManager.int64_type){
1312 long v = (long) key;
1314 if (Elements.Contains (v))
1315 lname = v.ToString ();
1317 Elements.Add (v, sl);
1318 } else if (compare_type == TypeManager.uint32_type){
1319 uint v = (uint) key;
1321 if (Elements.Contains (v))
1322 lname = v.ToString ();
1324 Elements.Add (v, sl);
1325 } else if (compare_type == TypeManager.char_type){
1326 char v = (char) key;
1328 if (Elements.Contains (v))
1329 lname = v.ToString ();
1331 Elements.Add (v, sl);
1332 } else if (compare_type == TypeManager.byte_type){
1333 byte v = (byte) key;
1335 if (Elements.Contains (v))
1336 lname = v.ToString ();
1338 Elements.Add (v, sl);
1339 } else if (compare_type == TypeManager.sbyte_type){
1340 sbyte v = (sbyte) key;
1342 if (Elements.Contains (v))
1343 lname = v.ToString ();
1345 Elements.Add (v, sl);
1346 } else if (compare_type == TypeManager.short_type){
1347 short v = (short) key;
1349 if (Elements.Contains (v))
1350 lname = v.ToString ();
1352 Elements.Add (v, sl);
1353 } else if (compare_type == TypeManager.ushort_type){
1354 ushort v = (ushort) key;
1356 if (Elements.Contains (v))
1357 lname = v.ToString ();
1359 Elements.Add (v, sl);
1360 } else if (compare_type == TypeManager.string_type){
1361 if (key is NullLiteral){
1362 if (Elements.Contains (NullLiteral.Null))
1365 Elements.Add (NullLiteral.Null, null);
1367 string s = (string) key;
1369 if (Elements.Contains (s))
1372 Elements.Add (s, sl);
1374 } else if (compare_type == TypeManager.int32_type) {
1377 if (Elements.Contains (v))
1378 lname = v.ToString ();
1380 Elements.Add (v, sl);
1381 } else if (compare_type == TypeManager.bool_type) {
1382 bool v = (bool) key;
1384 if (Elements.Contains (v))
1385 lname = v.ToString ();
1387 Elements.Add (v, sl);
1391 throw new Exception ("Unknown switch type!" +
1392 SwitchType + " " + compare_type);
1396 error152 ("case + " + lname);
1407 void EmitObjectInteger (ILGenerator ig, object k)
1410 IntConstant.EmitInt (ig, (int) k);
1411 else if (k is Constant) {
1412 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1415 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1418 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1420 IntConstant.EmitInt (ig, (int) (long) k);
1421 ig.Emit (OpCodes.Conv_I8);
1424 LongConstant.EmitLong (ig, (long) k);
1426 else if (k is ulong)
1428 if ((ulong) k < (1L<<32))
1430 IntConstant.EmitInt (ig, (int) (long) k);
1431 ig.Emit (OpCodes.Conv_U8);
1435 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1439 IntConstant.EmitInt (ig, (int) ((char) k));
1440 else if (k is sbyte)
1441 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1443 IntConstant.EmitInt (ig, (int) ((byte) k));
1444 else if (k is short)
1445 IntConstant.EmitInt (ig, (int) ((short) k));
1446 else if (k is ushort)
1447 IntConstant.EmitInt (ig, (int) ((ushort) k));
1449 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1451 throw new Exception ("Unhandled case");
1454 // structure used to hold blocks of keys while calculating table switch
1455 class KeyBlock : IComparable
1457 public KeyBlock (long _nFirst)
1459 nFirst = nLast = _nFirst;
1463 public ArrayList rgKeys = null;
1466 get { return (int) (nLast - nFirst + 1); }
1468 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1470 return kbLast.nLast - kbFirst.nFirst + 1;
1472 public int CompareTo (object obj)
1474 KeyBlock kb = (KeyBlock) obj;
1475 int nLength = Length;
1476 int nLengthOther = kb.Length;
1477 if (nLengthOther == nLength)
1478 return (int) (kb.nFirst - nFirst);
1479 return nLength - nLengthOther;
1484 /// This method emits code for a lookup-based switch statement (non-string)
1485 /// Basically it groups the cases into blocks that are at least half full,
1486 /// and then spits out individual lookup opcodes for each block.
1487 /// It emits the longest blocks first, and short blocks are just
1488 /// handled with direct compares.
1490 /// <param name="ec"></param>
1491 /// <param name="val"></param>
1492 /// <returns></returns>
1493 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1495 int cElements = Elements.Count;
1496 object [] rgKeys = new object [cElements];
1497 Elements.Keys.CopyTo (rgKeys, 0);
1498 Array.Sort (rgKeys);
1500 // initialize the block list with one element per key
1501 ArrayList rgKeyBlocks = new ArrayList ();
1502 foreach (object key in rgKeys)
1503 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1506 // iteratively merge the blocks while they are at least half full
1507 // there's probably a really cool way to do this with a tree...
1508 while (rgKeyBlocks.Count > 1)
1510 ArrayList rgKeyBlocksNew = new ArrayList ();
1511 kbCurr = (KeyBlock) rgKeyBlocks [0];
1512 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1514 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1515 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1518 kbCurr.nLast = kb.nLast;
1522 // start a new block
1523 rgKeyBlocksNew.Add (kbCurr);
1527 rgKeyBlocksNew.Add (kbCurr);
1528 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1530 rgKeyBlocks = rgKeyBlocksNew;
1533 // initialize the key lists
1534 foreach (KeyBlock kb in rgKeyBlocks)
1535 kb.rgKeys = new ArrayList ();
1537 // fill the key lists
1539 kbCurr = (KeyBlock) rgKeyBlocks [0];
1540 foreach (object key in rgKeys)
1542 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1544 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1545 kbCurr.rgKeys.Add (key);
1548 // sort the blocks so we can tackle the largest ones first
1549 rgKeyBlocks.Sort ();
1551 // okay now we can start...
1552 ILGenerator ig = ec.ig;
1553 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1554 Label lblDefault = new Label ();
1555 Type typeKeys = rgKeys [0].GetType (); // used for conversions
1557 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1559 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1560 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1563 foreach (object key in kb.rgKeys)
1565 ig.Emit (OpCodes.Ldloc, val);
1566 EmitObjectInteger (ig, key);
1567 SwitchLabel sl = (SwitchLabel) Elements [key];
1568 ig.Emit (OpCodes.Beq, sl.ILLabel);
1573 // TODO: if all the keys in the block are the same and there are
1574 // no gaps/defaults then just use a range-check.
1575 if (SwitchType == TypeManager.int64_type ||
1576 SwitchType == TypeManager.uint64_type)
1578 // TODO: optimize constant/I4 cases
1580 // check block range (could be > 2^31)
1581 ig.Emit (OpCodes.Ldloc, val);
1582 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1583 ig.Emit (OpCodes.Blt, lblDefault);
1584 ig.Emit (OpCodes.Ldloc, val);
1585 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1586 ig.Emit (OpCodes.Bgt, lblDefault);
1589 ig.Emit (OpCodes.Ldloc, val);
1592 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1593 ig.Emit (OpCodes.Sub);
1595 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1600 ig.Emit (OpCodes.Ldloc, val);
1601 int nFirst = (int) kb.nFirst;
1604 IntConstant.EmitInt (ig, nFirst);
1605 ig.Emit (OpCodes.Sub);
1607 else if (nFirst < 0)
1609 IntConstant.EmitInt (ig, -nFirst);
1610 ig.Emit (OpCodes.Add);
1614 // first, build the list of labels for the switch
1616 int cJumps = kb.Length;
1617 Label [] rgLabels = new Label [cJumps];
1618 for (int iJump = 0; iJump < cJumps; iJump++)
1620 object key = kb.rgKeys [iKey];
1621 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1623 SwitchLabel sl = (SwitchLabel) Elements [key];
1624 rgLabels [iJump] = sl.ILLabel;
1628 rgLabels [iJump] = lblDefault;
1630 // emit the switch opcode
1631 ig.Emit (OpCodes.Switch, rgLabels);
1634 // mark the default for this block
1636 ig.MarkLabel (lblDefault);
1639 // TODO: find the default case and emit it here,
1640 // to prevent having to do the following jump.
1641 // make sure to mark other labels in the default section
1643 // the last default just goes to the end
1644 ig.Emit (OpCodes.Br, lblDefault);
1646 // now emit the code for the sections
1647 bool fFoundDefault = false;
1648 bool fAllReturn = true;
1649 foreach (SwitchSection ss in Sections)
1651 foreach (SwitchLabel sl in ss.Labels)
1653 ig.MarkLabel (sl.ILLabel);
1654 ig.MarkLabel (sl.ILLabelCode);
1655 if (sl.Label == null)
1657 ig.MarkLabel (lblDefault);
1658 fFoundDefault = true;
1661 fAllReturn &= ss.Block.Emit (ec);
1662 //ig.Emit (OpCodes.Br, lblEnd);
1666 ig.MarkLabel (lblDefault);
1667 ig.MarkLabel (lblEnd);
1672 // This simple emit switch works, but does not take advantage of the
1674 // TODO: remove non-string logic from here
1675 // TODO: binary search strings?
1677 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1679 ILGenerator ig = ec.ig;
1680 Label end_of_switch = ig.DefineLabel ();
1681 Label next_test = ig.DefineLabel ();
1682 Label null_target = ig.DefineLabel ();
1683 bool default_found = false;
1684 bool first_test = true;
1685 bool pending_goto_end = false;
1686 bool all_return = true;
1687 bool is_string = false;
1691 // Special processing for strings: we cant compare
1694 if (SwitchType == TypeManager.string_type){
1695 ig.Emit (OpCodes.Ldloc, val);
1698 if (Elements.Contains (NullLiteral.Null)){
1699 ig.Emit (OpCodes.Brfalse, null_target);
1701 ig.Emit (OpCodes.Brfalse, default_target);
1703 ig.Emit (OpCodes.Ldloc, val);
1704 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1705 ig.Emit (OpCodes.Stloc, val);
1708 SwitchSection last_section;
1709 last_section = (SwitchSection) Sections [Sections.Count-1];
1711 foreach (SwitchSection ss in Sections){
1712 Label sec_begin = ig.DefineLabel ();
1714 if (pending_goto_end)
1715 ig.Emit (OpCodes.Br, end_of_switch);
1717 int label_count = ss.Labels.Count;
1719 foreach (SwitchLabel sl in ss.Labels){
1720 ig.MarkLabel (sl.ILLabel);
1723 ig.MarkLabel (next_test);
1724 next_test = ig.DefineLabel ();
1727 // If we are the default target
1729 if (sl.Label == null){
1730 ig.MarkLabel (default_target);
1731 default_found = true;
1733 object lit = sl.Converted;
1735 if (lit is NullLiteral){
1737 if (label_count == 1)
1738 ig.Emit (OpCodes.Br, next_test);
1743 StringConstant str = (StringConstant) lit;
1745 ig.Emit (OpCodes.Ldloc, val);
1746 ig.Emit (OpCodes.Ldstr, str.Value);
1747 if (label_count == 1)
1748 ig.Emit (OpCodes.Bne_Un, next_test);
1750 ig.Emit (OpCodes.Beq, sec_begin);
1752 ig.Emit (OpCodes.Ldloc, val);
1753 EmitObjectInteger (ig, lit);
1754 ig.Emit (OpCodes.Ceq);
1755 if (label_count == 1)
1756 ig.Emit (OpCodes.Brfalse, next_test);
1758 ig.Emit (OpCodes.Brtrue, sec_begin);
1762 if (label_count != 1 && ss != last_section)
1763 ig.Emit (OpCodes.Br, next_test);
1766 ig.MarkLabel (null_target);
1767 ig.MarkLabel (sec_begin);
1768 foreach (SwitchLabel sl in ss.Labels)
\r
1769 ig.MarkLabel (sl.ILLabelCode);
1770 if (ss.Block.Emit (ec))
1771 pending_goto_end = false;
1774 pending_goto_end = true;
1778 if (!default_found){
1779 ig.MarkLabel (default_target);
1782 ig.MarkLabel (next_test);
1783 ig.MarkLabel (end_of_switch);
1788 public override bool Emit (EmitContext ec)
1790 Expr = Expr.Resolve (ec);
1794 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1795 if (new_expr == null){
1796 Report.Error (151, loc, "An integer type or string was expected for switch");
1801 SwitchType = new_expr.Type;
1803 if (!CheckSwitch (ec))
1806 // Store variable for comparission purposes
1807 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1809 ec.ig.Emit (OpCodes.Stloc, value);
1811 ILGenerator ig = ec.ig;
1813 default_target = ig.DefineLabel ();
1816 // Setup the codegen context
1818 Label old_end = ec.LoopEnd;
1819 Switch old_switch = ec.Switch;
1821 ec.LoopEnd = ig.DefineLabel ();
1826 if (SwitchType == TypeManager.string_type)
1827 all_return = SimpleSwitchEmit (ec, value);
1829 all_return = TableSwitchEmit (ec, value);
1831 // Restore context state.
1832 ig.MarkLabel (ec.LoopEnd);
1835 // Restore the previous context
1837 ec.LoopEnd = old_end;
1838 ec.Switch = old_switch;
1844 public class Lock : Statement {
1845 public readonly Expression Expr;
1846 public readonly Statement Statement;
1848 public Lock (Expression expr, Statement stmt, Location l)
1855 public override bool Emit (EmitContext ec)
1857 Expression e = Expr.Resolve (ec);
1863 if (type.IsValueType){
1864 Report.Error (185, loc, "lock statement requires the expression to be " +
1865 " a reference type (type is: `" +
1866 TypeManager.CSharpName (type) + "'");
1870 ILGenerator ig = ec.ig;
1871 LocalBuilder temp = ig.DeclareLocal (type);
1874 ig.Emit (OpCodes.Dup);
1875 ig.Emit (OpCodes.Stloc, temp);
1876 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1879 Label end = ig.BeginExceptionBlock ();
1880 bool old_in_try = ec.InTry;
1882 Label finish = ig.DefineLabel ();
1883 Statement.Emit (ec);
1884 ec.InTry = old_in_try;
1885 // ig.Emit (OpCodes.Leave, finish);
1887 ig.MarkLabel (finish);
1890 ig.BeginFinallyBlock ();
1891 ig.Emit (OpCodes.Ldloc, temp);
1892 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1893 ig.EndExceptionBlock ();
1899 public class Unchecked : Statement {
1900 public readonly Block Block;
1902 public Unchecked (Block b)
1907 public override bool Emit (EmitContext ec)
1909 bool previous_state = ec.CheckState;
1910 bool previous_state_const = ec.ConstantCheckState;
1913 ec.CheckState = false;
1914 ec.ConstantCheckState = false;
1915 val = Block.Emit (ec);
1916 ec.CheckState = previous_state;
1917 ec.ConstantCheckState = previous_state_const;
1923 public class Checked : Statement {
1924 public readonly Block Block;
1926 public Checked (Block b)
1931 public override bool Emit (EmitContext ec)
1933 bool previous_state = ec.CheckState;
1934 bool previous_state_const = ec.ConstantCheckState;
1937 ec.CheckState = true;
1938 ec.ConstantCheckState = true;
1939 val = Block.Emit (ec);
1940 ec.CheckState = previous_state;
1941 ec.ConstantCheckState = previous_state_const;
1947 public class Unsafe : Statement {
1948 public readonly Block Block;
1950 public Unsafe (Block b)
1955 public override bool Emit (EmitContext ec)
1957 bool previous_state = ec.InUnsafe;
1961 val = Block.Emit (ec);
1962 ec.InUnsafe = previous_state;
1971 public class Fixed : Statement {
1973 ArrayList declarators;
1974 Statement statement;
1976 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
1979 declarators = decls;
1984 public override bool Emit (EmitContext ec)
1986 ILGenerator ig = ec.ig;
1989 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
1993 foreach (Pair p in declarators){
1994 VariableInfo vi = (VariableInfo) p.First;
1995 Expression e = (Expression) p.Second;
1998 // The rules for the possible declarators are pretty wise,
1999 // but the production on the grammar is more concise.
2001 // So we have to enforce these rules here.
2003 // We do not resolve before doing the case 1 test,
2004 // because the grammar is explicit in that the token &
2005 // is present, so we need to test for this particular case.
2009 // Case 1: & object.
2011 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2012 Expression child = ((Unary) e).Expr;
2015 if (child is ParameterReference || child is LocalVariableReference){
2018 "No need to use fixed statement for parameters or " +
2019 "local variable declarations (address is already " +
2028 child = ((Unary) e).Expr;
2030 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2034 // Store pointer in pinned location
2037 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2039 statement.Emit (ec);
2041 // Clear the pinned variable.
2042 ig.Emit (OpCodes.Ldc_I4_0);
2043 ig.Emit (OpCodes.Conv_U);
2044 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2056 if (e.Type.IsArray){
2057 Type array_type = e.Type.GetElementType ();
2061 // Provided that array_type is unmanaged,
2063 if (!TypeManager.VerifyUnManaged (array_type, loc))
2067 // and T* is implicitly convertible to the
2068 // pointer type given in the fixed statement.
2070 ArrayPtr array_ptr = new ArrayPtr (e);
2072 Expression converted = Expression.ConvertImplicitRequired (
2073 ec, array_ptr, vi.VariableType, loc);
2074 if (converted == null)
2078 // Store pointer in pinned location
2080 converted.Emit (ec);
2082 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2084 statement.Emit (ec);
2086 // Clear the pinned variable.
2087 ig.Emit (OpCodes.Ldc_I4_0);
2088 ig.Emit (OpCodes.Conv_U);
2089 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2097 if (e.Type == TypeManager.string_type){
2098 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2099 TypeManager.MakePinned (pinned_string);
2102 ig.Emit (OpCodes.Stloc, pinned_string);
2104 Expression sptr = new StringPtr (pinned_string);
2105 Expression converted = Expression.ConvertImplicitRequired (
2106 ec, sptr, vi.VariableType, loc);
2108 if (converted == null)
2111 converted.Emit (ec);
2112 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2114 statement.Emit (ec);
2116 // Clear the pinned variable
2117 ig.Emit (OpCodes.Ldnull);
2118 ig.Emit (OpCodes.Stloc, pinned_string);
2126 public class Catch {
2127 public readonly string Type;
2128 public readonly string Name;
2129 public readonly Block Block;
2130 public readonly Location Location;
2132 public Catch (string type, string name, Block block, Location l)
2141 public class Try : Statement {
2142 public readonly Block Fini, Block;
2143 public readonly ArrayList Specific;
2144 public readonly Catch General;
2147 // specific, general and fini might all be null.
2149 public Try (Block block, ArrayList specific, Catch general, Block fini)
2151 if (specific == null && general == null){
2152 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2156 this.Specific = specific;
2157 this.General = general;
2161 public override bool Emit (EmitContext ec)
2163 ILGenerator ig = ec.ig;
2165 Label finish = ig.DefineLabel ();;
2168 end = ig.BeginExceptionBlock ();
2169 bool old_in_try = ec.InTry;
2171 returns = Block.Emit (ec);
2172 ec.InTry = old_in_try;
2175 // System.Reflection.Emit provides this automatically:
2176 // ig.Emit (OpCodes.Leave, finish);
2178 bool old_in_catch = ec.InCatch;
2180 DeclSpace ds = ec.DeclSpace;
2182 foreach (Catch c in Specific){
2183 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2186 if (catch_type == null)
2189 ig.BeginCatchBlock (catch_type);
2191 if (c.Name != null){
2192 vi = c.Block.GetVariableInfo (c.Name);
2194 Console.WriteLine ("This should not happen! variable does not exist in this block");
2195 Environment.Exit (0);
2198 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2200 ig.Emit (OpCodes.Pop);
2202 if (!c.Block.Emit (ec))
2206 if (General != null){
2207 ig.BeginCatchBlock (TypeManager.object_type);
2208 ig.Emit (OpCodes.Pop);
2209 if (!General.Block.Emit (ec))
2212 ec.InCatch = old_in_catch;
2214 ig.MarkLabel (finish);
2216 ig.BeginFinallyBlock ();
2217 bool old_in_finally = ec.InFinally;
2218 ec.InFinally = true;
2220 ec.InFinally = old_in_finally;
2223 ig.EndExceptionBlock ();
2226 // FIXME: Is this correct?
2227 // Replace with `returns' and check test-18, maybe we can
2228 // perform an optimization here.
2235 // FIXME: We still do not support the expression variant of the using
2238 public class Using : Statement {
2239 object expression_or_block;
2240 Statement Statement;
2242 public Using (object expression_or_block, Statement stmt, Location l)
2244 this.expression_or_block = expression_or_block;
2250 // Emits the code for the case of using using a local variable declaration.
2252 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2254 ILGenerator ig = ec.ig;
2255 Expression [] converted_vars;
2256 bool need_conv = false;
2257 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2264 // The type must be an IDisposable or an implicit conversion
2267 converted_vars = new Expression [var_list.Count];
2268 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2269 foreach (DictionaryEntry e in var_list){
2270 Expression var = (Expression) e.Key;
2272 var = var.Resolve (ec);
2276 converted_vars [i] = Expression.ConvertImplicit (
2277 ec, var, TypeManager.idisposable_type, loc);
2279 if (converted_vars [i] == null)
2287 bool old_in_try = ec.InTry;
2289 foreach (DictionaryEntry e in var_list){
2290 LocalVariableReference var = (LocalVariableReference) e.Key;
2291 Expression expr = (Expression) e.Value;
2294 a = new Assign (var, expr, loc);
2297 converted_vars [i] = var;
2301 ((ExpressionStatement) a).EmitStatement (ec);
2303 ig.BeginExceptionBlock ();
2306 Statement.Emit (ec);
2307 ec.InTry = old_in_try;
2309 bool old_in_finally = ec.InFinally;
2310 ec.InFinally = true;
2311 var_list.Reverse ();
2312 foreach (DictionaryEntry e in var_list){
2313 LocalVariableReference var = (LocalVariableReference) e.Key;
2314 Label skip = ig.DefineLabel ();
2317 ig.BeginFinallyBlock ();
2320 ig.Emit (OpCodes.Brfalse, skip);
2321 converted_vars [i].Emit (ec);
2322 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2323 ig.MarkLabel (skip);
2324 ig.EndExceptionBlock ();
2326 ec.InFinally = old_in_finally;
2331 bool EmitExpression (EmitContext ec, Expression expr)
2333 Type expr_type = expr.Type;
2334 Expression conv = null;
2336 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2337 conv = Expression.ConvertImplicit (
2338 ec, expr, TypeManager.idisposable_type, loc);
2345 // Make a copy of the expression and operate on that.
2347 ILGenerator ig = ec.ig;
2348 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2353 ig.Emit (OpCodes.Stloc, local_copy);
2355 bool old_in_try = ec.InTry;
2357 ig.BeginExceptionBlock ();
2358 Statement.Emit (ec);
2359 ec.InTry = old_in_try;
2361 Label skip = ig.DefineLabel ();
2362 bool old_in_finally = ec.InFinally;
2363 ig.BeginFinallyBlock ();
2364 ig.Emit (OpCodes.Ldloc, local_copy);
2365 ig.Emit (OpCodes.Brfalse, skip);
2366 ig.Emit (OpCodes.Ldloc, local_copy);
2367 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2368 ig.MarkLabel (skip);
2369 ec.InFinally = old_in_finally;
2370 ig.EndExceptionBlock ();
2375 public override bool Emit (EmitContext ec)
2377 if (expression_or_block is DictionaryEntry){
2378 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2379 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2381 return EmitLocalVariableDecls (ec, t, var_list);
2382 } if (expression_or_block is Expression){
2383 Expression e = (Expression) expression_or_block;
2389 return EmitExpression (ec, e);
2396 /// Implementation of the foreach C# statement
2398 public class Foreach : Statement {
2400 LocalVariableReference variable;
2402 Statement statement;
2404 public Foreach (string type, LocalVariableReference var, Expression expr,
2405 Statement stmt, Location l)
2408 this.variable = var;
2415 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2417 static MethodInfo FetchMethodMoveNext (Type t)
2419 MemberInfo [] move_next_list;
2421 move_next_list = TypeContainer.FindMembers (
2422 t, MemberTypes.Method,
2423 BindingFlags.Public | BindingFlags.Instance,
2424 Type.FilterName, "MoveNext");
2425 if (move_next_list == null || move_next_list.Length == 0)
2428 foreach (MemberInfo m in move_next_list){
2429 MethodInfo mi = (MethodInfo) m;
2432 args = TypeManager.GetArgumentTypes (mi);
2433 if (args != null && args.Length == 0){
2434 if (mi.ReturnType == TypeManager.bool_type)
2442 // Retrieves a `public T get_Current ()' method from the Type `t'
2444 static MethodInfo FetchMethodGetCurrent (Type t)
2446 MemberInfo [] move_next_list;
2448 move_next_list = TypeContainer.FindMembers (
2449 t, MemberTypes.Method,
2450 BindingFlags.Public | BindingFlags.Instance,
2451 Type.FilterName, "get_Current");
2452 if (move_next_list == null || move_next_list.Length == 0)
2455 foreach (MemberInfo m in move_next_list){
2456 MethodInfo mi = (MethodInfo) m;
2459 args = TypeManager.GetArgumentTypes (mi);
2460 if (args != null && args.Length == 0)
2467 // This struct records the helper methods used by the Foreach construct
2469 class ForeachHelperMethods {
2470 public EmitContext ec;
2471 public MethodInfo get_enumerator;
2472 public MethodInfo move_next;
2473 public MethodInfo get_current;
2475 public ForeachHelperMethods (EmitContext ec)
2481 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2486 if (!(m is MethodInfo))
2489 if (m.Name != "GetEnumerator")
2492 MethodInfo mi = (MethodInfo) m;
2493 Type [] args = TypeManager.GetArgumentTypes (mi);
2495 if (args.Length != 0)
2498 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2499 EmitContext ec = hm.ec;
2502 // Check whether GetEnumerator is accessible to us
2504 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2506 Type declaring = mi.DeclaringType;
2507 if (prot == MethodAttributes.Private){
2508 if (declaring != ec.ContainerType)
2510 } else if (prot == MethodAttributes.FamANDAssem){
2511 // If from a different assembly, false
2512 if (!(mi is MethodBuilder))
2515 // Are we being invoked from the same class, or from a derived method?
2517 if (ec.ContainerType != declaring){
2518 if (!ec.ContainerType.IsSubclassOf (declaring))
2521 } else if (prot == MethodAttributes.FamORAssem){
2522 if (!(mi is MethodBuilder ||
2523 ec.ContainerType == declaring ||
2524 ec.ContainerType.IsSubclassOf (declaring)))
2526 } if (prot == MethodAttributes.Family){
2527 if (!(ec.ContainerType == declaring ||
2528 ec.ContainerType.IsSubclassOf (declaring)))
2533 // Ok, we can access it, now make sure that we can do something
2534 // with this `GetEnumerator'
2536 if (mi.ReturnType == TypeManager.ienumerator_type ||
2537 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2538 hm.move_next = TypeManager.bool_movenext_void;
2539 hm.get_current = TypeManager.object_getcurrent_void;
2544 // Ok, so they dont return an IEnumerable, we will have to
2545 // find if they support the GetEnumerator pattern.
2547 Type return_type = mi.ReturnType;
2549 hm.move_next = FetchMethodMoveNext (return_type);
2550 if (hm.move_next == null)
2552 hm.get_current = FetchMethodGetCurrent (return_type);
2553 if (hm.get_current == null)
2560 /// This filter is used to find the GetEnumerator method
2561 /// on which IEnumerator operates
2563 static MemberFilter FilterEnumerator;
2567 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2570 void error1579 (Type t)
2572 Report.Error (1579, loc,
2573 "foreach statement cannot operate on variables of type `" +
2574 t.FullName + "' because that class does not provide a " +
2575 " GetEnumerator method or it is inaccessible");
2578 static bool TryType (Type t, ForeachHelperMethods hm)
2582 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2583 BindingFlags.Public | BindingFlags.NonPublic |
2584 BindingFlags.Instance,
2585 FilterEnumerator, hm);
2587 if (mi == null || mi.Length == 0)
2590 hm.get_enumerator = (MethodInfo) mi [0];
2595 // Looks for a usable GetEnumerator in the Type, and if found returns
2596 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2598 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2600 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2602 if (TryType (t, hm))
2606 // Now try to find the method in the interfaces
2609 Type [] ifaces = t.GetInterfaces ();
2611 foreach (Type i in ifaces){
2612 if (TryType (i, hm))
2617 // Since TypeBuilder.GetInterfaces only returns the interface
2618 // types for this type, we have to keep looping, but once
2619 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2620 // done, because it returns all the types
2622 if ((t is TypeBuilder))
2632 // FIXME: possible optimization.
2633 // We might be able to avoid creating `empty' if the type is the sam
2635 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2637 ILGenerator ig = ec.ig;
2638 LocalBuilder enumerator, disposable;
2639 Expression empty = new EmptyExpression ();
2643 // FIXME: maybe we can apply the same trick we do in the
2644 // array handling to avoid creating empty and conv in some cases.
2646 // Although it is not as important in this case, as the type
2647 // will not likely be object (what the enumerator will return).
2649 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2653 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2654 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2657 // Instantiate the enumerator
2659 if (expr.Type.IsValueType){
2660 if (expr is IMemoryLocation){
2661 IMemoryLocation ml = (IMemoryLocation) expr;
2663 ml.AddressOf (ec, AddressOp.Load);
2665 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2666 " does not implement IMemoryLocation");
2667 ig.Emit (OpCodes.Call, hm.get_enumerator);
2670 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2672 ig.Emit (OpCodes.Stloc, enumerator);
2675 // Protect the code in a try/finalize block, so that
2676 // if the beast implement IDisposable, we get rid of it
2678 Label l = ig.BeginExceptionBlock ();
2679 bool old_in_try = ec.InTry;
2682 Label end_try = ig.DefineLabel ();
2684 ig.MarkLabel (ec.LoopBegin);
2685 ig.Emit (OpCodes.Ldloc, enumerator);
2686 ig.Emit (OpCodes.Callvirt, hm.move_next);
2687 ig.Emit (OpCodes.Brfalse, end_try);
2688 ig.Emit (OpCodes.Ldloc, enumerator);
2689 ig.Emit (OpCodes.Callvirt, hm.get_current);
2690 variable.EmitAssign (ec, conv);
2691 statement.Emit (ec);
2692 ig.Emit (OpCodes.Br, ec.LoopBegin);
2693 ig.MarkLabel (end_try);
2694 ec.InTry = old_in_try;
2696 // The runtime provides this for us.
2697 // ig.Emit (OpCodes.Leave, end);
2700 // Now the finally block
2702 Label end_finally = ig.DefineLabel ();
2703 bool old_in_finally = ec.InFinally;
2704 ec.InFinally = true;
2705 ig.BeginFinallyBlock ();
2707 ig.Emit (OpCodes.Ldloc, enumerator);
2708 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2709 ig.Emit (OpCodes.Stloc, disposable);
2710 ig.Emit (OpCodes.Ldloc, disposable);
2711 ig.Emit (OpCodes.Brfalse, end_finally);
2712 ig.Emit (OpCodes.Ldloc, disposable);
2713 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2714 ig.MarkLabel (end_finally);
2715 ec.InFinally = old_in_finally;
2717 // The runtime generates this anyways.
2718 // ig.Emit (OpCodes.Endfinally);
2720 ig.EndExceptionBlock ();
2722 ig.MarkLabel (ec.LoopEnd);
2727 // FIXME: possible optimization.
2728 // We might be able to avoid creating `empty' if the type is the sam
2730 bool EmitArrayForeach (EmitContext ec, Type var_type)
2732 Type array_type = expr.Type;
2733 Type element_type = array_type.GetElementType ();
2734 Expression conv = null;
2735 Expression empty = new EmptyExpression (element_type);
2737 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2741 int rank = array_type.GetArrayRank ();
2742 ILGenerator ig = ec.ig;
2744 LocalBuilder copy = ig.DeclareLocal (array_type);
2747 // Make our copy of the array
2750 ig.Emit (OpCodes.Stloc, copy);
2753 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2757 ig.Emit (OpCodes.Ldc_I4_0);
2758 ig.Emit (OpCodes.Stloc, counter);
2759 test = ig.DefineLabel ();
2760 ig.Emit (OpCodes.Br, test);
2762 loop = ig.DefineLabel ();
2763 ig.MarkLabel (loop);
2765 ig.Emit (OpCodes.Ldloc, copy);
2766 ig.Emit (OpCodes.Ldloc, counter);
2767 ArrayAccess.EmitLoadOpcode (ig, var_type);
2769 variable.EmitAssign (ec, conv);
2771 statement.Emit (ec);
2773 ig.MarkLabel (ec.LoopBegin);
2774 ig.Emit (OpCodes.Ldloc, counter);
2775 ig.Emit (OpCodes.Ldc_I4_1);
2776 ig.Emit (OpCodes.Add);
2777 ig.Emit (OpCodes.Stloc, counter);
2779 ig.MarkLabel (test);
2780 ig.Emit (OpCodes.Ldloc, counter);
2781 ig.Emit (OpCodes.Ldloc, copy);
2782 ig.Emit (OpCodes.Ldlen);
2783 ig.Emit (OpCodes.Conv_I4);
2784 ig.Emit (OpCodes.Blt, loop);
2786 LocalBuilder [] dim_len = new LocalBuilder [rank];
2787 LocalBuilder [] dim_count = new LocalBuilder [rank];
2788 Label [] loop = new Label [rank];
2789 Label [] test = new Label [rank];
2792 for (dim = 0; dim < rank; dim++){
2793 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2794 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2795 test [dim] = ig.DefineLabel ();
2796 loop [dim] = ig.DefineLabel ();
2799 for (dim = 0; dim < rank; dim++){
2800 ig.Emit (OpCodes.Ldloc, copy);
2801 IntLiteral.EmitInt (ig, dim);
2802 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2803 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2806 for (dim = 0; dim < rank; dim++){
2807 ig.Emit (OpCodes.Ldc_I4_0);
2808 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2809 ig.Emit (OpCodes.Br, test [dim]);
2810 ig.MarkLabel (loop [dim]);
2813 ig.Emit (OpCodes.Ldloc, copy);
2814 for (dim = 0; dim < rank; dim++)
2815 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2818 // FIXME: Maybe we can cache the computation of `get'?
2820 Type [] args = new Type [rank];
2823 for (int i = 0; i < rank; i++)
2824 args [i] = TypeManager.int32_type;
2826 ModuleBuilder mb = CodeGen.ModuleBuilder;
2827 get = mb.GetArrayMethod (
2829 CallingConventions.HasThis| CallingConventions.Standard,
2831 ig.Emit (OpCodes.Call, get);
2832 variable.EmitAssign (ec, conv);
2833 statement.Emit (ec);
2834 ig.MarkLabel (ec.LoopBegin);
2835 for (dim = rank - 1; dim >= 0; dim--){
2836 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2837 ig.Emit (OpCodes.Ldc_I4_1);
2838 ig.Emit (OpCodes.Add);
2839 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2841 ig.MarkLabel (test [dim]);
2842 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2843 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2844 ig.Emit (OpCodes.Blt, loop [dim]);
2847 ig.MarkLabel (ec.LoopEnd);
2852 public override bool Emit (EmitContext ec)
2857 expr = expr.Resolve (ec);
2861 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2862 if (var_type == null)
2866 // We need an instance variable. Not sure this is the best
2867 // way of doing this.
2869 // FIXME: When we implement propertyaccess, will those turn
2870 // out to return values in ExprClass? I think they should.
2872 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2873 expr.eclass == ExprClass.PropertyAccess)){
2874 error1579 (expr.Type);
2878 ILGenerator ig = ec.ig;
2880 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2881 bool old_inloop = ec.InLoop;
2882 ec.LoopBegin = ig.DefineLabel ();
2883 ec.LoopEnd = ig.DefineLabel ();
2886 if (expr.Type.IsArray)
2887 ret_val = EmitArrayForeach (ec, var_type);
2889 ForeachHelperMethods hm;
2891 hm = ProbeCollectionType (ec, expr.Type);
2895 ret_val = EmitCollectionForeach (ec, var_type, hm);
2898 ec.LoopBegin = old_begin;
2899 ec.LoopEnd = old_end;
2900 ec.InLoop = old_inloop;