2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 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 {
22 /// Return value indicates whether all code paths emitted return.
24 public abstract bool Emit (EmitContext ec);
27 /// Emits a bool expression.
29 public static Expression EmitBoolExpression (EmitContext ec, Expression e,
30 Label target, bool isTrue, Location loc)
37 if (e.Type != TypeManager.bool_type){
38 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
44 31, loc, "Can not convert the expression to a boolean");
52 if (u.Oper == Unary.Operator.LogicalNot){
55 u.EmitLogicalNot (ec);
64 ec.ig.Emit (OpCodes.Brfalse, target);
66 ec.ig.Emit (OpCodes.Brtrue, target);
69 ec.ig.Emit (OpCodes.Brtrue, target);
71 ec.ig.Emit (OpCodes.Brfalse, target);
79 public class EmptyStatement : Statement {
80 public override bool Emit (EmitContext ec)
86 public class If : Statement {
87 public readonly Expression Expr;
88 public readonly Statement TrueStatement;
89 public readonly Statement FalseStatement;
92 public If (Expression expr, Statement trueStatement, Location l)
95 TrueStatement = trueStatement;
99 public If (Expression expr,
100 Statement trueStatement,
101 Statement falseStatement,
105 TrueStatement = trueStatement;
106 FalseStatement = falseStatement;
110 public override bool Emit (EmitContext ec)
112 ILGenerator ig = ec.ig;
113 Label false_target = ig.DefineLabel ();
115 bool is_true_ret, is_false_ret;
117 if (EmitBoolExpression (ec, Expr, false_target, false, loc) == null)
120 is_true_ret = TrueStatement.Emit (ec);
121 is_false_ret = is_true_ret;
123 if (FalseStatement != null){
124 bool branch_emitted = false;
126 end = ig.DefineLabel ();
128 ig.Emit (OpCodes.Br, end);
129 branch_emitted = true;
132 ig.MarkLabel (false_target);
133 is_false_ret = FalseStatement.Emit (ec);
138 ig.MarkLabel (false_target);
139 is_false_ret = false;
142 return is_true_ret && is_false_ret;
146 public class Do : Statement {
147 public readonly Expression Expr;
148 public readonly Statement EmbeddedStatement;
151 public Do (Statement statement, Expression boolExpr, Location l)
154 EmbeddedStatement = statement;
158 public override bool Emit (EmitContext ec)
160 ILGenerator ig = ec.ig;
161 Label loop = ig.DefineLabel ();
162 Label old_begin = ec.LoopBegin;
163 Label old_end = ec.LoopEnd;
164 bool old_inloop = ec.InLoop;
167 ec.LoopBegin = ig.DefineLabel ();
168 ec.LoopEnd = ig.DefineLabel ();
172 EmbeddedStatement.Emit (ec);
173 ig.MarkLabel (ec.LoopBegin);
174 e = EmitBoolExpression (ec, Expr, loop, true, loc);
175 ig.MarkLabel (ec.LoopEnd);
177 ec.LoopBegin = old_begin;
178 ec.LoopEnd = old_end;
179 ec.InLoop = old_inloop;
182 // Inform whether we are infinite or not
184 if (e is BoolConstant){
185 BoolConstant bc = (BoolConstant) e;
187 if (bc.Value == true)
195 public class While : Statement {
196 public readonly Expression Expr;
197 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;
249 public For (Statement initStatement,
255 InitStatement = initStatement;
257 Increment = increment;
258 Statement = statement;
262 public override bool Emit (EmitContext ec)
264 ILGenerator ig = ec.ig;
265 Label old_begin = ec.LoopBegin;
266 Label old_end = ec.LoopEnd;
267 bool old_inloop = ec.InLoop;
268 Label loop = ig.DefineLabel ();
271 if (InitStatement != null)
272 if (! (InitStatement is EmptyStatement))
273 InitStatement.Emit (ec);
275 ec.LoopBegin = ig.DefineLabel ();
276 ec.LoopEnd = ig.DefineLabel ();
282 // If test is null, there is no test, and we are just
286 e = EmitBoolExpression (ec, Test, ec.LoopEnd, false, loc);
289 ig.MarkLabel (ec.LoopBegin);
290 if (!(Increment is EmptyStatement))
292 ig.Emit (OpCodes.Br, loop);
293 ig.MarkLabel (ec.LoopEnd);
295 ec.LoopBegin = old_begin;
296 ec.LoopEnd = old_end;
297 ec.InLoop = old_inloop;
300 // Inform whether we are infinite or not
303 if (e is BoolConstant){
304 BoolConstant bc = (BoolConstant) e;
315 public class StatementExpression : Statement {
316 public readonly ExpressionStatement Expr;
318 public StatementExpression (ExpressionStatement expr)
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;
352 public readonly Location loc;
354 public Return (Expression expr, Location l)
360 public override bool Emit (EmitContext ec)
363 Report.Error (157,loc,"Control can not leave the body of the finally block");
367 if (ec.ReturnType == null){
369 Report.Error (127, loc, "Return with a value not allowed here");
374 Report.Error (126, loc, "An object of type `" +
375 TypeManager.CSharpName (ec.ReturnType) + "' is " +
376 "expected for the return statement");
380 Expr = Expr.Resolve (ec);
384 if (Expr.Type != ec.ReturnType)
385 Expr = Expression.ConvertImplicitRequired (
386 ec, Expr, ec.ReturnType, loc);
393 if (ec.InTry || ec.InCatch)
394 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
397 if (ec.InTry || ec.InCatch){
398 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
401 ec.ig.Emit (OpCodes.Ret);
407 public class Goto : Statement {
412 public Goto (Block parent_block, string label, Location l)
414 block = parent_block;
419 public string Target {
425 public override bool Emit (EmitContext ec)
427 LabeledStatement label = block.LookupLabel (target);
431 // Maybe we should catch this before?
435 "No such label `" + target + "' in this scope");
438 Label l = label.LabelTarget (ec);
439 ec.ig.Emit (OpCodes.Br, l);
445 public class LabeledStatement : Statement {
450 public LabeledStatement (string label_name)
452 this.label_name = label_name;
455 public Label LabelTarget (EmitContext ec)
459 label = ec.ig.DefineLabel ();
465 public override bool Emit (EmitContext ec)
468 ec.ig.MarkLabel (label);
476 /// `goto default' statement
478 public class GotoDefault : Statement {
481 public GotoDefault (Location l)
486 public override bool Emit (EmitContext ec)
488 if (ec.Switch == null){
489 Report.Error (153, loc, "goto default is only valid in a switch statement");
493 if (!ec.Switch.GotDefault){
494 Report.Error (159, loc, "No default target on switch statement");
497 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
503 /// `goto case' statement
505 public class GotoCase : Statement {
509 public GotoCase (Expression e, Location l)
515 public override bool Emit (EmitContext ec)
517 if (ec.Switch == null){
518 Report.Error (153, loc, "goto case is only valid in a switch statement");
522 expr = expr.Resolve (ec);
526 if (!(expr is Constant)){
527 Report.Error (159, loc, "Target expression for goto case is not constant");
531 object val = Expression.ConvertIntLiteral (
532 (Constant) expr, ec.Switch.SwitchType, loc);
537 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
542 "No such label 'case " + val + "': for the goto case");
545 ec.ig.Emit (OpCodes.Br, sl.ILLabel);
550 public class Throw : Statement {
551 public readonly Expression Expr;
554 public Throw (Expression expr, Location l)
560 public override bool Emit (EmitContext ec)
564 ec.ig.Emit (OpCodes.Rethrow);
568 "A throw statement with no argument is only " +
569 "allowed in a catch clause");
574 Expression e = Expr.Resolve (ec);
581 ec.ig.Emit (OpCodes.Throw);
587 public class Break : Statement {
590 public Break (Location l)
595 public override bool Emit (EmitContext ec)
597 ILGenerator ig = ec.ig;
599 if (ec.InLoop == false && ec.Switch == null){
600 Report.Error (139, loc, "No enclosing loop or switch to continue to");
604 ig.Emit (OpCodes.Br, ec.LoopEnd);
609 public class Continue : Statement {
612 public Continue (Location l)
617 public override bool Emit (EmitContext ec)
619 Label begin = ec.LoopBegin;
622 Report.Error (139, loc, "No enclosing loop to continue to");
627 // UGH: Non trivial. This Br might cross a try/catch boundary
631 // try { ... } catch { continue; }
635 // try {} catch { while () { continue; }}
637 ec.ig.Emit (OpCodes.Br, begin);
642 public class VariableInfo {
643 public readonly string Type;
644 public LocalBuilder LocalBuilder;
645 public Type VariableType;
646 public readonly Location Location;
650 public bool Assigned;
651 public bool ReadOnly;
653 public VariableInfo (string type, Location l)
664 throw new Exception ("Unassigned idx for variable");
674 public void MakePinned ()
676 TypeManager.MakePinned (LocalBuilder);
681 /// Block represents a C# block.
685 /// This class is used in a number of places: either to represent
686 /// explicit blocks that the programmer places or implicit blocks.
688 /// Implicit blocks are used as labels or to introduce variable
691 public class Block : Statement {
692 public readonly Block Parent;
693 public readonly bool Implicit;
696 // The statements in this block
698 StatementCollection statements;
701 // An array of Blocks. We keep track of children just
702 // to generate the local variable declarations.
704 // Statements and child statements are handled through the
710 // Labels. (label, block) pairs.
715 // Keeps track of (name, type) pairs
720 // Keeps track of constants
724 // Maps variable names to ILGenerator.LocalBuilders
726 Hashtable local_builders;
734 public Block (Block parent)
737 parent.AddChild (this);
739 this.Parent = parent;
740 this.Implicit = false;
745 public Block (Block parent, bool implicit_block)
748 parent.AddChild (this);
750 this.Parent = parent;
751 this.Implicit = true;
761 void AddChild (Block b)
763 if (children == null)
764 children = new ArrayList ();
770 /// Adds a label to the current block.
774 /// false if the name already exists in this block. true
778 public bool AddLabel (string name, LabeledStatement target)
781 labels = new Hashtable ();
782 if (labels.Contains (name))
785 labels.Add (name, target);
789 public LabeledStatement LookupLabel (string name)
792 if (labels.Contains (name))
793 return ((LabeledStatement) labels [name]);
797 return Parent.LookupLabel (name);
802 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
804 if (variables == null)
805 variables = new Hashtable ();
807 if (GetVariableType (name) != null)
812 Parameter p = pars.GetParameterByName (name, out idx);
817 VariableInfo vi = new VariableInfo (type, l);
819 variables.Add (name, vi);
821 // Console.WriteLine ("Adding {0} to {1}", name, ID);
825 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
827 if (AddVariable (type, name, pars, l) == null)
830 if (constants == null)
831 constants = new Hashtable ();
833 constants.Add (name, value);
837 public Hashtable Variables {
843 public VariableInfo GetVariableInfo (string name)
845 if (variables != null) {
847 temp = variables [name];
850 return (VariableInfo) temp;
855 return Parent.GetVariableInfo (name);
860 public string GetVariableType (string name)
862 VariableInfo vi = GetVariableInfo (name);
870 public Expression GetConstantExpression (string name)
872 if (constants != null) {
874 temp = constants [name];
877 return (Expression) temp;
881 return Parent.GetConstantExpression (name);
887 /// True if the variable named @name has been defined
890 public bool IsVariableDefined (string name)
892 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
893 if (variables != null) {
894 if (variables.Contains (name))
899 return Parent.IsVariableDefined (name);
905 /// True if the variable named @name is a constant
907 public bool IsConstant (string name)
911 e = GetConstantExpression (name);
917 /// Use to fetch the statement associated with this label
919 public Statement this [string name] {
921 return (Statement) labels [name];
926 /// A list of labels that were not used within this block
928 public string [] GetUnreferenced ()
930 // FIXME: Implement me
934 public StatementCollection Statements {
936 if (statements == null)
937 statements = new StatementCollection ();
943 public void AddStatement (Statement s)
945 if (statements == null)
946 statements = new StatementCollection ();
964 /// Emits the variable declarations and labels.
967 /// tc: is our typecontainer (to resolve type references)
968 /// ig: is the code generator:
969 /// toplevel: the toplevel block. This is used for checking
970 /// that no two labels with the same name are used.
972 public int EmitMeta (EmitContext ec, Block toplevel, int count)
974 DeclSpace ds = ec.DeclSpace;
975 ILGenerator ig = ec.ig;
978 // Process this block variables
980 if (variables != null){
981 local_builders = new Hashtable ();
983 foreach (DictionaryEntry de in variables){
984 string name = (string) de.Key;
985 VariableInfo vi = (VariableInfo) de.Value;
988 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
993 vi.LocalBuilder = ig.DeclareLocal (t);
996 if (constants == null)
999 Expression cv = (Expression) constants [name];
1003 Expression e = cv.Resolve (ec);
1007 if (!(e is Constant)){
1008 Report.Error (133, vi.Location,
1009 "The expression being assigned to `" +
1010 name + "' must be constant (" + e + ")");
1014 constants.Remove (name);
1015 constants.Add (name, e);
1020 // Now, handle the children
1022 if (children != null){
1023 foreach (Block b in children)
1024 count = b.EmitMeta (ec, toplevel, count);
1030 public void UsageWarning ()
1034 if (variables != null){
1035 foreach (DictionaryEntry de in variables){
1036 VariableInfo vi = (VariableInfo) de.Value;
1041 name = (string) de.Key;
1045 219, vi.Location, "The variable `" + name +
1046 "' is assigned but its value is never used");
1049 168, vi.Location, "The variable `" +
1051 "' is declared but never used");
1056 if (children != null)
1057 foreach (Block b in children)
1061 // static int count;
1063 public override bool Emit (EmitContext ec)
1065 bool is_ret = false;
1066 Block prev_block = ec.CurrentBlock;
1069 ec.CurrentBlock = this;
1071 // throw new Exception ();
1072 foreach (Statement s in Statements)
1073 is_ret = s.Emit (ec);
1076 ec.CurrentBlock = prev_block;
1081 public class SwitchLabel {
1084 public Location loc;
1085 public Label ILLabel;
1088 // if expr == null, then it is the default case.
1090 public SwitchLabel (Expression expr, Location l)
1096 public Expression Label {
1102 public object Converted {
1109 // Resolves the expression, reduces it to a literal if possible
1110 // and then converts it to the requested type.
1112 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1114 ILLabel = ec.ig.DefineLabel ();
1119 Expression e = label.Resolve (ec);
1124 if (!(e is Constant)){
1125 Console.WriteLine ("Value is: " + label);
1126 Report.Error (150, loc, "A constant value is expected");
1130 if (e is StringConstant || e is NullLiteral){
1131 if (required_type == TypeManager.string_type){
1133 ILLabel = ec.ig.DefineLabel ();
1138 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1139 if (converted == null)
1146 public class SwitchSection {
1147 // An array of SwitchLabels.
1148 public readonly ArrayList Labels;
1149 public readonly Block Block;
1151 public SwitchSection (ArrayList labels, Block block)
1158 public class Switch : Statement {
1159 public readonly ArrayList Sections;
1160 public Expression Expr;
1163 /// Maps constants whose type type SwitchType to their SwitchLabels.
1165 public Hashtable Elements;
1168 /// The governing switch type
1170 public Type SwitchType;
1176 Label default_target;
1180 // The types allowed to be implicitly cast from
1181 // on the governing type
1183 static Type [] allowed_types;
1185 public Switch (Expression e, ArrayList sects, Location l)
1192 public bool GotDefault {
1198 public Label DefaultTarget {
1200 return default_target;
1205 // Determines the governing type for a switch. The returned
1206 // expression might be the expression from the switch, or an
1207 // expression that includes any potential conversions to the
1208 // integral types or to string.
1210 Expression SwitchGoverningType (EmitContext ec, Type t)
1212 if (t == TypeManager.int32_type ||
1213 t == TypeManager.uint32_type ||
1214 t == TypeManager.char_type ||
1215 t == TypeManager.byte_type ||
1216 t == TypeManager.sbyte_type ||
1217 t == TypeManager.ushort_type ||
1218 t == TypeManager.short_type ||
1219 t == TypeManager.uint64_type ||
1220 t == TypeManager.int64_type ||
1221 t == TypeManager.string_type ||
1222 t.IsSubclassOf (TypeManager.enum_type))
1225 if (allowed_types == null){
1226 allowed_types = new Type [] {
1227 TypeManager.sbyte_type,
1228 TypeManager.byte_type,
1229 TypeManager.short_type,
1230 TypeManager.ushort_type,
1231 TypeManager.int32_type,
1232 TypeManager.uint32_type,
1233 TypeManager.int64_type,
1234 TypeManager.uint64_type,
1235 TypeManager.char_type,
1236 TypeManager.string_type
1241 // Try to find a *user* defined implicit conversion.
1243 // If there is no implicit conversion, or if there are multiple
1244 // conversions, we have to report an error
1246 Expression converted = null;
1247 foreach (Type tt in allowed_types){
1250 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1254 if (converted != null){
1255 Report.Error (-12, loc, "More than one conversion to an integral " +
1256 " type exists for type `" +
1257 TypeManager.CSharpName (Expr.Type)+"'");
1265 void error152 (string n)
1268 152, "The label `" + n + ":' " +
1269 "is already present on this switch statement");
1273 // Performs the basic sanity checks on the switch statement
1274 // (looks for duplicate keys and non-constant expressions).
1276 // It also returns a hashtable with the keys that we will later
1277 // use to compute the switch tables
1279 bool CheckSwitch (EmitContext ec)
1283 Elements = new Hashtable ();
1285 got_default = false;
1287 if (TypeManager.IsEnumType (SwitchType)){
1288 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1290 compare_type = SwitchType;
1292 foreach (SwitchSection ss in Sections){
1293 foreach (SwitchLabel sl in ss.Labels){
1294 if (!sl.ResolveAndReduce (ec, SwitchType)){
1299 if (sl.Label == null){
1301 error152 ("default");
1308 object key = sl.Converted;
1310 if (key is Constant)
1311 key = ((Constant) key).GetValue ();
1314 key = NullLiteral.Null;
1316 string lname = null;
1317 if (compare_type == TypeManager.uint64_type){
1318 ulong v = (ulong) key;
1320 if (Elements.Contains (v))
1321 lname = v.ToString ();
1323 Elements.Add (v, sl);
1324 } else if (compare_type == TypeManager.int64_type){
1325 long v = (long) key;
1327 if (Elements.Contains (v))
1328 lname = v.ToString ();
1330 Elements.Add (v, sl);
1331 } else if (compare_type == TypeManager.uint32_type){
1332 uint v = (uint) key;
1334 if (Elements.Contains (v))
1335 lname = v.ToString ();
1337 Elements.Add (v, sl);
1338 } else if (compare_type == TypeManager.char_type){
1339 char v = (char) key;
1341 if (Elements.Contains (v))
1342 lname = v.ToString ();
1344 Elements.Add (v, sl);
1345 } else if (compare_type == TypeManager.byte_type){
1346 byte v = (byte) key;
1348 if (Elements.Contains (v))
1349 lname = v.ToString ();
1351 Elements.Add (v, sl);
1352 } else if (compare_type == TypeManager.sbyte_type){
1353 sbyte v = (sbyte) key;
1355 if (Elements.Contains (v))
1356 lname = v.ToString ();
1358 Elements.Add (v, sl);
1359 } else if (compare_type == TypeManager.short_type){
1360 short v = (short) key;
1362 if (Elements.Contains (v))
1363 lname = v.ToString ();
1365 Elements.Add (v, sl);
1366 } else if (compare_type == TypeManager.ushort_type){
1367 ushort v = (ushort) key;
1369 if (Elements.Contains (v))
1370 lname = v.ToString ();
1372 Elements.Add (v, sl);
1373 } else if (compare_type == TypeManager.string_type){
1374 if (key is NullLiteral){
1375 if (Elements.Contains (NullLiteral.Null))
1378 Elements.Add (NullLiteral.Null, null);
1380 string s = (string) key;
1382 if (Elements.Contains (s))
1385 Elements.Add (s, sl);
1387 } else if (compare_type == TypeManager.int32_type) {
1390 if (Elements.Contains (v))
1391 lname = v.ToString ();
1393 Elements.Add (v, sl);
1395 throw new Exception ("Unknown switch type!" +
1396 SwitchType + " " + compare_type);
1400 error152 ("case + " + lname);
1411 void EmitObjectInteger (ILGenerator ig, object k)
1414 IntConstant.EmitInt (ig, (int) k);
1415 else if (k is Constant){
1416 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1417 } else if (k is uint)
1418 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1420 LongConstant.EmitLong (ig, (long) k);
1421 else if (k is ulong)
1422 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1424 IntConstant.EmitInt (ig, (int) ((char) k));
1425 else if (k is sbyte)
1426 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1428 IntConstant.EmitInt (ig, (int) ((byte) k));
1430 throw new Exception ("Unhandled case");
1434 // This simple emit switch works, but does not take advantage of the
1435 // `switch' opcode. The swithc opcode uses a jump table that we are not
1436 // computing at this point
1438 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1440 ILGenerator ig = ec.ig;
1441 Label end_of_switch = ig.DefineLabel ();
1442 Label next_test = ig.DefineLabel ();
1443 Label null_target = ig.DefineLabel ();
1444 bool default_found = false;
1445 bool first_test = true;
1446 bool pending_goto_end = false;
1447 bool all_return = true;
1448 bool is_string = false;
1452 // Special processing for strings: we cant compare
1455 if (SwitchType == TypeManager.string_type){
1456 ig.Emit (OpCodes.Ldloc, val);
1459 if (Elements.Contains (NullLiteral.Null)){
1460 ig.Emit (OpCodes.Brfalse, null_target);
1462 ig.Emit (OpCodes.Brfalse, default_target);
1464 ig.Emit (OpCodes.Ldloc, val);
1465 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1466 ig.Emit (OpCodes.Stloc, val);
1469 foreach (SwitchSection ss in Sections){
1470 Label sec_begin = ig.DefineLabel ();
1472 if (pending_goto_end)
1473 ig.Emit (OpCodes.Br, end_of_switch);
1475 int label_count = ss.Labels.Count;
1477 foreach (SwitchLabel sl in ss.Labels){
1478 ig.MarkLabel (sl.ILLabel);
1481 ig.MarkLabel (next_test);
1482 next_test = ig.DefineLabel ();
1485 // If we are the default target
1487 if (sl.Label == null){
1488 ig.MarkLabel (default_target);
1489 default_found = true;
1491 object lit = sl.Converted;
1493 if (lit is NullLiteral){
1495 if (label_count == 1)
1496 ig.Emit (OpCodes.Br, next_test);
1501 StringConstant str = (StringConstant) lit;
1503 ig.Emit (OpCodes.Ldloc, val);
1504 ig.Emit (OpCodes.Ldstr, str.Value);
1505 if (label_count == 1)
1506 ig.Emit (OpCodes.Bne_Un, next_test);
1508 ig.Emit (OpCodes.Beq, sec_begin);
1510 ig.Emit (OpCodes.Ldloc, val);
1511 EmitObjectInteger (ig, lit);
1512 ig.Emit (OpCodes.Ceq);
1513 if (label_count == 1)
1514 ig.Emit (OpCodes.Brfalse, next_test);
1516 ig.Emit (OpCodes.Brtrue, sec_begin);
1520 if (label_count != 1)
1521 ig.Emit (OpCodes.Br, next_test);
1524 ig.MarkLabel (null_target);
1525 ig.MarkLabel (sec_begin);
1526 if (ss.Block.Emit (ec))
1527 pending_goto_end = false;
1530 pending_goto_end = true;
1535 ig.MarkLabel (default_target);
1536 ig.MarkLabel (next_test);
1537 ig.MarkLabel (end_of_switch);
1542 public override bool Emit (EmitContext ec)
1544 Expr = Expr.Resolve (ec);
1548 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1549 if (new_expr == null){
1550 Report.Error (151, loc, "An integer type or string was expected for switch");
1555 SwitchType = new_expr.Type;
1557 if (!CheckSwitch (ec))
1560 // Store variable for comparission purposes
1561 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1563 ec.ig.Emit (OpCodes.Stloc, value);
1565 ILGenerator ig = ec.ig;
1567 default_target = ig.DefineLabel ();
1570 // Setup the codegen context
1572 Label old_end = ec.LoopEnd;
1573 Switch old_switch = ec.Switch;
1575 ec.LoopEnd = ig.DefineLabel ();
1579 bool all_return = SimpleSwitchEmit (ec, value);
1581 // Restore context state.
1582 ig.MarkLabel (ec.LoopEnd);
1585 // FIXME: I am emitting a nop, because the switch performs
1586 // no analysis on whether something ever reaches the end
1588 // try: b (int a) { switch (a) { default: return 0; } }
1589 ig.Emit (OpCodes.Nop);
1592 // Restore the previous context
1594 ec.LoopEnd = old_end;
1595 ec.Switch = old_switch;
1598 // Because we have a nop at the end
1604 public class Lock : Statement {
1605 public readonly Expression Expr;
1606 public readonly Statement Statement;
1609 public Lock (Expression expr, Statement stmt, Location l)
1616 public override bool Emit (EmitContext ec)
1618 Expression e = Expr.Resolve (ec);
1624 if (type.IsValueType){
1625 Report.Error (185, loc, "lock statement requires the expression to be " +
1626 " a reference type (type is: `" +
1627 TypeManager.CSharpName (type) + "'");
1631 ILGenerator ig = ec.ig;
1632 LocalBuilder temp = ig.DeclareLocal (type);
1635 ig.Emit (OpCodes.Dup);
1636 ig.Emit (OpCodes.Stloc, temp);
1637 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1640 Label end = ig.BeginExceptionBlock ();
1641 bool old_in_try = ec.InTry;
1643 Label finish = ig.DefineLabel ();
1644 Statement.Emit (ec);
1645 ec.InTry = old_in_try;
1646 // ig.Emit (OpCodes.Leave, finish);
1648 ig.MarkLabel (finish);
1651 ig.BeginFinallyBlock ();
1652 ig.Emit (OpCodes.Ldloc, temp);
1653 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1654 ig.EndExceptionBlock ();
1660 public class Unchecked : Statement {
1661 public readonly Block Block;
1663 public Unchecked (Block b)
1668 public override bool Emit (EmitContext ec)
1670 bool previous_state = ec.CheckState;
1671 bool previous_state_const = ec.ConstantCheckState;
1674 ec.CheckState = false;
1675 ec.ConstantCheckState = false;
1676 val = Block.Emit (ec);
1677 ec.CheckState = previous_state;
1678 ec.ConstantCheckState = previous_state_const;
1684 public class Checked : Statement {
1685 public readonly Block Block;
1687 public Checked (Block b)
1692 public override bool Emit (EmitContext ec)
1694 bool previous_state = ec.CheckState;
1695 bool previous_state_const = ec.ConstantCheckState;
1698 ec.CheckState = true;
1699 ec.ConstantCheckState = true;
1700 val = Block.Emit (ec);
1701 ec.CheckState = previous_state;
1702 ec.ConstantCheckState = previous_state_const;
1708 public class Unsafe : Statement {
1709 public readonly Block Block;
1711 public Unsafe (Block b)
1716 public override bool Emit (EmitContext ec)
1718 bool previous_state = ec.InUnsafe;
1722 val = Block.Emit (ec);
1723 ec.InUnsafe = previous_state;
1732 public class Fixed : Statement {
1734 ArrayList declarators;
1735 Statement statement;
1738 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
1741 declarators = decls;
1746 public override bool Emit (EmitContext ec)
1748 ILGenerator ig = ec.ig;
1751 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
1755 foreach (Pair p in declarators){
1756 VariableInfo vi = (VariableInfo) p.First;
1757 Expression e = (Expression) p.Second;
1760 // The rules for the possible declarators are pretty wise,
1761 // but the production on the grammar is more concise.
1763 // So we have to enforce these rules here.
1765 // We do not resolve before doing the case 1 test,
1766 // because the grammar is explicit in that the token &
1767 // is present, so we need to test for this particular case.
1771 // Case 1: & object.
1773 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
1774 Expression child = ((Unary) e).Expr;
1777 if (child is ParameterReference || child is LocalVariableReference){
1780 "No need to use fixed statement for parameters or " +
1781 "local variable declarations (address is already " +
1790 child = ((Unary) e).Expr;
1792 if (!TypeManager.VerifyUnManaged (child.Type, loc))
1796 // Store pointer in pinned location
1799 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1801 statement.Emit (ec);
1803 // Clear the pinned variable.
1804 ig.Emit (OpCodes.Ldc_I4_0);
1805 ig.Emit (OpCodes.Conv_U);
1806 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1818 if (e.Type.IsArray){
1819 Type array_type = e.Type.GetElementType ();
1823 // Provided that array_type is unmanaged,
1825 if (!TypeManager.VerifyUnManaged (array_type, loc))
1829 // and T* is implicitly convertible to the
1830 // pointer type given in the fixed statement.
1832 ArrayPtr array_ptr = new ArrayPtr (e);
1834 Expression converted = Expression.ConvertImplicitRequired (
1835 ec, array_ptr, vi.VariableType, loc);
1836 if (converted == null)
1840 // Store pointer in pinned location
1842 converted.Emit (ec);
1844 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1846 statement.Emit (ec);
1848 // Clear the pinned variable.
1849 ig.Emit (OpCodes.Ldc_I4_0);
1850 ig.Emit (OpCodes.Conv_U);
1851 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1859 if (e.Type == TypeManager.string_type){
1860 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
1861 TypeManager.MakePinned (pinned_string);
1864 ig.Emit (OpCodes.Stloc, pinned_string);
1866 Expression sptr = new StringPtr (pinned_string);
1867 Expression converted = Expression.ConvertImplicitRequired (
1868 ec, sptr, vi.VariableType, loc);
1870 if (converted == null)
1873 converted.Emit (ec);
1874 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1876 statement.Emit (ec);
1878 // Clear the pinned variable
1879 ig.Emit (OpCodes.Ldnull);
1880 ig.Emit (OpCodes.Stloc, pinned_string);
1888 public class Catch {
1889 public readonly string Type;
1890 public readonly string Name;
1891 public readonly Block Block;
1892 public readonly Location Location;
1894 public Catch (string type, string name, Block block, Location l)
1903 public class Try : Statement {
1904 public readonly Block Fini, Block;
1905 public readonly ArrayList Specific;
1906 public readonly Catch General;
1909 // specific, general and fini might all be null.
1911 public Try (Block block, ArrayList specific, Catch general, Block fini)
1913 if (specific == null && general == null){
1914 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1918 this.Specific = specific;
1919 this.General = general;
1923 public override bool Emit (EmitContext ec)
1925 ILGenerator ig = ec.ig;
1927 Label finish = ig.DefineLabel ();;
1930 end = ig.BeginExceptionBlock ();
1931 bool old_in_try = ec.InTry;
1933 returns = Block.Emit (ec);
1934 ec.InTry = old_in_try;
1937 // System.Reflection.Emit provides this automatically:
1938 // ig.Emit (OpCodes.Leave, finish);
1940 bool old_in_catch = ec.InCatch;
1942 DeclSpace ds = ec.DeclSpace;
1944 foreach (Catch c in Specific){
1945 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1948 if (catch_type == null)
1951 ig.BeginCatchBlock (catch_type);
1953 if (c.Name != null){
1954 vi = c.Block.GetVariableInfo (c.Name);
1956 Console.WriteLine ("This should not happen! variable does not exist in this block");
1957 Environment.Exit (0);
1960 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1962 ig.Emit (OpCodes.Pop);
1967 if (General != null){
1968 ig.BeginCatchBlock (TypeManager.object_type);
1969 ig.Emit (OpCodes.Pop);
1970 General.Block.Emit (ec);
1972 ec.InCatch = old_in_catch;
1974 ig.MarkLabel (finish);
1976 ig.BeginFinallyBlock ();
1977 bool old_in_finally = ec.InFinally;
1978 ec.InFinally = true;
1980 ec.InFinally = old_in_finally;
1983 ig.EndExceptionBlock ();
1986 // FIXME: Is this correct?
1987 // Replace with `returns' and check test-18, maybe we can
1988 // perform an optimization here.
1995 // FIXME: We still do not support the expression variant of the using
1998 public class Using : Statement {
1999 object expression_or_block;
2000 Statement Statement;
2003 public Using (object expression_or_block, Statement stmt, Location l)
2005 this.expression_or_block = expression_or_block;
2011 // Emits the code for the case of using using a local variable declaration.
2013 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2015 ILGenerator ig = ec.ig;
2016 Expression [] converted_vars;
2017 bool need_conv = false;
2018 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2025 // The type must be an IDisposable or an implicit conversion
2028 converted_vars = new Expression [var_list.Count];
2029 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2030 foreach (DictionaryEntry e in var_list){
2031 Expression var = (Expression) e.Key;
2033 var = var.Resolve (ec);
2037 converted_vars [i] = Expression.ConvertImplicit (
2038 ec, var, TypeManager.idisposable_type, loc);
2040 if (converted_vars [i] == null)
2048 bool old_in_try = ec.InTry;
2050 foreach (DictionaryEntry e in var_list){
2051 LocalVariableReference var = (LocalVariableReference) e.Key;
2052 Expression expr = (Expression) e.Value;
2055 a = new Assign (var, expr, loc);
2058 converted_vars [i] = var;
2062 ((ExpressionStatement) a).EmitStatement (ec);
2064 ig.BeginExceptionBlock ();
2067 Statement.Emit (ec);
2068 ec.InTry = old_in_try;
2070 bool old_in_finally = ec.InFinally;
2071 ec.InFinally = true;
2072 var_list.Reverse ();
2073 foreach (DictionaryEntry e in var_list){
2074 LocalVariableReference var = (LocalVariableReference) e.Key;
2075 Label skip = ig.DefineLabel ();
2078 ig.BeginFinallyBlock ();
2081 ig.Emit (OpCodes.Brfalse, skip);
2082 converted_vars [i].Emit (ec);
2083 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2084 ig.MarkLabel (skip);
2085 ig.EndExceptionBlock ();
2087 ec.InFinally = old_in_finally;
2092 bool EmitExpression (EmitContext ec, Expression expr)
2094 Type expr_type = expr.Type;
2095 Expression conv = null;
2097 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2098 conv = Expression.ConvertImplicit (
2099 ec, expr, TypeManager.idisposable_type, loc);
2106 // Make a copy of the expression and operate on that.
2108 ILGenerator ig = ec.ig;
2109 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2114 ig.Emit (OpCodes.Stloc, local_copy);
2116 bool old_in_try = ec.InTry;
2118 ig.BeginExceptionBlock ();
2119 Statement.Emit (ec);
2120 ec.InTry = old_in_try;
2122 Label skip = ig.DefineLabel ();
2123 bool old_in_finally = ec.InFinally;
2124 ig.BeginFinallyBlock ();
2125 ig.Emit (OpCodes.Ldloc, local_copy);
2126 ig.Emit (OpCodes.Brfalse, skip);
2127 ig.Emit (OpCodes.Ldloc, local_copy);
2128 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2129 ig.MarkLabel (skip);
2130 ec.InFinally = old_in_finally;
2131 ig.EndExceptionBlock ();
2136 public override bool Emit (EmitContext ec)
2138 if (expression_or_block is DictionaryEntry){
2139 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2140 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2142 return EmitLocalVariableDecls (ec, t, var_list);
2143 } if (expression_or_block is Expression){
2144 Expression e = (Expression) expression_or_block;
2150 return EmitExpression (ec, e);
2157 /// Implementation of the foreach C# statement
2159 public class Foreach : Statement {
2161 LocalVariableReference variable;
2163 Statement statement;
2166 public Foreach (string type, LocalVariableReference var, Expression expr,
2167 Statement stmt, Location l)
2170 this.variable = var;
2177 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2179 static MethodInfo FetchMethodMoveNext (Type t)
2181 MemberInfo [] move_next_list;
2183 move_next_list = TypeContainer.FindMembers (
2184 t, MemberTypes.Method,
2185 BindingFlags.Public | BindingFlags.Instance,
2186 Type.FilterName, "MoveNext");
2187 if (move_next_list == null || move_next_list.Length == 0)
2190 foreach (MemberInfo m in move_next_list){
2191 MethodInfo mi = (MethodInfo) m;
2194 args = TypeManager.GetArgumentTypes (mi);
2195 if (args != null && args.Length == 0){
2196 if (mi.ReturnType == TypeManager.bool_type)
2204 // Retrieves a `public T get_Current ()' method from the Type `t'
2206 static MethodInfo FetchMethodGetCurrent (Type t)
2208 MemberInfo [] move_next_list;
2210 move_next_list = TypeContainer.FindMembers (
2211 t, MemberTypes.Method,
2212 BindingFlags.Public | BindingFlags.Instance,
2213 Type.FilterName, "get_Current");
2214 if (move_next_list == null || move_next_list.Length == 0)
2217 foreach (MemberInfo m in move_next_list){
2218 MethodInfo mi = (MethodInfo) m;
2221 args = TypeManager.GetArgumentTypes (mi);
2222 if (args != null && args.Length == 0)
2229 // This struct records the helper methods used by the Foreach construct
2231 class ForeachHelperMethods {
2232 public EmitContext ec;
2233 public MethodInfo get_enumerator;
2234 public MethodInfo move_next;
2235 public MethodInfo get_current;
2237 public ForeachHelperMethods (EmitContext ec)
2243 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2248 if (!(m is MethodInfo))
2251 if (m.Name != "GetEnumerator")
2254 MethodInfo mi = (MethodInfo) m;
2255 Type [] args = TypeManager.GetArgumentTypes (mi);
2257 if (args.Length != 0)
2260 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2261 EmitContext ec = hm.ec;
2264 // Check whether GetEnumerator is accessible to us
2266 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2268 Type declaring = mi.DeclaringType;
2269 if (prot == MethodAttributes.Private){
2270 if (declaring != ec.ContainerType)
2272 } else if (prot == MethodAttributes.FamANDAssem){
2273 // If from a different assembly, false
2274 if (!(mi is MethodBuilder))
2277 // Are we being invoked from the same class, or from a derived method?
2279 if (ec.ContainerType != declaring){
2280 if (!ec.ContainerType.IsSubclassOf (declaring))
2283 } else if (prot == MethodAttributes.FamORAssem){
2284 if (!(mi is MethodBuilder ||
2285 ec.ContainerType == declaring ||
2286 ec.ContainerType.IsSubclassOf (declaring)))
2288 } if (prot == MethodAttributes.Family){
2289 if (!(ec.ContainerType == declaring ||
2290 ec.ContainerType.IsSubclassOf (declaring)))
2295 // Ok, we can access it, now make sure that we can do something
2296 // with this `GetEnumerator'
2298 if (mi.ReturnType == TypeManager.ienumerator_type ||
2299 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2300 hm.move_next = TypeManager.bool_movenext_void;
2301 hm.get_current = TypeManager.object_getcurrent_void;
2306 // Ok, so they dont return an IEnumerable, we will have to
2307 // find if they support the GetEnumerator pattern.
2309 Type return_type = mi.ReturnType;
2311 hm.move_next = FetchMethodMoveNext (return_type);
2312 if (hm.move_next == null)
2314 hm.get_current = FetchMethodGetCurrent (return_type);
2315 if (hm.get_current == null)
2322 /// This filter is used to find the GetEnumerator method
2323 /// on which IEnumerator operates
2325 static MemberFilter FilterEnumerator;
2329 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2332 void error1579 (Type t)
2334 Report.Error (1579, loc,
2335 "foreach statement cannot operate on variables of type `" +
2336 t.FullName + "' because that class does not provide a " +
2337 " GetEnumerator method or it is inaccessible");
2340 static bool TryType (Type t, ForeachHelperMethods hm)
2344 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2345 BindingFlags.Public | BindingFlags.NonPublic |
2346 BindingFlags.Instance,
2347 FilterEnumerator, hm);
2349 if (mi == null || mi.Length == 0)
2352 hm.get_enumerator = (MethodInfo) mi [0];
2357 // Looks for a usable GetEnumerator in the Type, and if found returns
2358 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2360 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2362 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2364 if (TryType (t, hm))
2368 // Now try to find the method in the interfaces
2371 Type [] ifaces = t.GetInterfaces ();
2373 foreach (Type i in ifaces){
2374 if (TryType (i, hm))
2379 // Since TypeBuilder.GetInterfaces only returns the interface
2380 // types for this type, we have to keep looping, but once
2381 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2382 // done, because it returns all the types
2384 if ((t is TypeBuilder))
2394 // FIXME: possible optimization.
2395 // We might be able to avoid creating `empty' if the type is the sam
2397 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2399 ILGenerator ig = ec.ig;
2400 LocalBuilder enumerator, disposable;
2401 Expression empty = new EmptyExpression ();
2405 // FIXME: maybe we can apply the same trick we do in the
2406 // array handling to avoid creating empty and conv in some cases.
2408 // Although it is not as important in this case, as the type
2409 // will not likely be object (what the enumerator will return).
2411 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2415 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2416 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2419 // Instantiate the enumerator
2421 if (expr.Type.IsValueType){
2422 if (expr is IMemoryLocation){
2423 IMemoryLocation ml = (IMemoryLocation) expr;
2425 ml.AddressOf (ec, AddressOp.Load);
2427 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2428 " does not implement IMemoryLocation");
2429 ig.Emit (OpCodes.Call, hm.get_enumerator);
2432 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2434 ig.Emit (OpCodes.Stloc, enumerator);
2437 // Protect the code in a try/finalize block, so that
2438 // if the beast implement IDisposable, we get rid of it
2440 Label l = ig.BeginExceptionBlock ();
2441 bool old_in_try = ec.InTry;
2444 Label end_try = ig.DefineLabel ();
2446 ig.MarkLabel (ec.LoopBegin);
2447 ig.Emit (OpCodes.Ldloc, enumerator);
2448 ig.Emit (OpCodes.Callvirt, hm.move_next);
2449 ig.Emit (OpCodes.Brfalse, end_try);
2450 ig.Emit (OpCodes.Ldloc, enumerator);
2451 ig.Emit (OpCodes.Callvirt, hm.get_current);
2452 variable.EmitAssign (ec, conv);
2453 statement.Emit (ec);
2454 ig.Emit (OpCodes.Br, ec.LoopBegin);
2455 ig.MarkLabel (end_try);
2456 ec.InTry = old_in_try;
2458 // The runtime provides this for us.
2459 // ig.Emit (OpCodes.Leave, end);
2462 // Now the finally block
2464 Label end_finally = ig.DefineLabel ();
2465 bool old_in_finally = ec.InFinally;
2466 ec.InFinally = true;
2467 ig.BeginFinallyBlock ();
2469 ig.Emit (OpCodes.Ldloc, enumerator);
2470 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2471 ig.Emit (OpCodes.Stloc, disposable);
2472 ig.Emit (OpCodes.Ldloc, disposable);
2473 ig.Emit (OpCodes.Brfalse, end_finally);
2474 ig.Emit (OpCodes.Ldloc, disposable);
2475 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2476 ig.MarkLabel (end_finally);
2477 ec.InFinally = old_in_finally;
2479 // The runtime generates this anyways.
2480 // ig.Emit (OpCodes.Endfinally);
2482 ig.EndExceptionBlock ();
2484 ig.MarkLabel (ec.LoopEnd);
2489 // FIXME: possible optimization.
2490 // We might be able to avoid creating `empty' if the type is the sam
2492 bool EmitArrayForeach (EmitContext ec, Type var_type)
2494 Type array_type = expr.Type;
2495 Type element_type = array_type.GetElementType ();
2496 Expression conv = null;
2497 Expression empty = new EmptyExpression (element_type);
2499 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2503 int rank = array_type.GetArrayRank ();
2504 ILGenerator ig = ec.ig;
2506 LocalBuilder copy = ig.DeclareLocal (array_type);
2509 // Make our copy of the array
2512 ig.Emit (OpCodes.Stloc, copy);
2515 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2519 ig.Emit (OpCodes.Ldc_I4_0);
2520 ig.Emit (OpCodes.Stloc, counter);
2521 test = ig.DefineLabel ();
2522 ig.Emit (OpCodes.Br, test);
2524 loop = ig.DefineLabel ();
2525 ig.MarkLabel (loop);
2527 ig.Emit (OpCodes.Ldloc, copy);
2528 ig.Emit (OpCodes.Ldloc, counter);
2529 ArrayAccess.EmitLoadOpcode (ig, var_type);
2531 variable.EmitAssign (ec, conv);
2533 statement.Emit (ec);
2535 ig.MarkLabel (ec.LoopBegin);
2536 ig.Emit (OpCodes.Ldloc, counter);
2537 ig.Emit (OpCodes.Ldc_I4_1);
2538 ig.Emit (OpCodes.Add);
2539 ig.Emit (OpCodes.Stloc, counter);
2541 ig.MarkLabel (test);
2542 ig.Emit (OpCodes.Ldloc, counter);
2543 ig.Emit (OpCodes.Ldloc, copy);
2544 ig.Emit (OpCodes.Ldlen);
2545 ig.Emit (OpCodes.Conv_I4);
2546 ig.Emit (OpCodes.Blt, loop);
2548 LocalBuilder [] dim_len = new LocalBuilder [rank];
2549 LocalBuilder [] dim_count = new LocalBuilder [rank];
2550 Label [] loop = new Label [rank];
2551 Label [] test = new Label [rank];
2554 for (dim = 0; dim < rank; dim++){
2555 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2556 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2557 test [dim] = ig.DefineLabel ();
2558 loop [dim] = ig.DefineLabel ();
2561 for (dim = 0; dim < rank; dim++){
2562 ig.Emit (OpCodes.Ldloc, copy);
2563 IntLiteral.EmitInt (ig, dim);
2564 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2565 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2568 for (dim = 0; dim < rank; dim++){
2569 ig.Emit (OpCodes.Ldc_I4_0);
2570 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2571 ig.Emit (OpCodes.Br, test [dim]);
2572 ig.MarkLabel (loop [dim]);
2575 ig.Emit (OpCodes.Ldloc, copy);
2576 for (dim = 0; dim < rank; dim++)
2577 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2580 // FIXME: Maybe we can cache the computation of `get'?
2582 Type [] args = new Type [rank];
2585 for (int i = 0; i < rank; i++)
2586 args [i] = TypeManager.int32_type;
2588 ModuleBuilder mb = RootContext.ModuleBuilder;
2589 get = mb.GetArrayMethod (
2591 CallingConventions.HasThis| CallingConventions.Standard,
2593 ig.Emit (OpCodes.Call, get);
2594 variable.EmitAssign (ec, conv);
2595 statement.Emit (ec);
2596 ig.MarkLabel (ec.LoopBegin);
2597 for (dim = rank - 1; dim >= 0; dim--){
2598 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2599 ig.Emit (OpCodes.Ldc_I4_1);
2600 ig.Emit (OpCodes.Add);
2601 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2603 ig.MarkLabel (test [dim]);
2604 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2605 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2606 ig.Emit (OpCodes.Blt, loop [dim]);
2609 ig.MarkLabel (ec.LoopEnd);
2614 public override bool Emit (EmitContext ec)
2619 expr = expr.Resolve (ec);
2623 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2624 if (var_type == null)
2628 // We need an instance variable. Not sure this is the best
2629 // way of doing this.
2631 // FIXME: When we implement propertyaccess, will those turn
2632 // out to return values in ExprClass? I think they should.
2634 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2635 expr.eclass == ExprClass.PropertyAccess)){
2636 error1579 (expr.Type);
2640 ILGenerator ig = ec.ig;
2642 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2643 bool old_inloop = ec.InLoop;
2644 ec.LoopBegin = ig.DefineLabel ();
2645 ec.LoopEnd = ig.DefineLabel ();
2648 if (expr.Type.IsArray)
2649 ret_val = EmitArrayForeach (ec, var_type);
2651 ForeachHelperMethods hm;
2653 hm = ProbeCollectionType (ec, expr.Type);
2657 ret_val = EmitCollectionForeach (ec, var_type, hm);
2660 ec.LoopBegin = old_begin;
2661 ec.LoopEnd = old_end;
2662 ec.InLoop = old_inloop;