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;
649 public bool Assigned;
650 public bool ReadOnly;
652 public VariableInfo (string type, Location l)
659 public void MakePinned ()
661 TypeManager.MakePinned (LocalBuilder);
666 /// Block represents a C# block.
670 /// This class is used in a number of places: either to represent
671 /// explicit blocks that the programmer places or implicit blocks.
673 /// Implicit blocks are used as labels or to introduce variable
676 public class Block : Statement {
677 public readonly Block Parent;
678 public readonly bool Implicit;
681 // The statements in this block
683 StatementCollection statements;
686 // An array of Blocks. We keep track of children just
687 // to generate the local variable declarations.
689 // Statements and child statements are handled through the
695 // Labels. (label, block) pairs.
700 // Keeps track of (name, type) pairs
705 // Keeps track of constants
709 // Maps variable names to ILGenerator.LocalBuilders
711 Hashtable local_builders;
719 public Block (Block parent)
722 parent.AddChild (this);
724 this.Parent = parent;
725 this.Implicit = false;
730 public Block (Block parent, bool implicit_block)
733 parent.AddChild (this);
735 this.Parent = parent;
736 this.Implicit = true;
746 void AddChild (Block b)
748 if (children == null)
749 children = new ArrayList ();
755 /// Adds a label to the current block.
759 /// false if the name already exists in this block. true
763 public bool AddLabel (string name, LabeledStatement target)
766 labels = new Hashtable ();
767 if (labels.Contains (name))
770 labels.Add (name, target);
774 public LabeledStatement LookupLabel (string name)
777 if (labels.Contains (name))
778 return ((LabeledStatement) labels [name]);
782 return Parent.LookupLabel (name);
787 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
789 if (variables == null)
790 variables = new Hashtable ();
792 if (GetVariableType (name) != null)
797 Parameter p = pars.GetParameterByName (name, out idx);
802 VariableInfo vi = new VariableInfo (type, l);
804 variables.Add (name, vi);
806 // Console.WriteLine ("Adding {0} to {1}", name, ID);
810 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
812 if (AddVariable (type, name, pars, l) == null)
815 if (constants == null)
816 constants = new Hashtable ();
818 constants.Add (name, value);
822 public Hashtable Variables {
828 public VariableInfo GetVariableInfo (string name)
830 if (variables != null) {
832 temp = variables [name];
835 return (VariableInfo) temp;
840 return Parent.GetVariableInfo (name);
845 public string GetVariableType (string name)
847 VariableInfo vi = GetVariableInfo (name);
855 public Expression GetConstantExpression (string name)
857 if (constants != null) {
859 temp = constants [name];
862 return (Expression) temp;
866 return Parent.GetConstantExpression (name);
872 /// True if the variable named @name has been defined
875 public bool IsVariableDefined (string name)
877 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
878 if (variables != null) {
879 if (variables.Contains (name))
884 return Parent.IsVariableDefined (name);
890 /// True if the variable named @name is a constant
892 public bool IsConstant (string name)
896 e = GetConstantExpression (name);
902 /// Use to fetch the statement associated with this label
904 public Statement this [string name] {
906 return (Statement) labels [name];
911 /// A list of labels that were not used within this block
913 public string [] GetUnreferenced ()
915 // FIXME: Implement me
919 public StatementCollection Statements {
921 if (statements == null)
922 statements = new StatementCollection ();
928 public void AddStatement (Statement s)
930 if (statements == null)
931 statements = new StatementCollection ();
949 /// Emits the variable declarations and labels.
952 /// tc: is our typecontainer (to resolve type references)
953 /// ig: is the code generator:
954 /// toplevel: the toplevel block. This is used for checking
955 /// that no two labels with the same name are used.
957 public void EmitMeta (EmitContext ec, Block toplevel)
959 DeclSpace ds = ec.DeclSpace;
960 ILGenerator ig = ec.ig;
963 // Process this block variables
965 if (variables != null){
966 local_builders = new Hashtable ();
968 foreach (DictionaryEntry de in variables){
969 string name = (string) de.Key;
970 VariableInfo vi = (VariableInfo) de.Value;
973 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
978 vi.LocalBuilder = ig.DeclareLocal (t);
980 if (constants == null)
983 Expression cv = (Expression) constants [name];
987 Expression e = cv.Resolve (ec);
991 if (!(e is Constant)){
992 Report.Error (133, vi.Location,
993 "The expression being assigned to `" +
994 name + "' must be constant (" + e + ")");
998 constants.Remove (name);
999 constants.Add (name, e);
1004 // Now, handle the children
1006 if (children != null){
1007 foreach (Block b in children)
1008 b.EmitMeta (ec, toplevel);
1012 public void UsageWarning ()
1016 if (variables != null){
1017 foreach (DictionaryEntry de in variables){
1018 VariableInfo vi = (VariableInfo) de.Value;
1023 name = (string) de.Key;
1027 219, vi.Location, "The variable `" + name +
1028 "' is assigned but its value is never used");
1031 168, vi.Location, "The variable `" +
1033 "' is declared but never used");
1038 if (children != null)
1039 foreach (Block b in children)
1043 // static int count;
1045 public override bool Emit (EmitContext ec)
1047 bool is_ret = false;
1048 Block prev_block = ec.CurrentBlock;
1051 ec.CurrentBlock = this;
1053 // throw new Exception ();
1054 foreach (Statement s in Statements)
1055 is_ret = s.Emit (ec);
1058 ec.CurrentBlock = prev_block;
1063 public class SwitchLabel {
1066 public Location loc;
1067 public Label ILLabel;
1070 // if expr == null, then it is the default case.
1072 public SwitchLabel (Expression expr, Location l)
1078 public Expression Label {
1084 public object Converted {
1091 // Resolves the expression, reduces it to a literal if possible
1092 // and then converts it to the requested type.
1094 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1096 ILLabel = ec.ig.DefineLabel ();
1101 Expression e = label.Resolve (ec);
1106 if (!(e is Constant)){
1107 Console.WriteLine ("Value is: " + label);
1108 Report.Error (150, loc, "A constant value is expected");
1112 if (e is StringConstant || e is NullLiteral){
1113 if (required_type == TypeManager.string_type){
1115 ILLabel = ec.ig.DefineLabel ();
1120 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1121 if (converted == null)
1128 public class SwitchSection {
1129 // An array of SwitchLabels.
1130 public readonly ArrayList Labels;
1131 public readonly Block Block;
1133 public SwitchSection (ArrayList labels, Block block)
1140 public class Switch : Statement {
1141 public readonly ArrayList Sections;
1142 public Expression Expr;
1145 /// Maps constants whose type type SwitchType to their SwitchLabels.
1147 public Hashtable Elements;
1150 /// The governing switch type
1152 public Type SwitchType;
1158 Label default_target;
1162 // The types allowed to be implicitly cast from
1163 // on the governing type
1165 static Type [] allowed_types;
1167 public Switch (Expression e, ArrayList sects, Location l)
1174 public bool GotDefault {
1180 public Label DefaultTarget {
1182 return default_target;
1187 // Determines the governing type for a switch. The returned
1188 // expression might be the expression from the switch, or an
1189 // expression that includes any potential conversions to the
1190 // integral types or to string.
1192 Expression SwitchGoverningType (EmitContext ec, Type t)
1194 if (t == TypeManager.int32_type ||
1195 t == TypeManager.uint32_type ||
1196 t == TypeManager.char_type ||
1197 t == TypeManager.byte_type ||
1198 t == TypeManager.sbyte_type ||
1199 t == TypeManager.ushort_type ||
1200 t == TypeManager.short_type ||
1201 t == TypeManager.uint64_type ||
1202 t == TypeManager.int64_type ||
1203 t == TypeManager.string_type ||
1204 t.IsSubclassOf (TypeManager.enum_type))
1207 if (allowed_types == null){
1208 allowed_types = new Type [] {
1209 TypeManager.sbyte_type,
1210 TypeManager.byte_type,
1211 TypeManager.short_type,
1212 TypeManager.ushort_type,
1213 TypeManager.int32_type,
1214 TypeManager.uint32_type,
1215 TypeManager.int64_type,
1216 TypeManager.uint64_type,
1217 TypeManager.char_type,
1218 TypeManager.string_type
1223 // Try to find a *user* defined implicit conversion.
1225 // If there is no implicit conversion, or if there are multiple
1226 // conversions, we have to report an error
1228 Expression converted = null;
1229 foreach (Type tt in allowed_types){
1232 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1236 if (converted != null){
1237 Report.Error (-12, loc, "More than one conversion to an integral " +
1238 " type exists for type `" +
1239 TypeManager.CSharpName (Expr.Type)+"'");
1247 void error152 (string n)
1250 152, "The label `" + n + ":' " +
1251 "is already present on this switch statement");
1255 // Performs the basic sanity checks on the switch statement
1256 // (looks for duplicate keys and non-constant expressions).
1258 // It also returns a hashtable with the keys that we will later
1259 // use to compute the switch tables
1261 bool CheckSwitch (EmitContext ec)
1265 Elements = new Hashtable ();
1267 got_default = false;
1269 if (TypeManager.IsEnumType (SwitchType)){
1270 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1272 compare_type = SwitchType;
1274 foreach (SwitchSection ss in Sections){
1275 foreach (SwitchLabel sl in ss.Labels){
1276 if (!sl.ResolveAndReduce (ec, SwitchType)){
1281 if (sl.Label == null){
1283 error152 ("default");
1290 object key = sl.Converted;
1292 if (key is Constant)
1293 key = ((Constant) key).GetValue ();
1296 key = NullLiteral.Null;
1298 string lname = null;
1299 if (compare_type == TypeManager.uint64_type){
1300 ulong v = (ulong) key;
1302 if (Elements.Contains (v))
1303 lname = v.ToString ();
1305 Elements.Add (v, sl);
1306 } else if (compare_type == TypeManager.int64_type){
1307 long v = (long) key;
1309 if (Elements.Contains (v))
1310 lname = v.ToString ();
1312 Elements.Add (v, sl);
1313 } else if (compare_type == TypeManager.uint32_type){
1314 uint v = (uint) key;
1316 if (Elements.Contains (v))
1317 lname = v.ToString ();
1319 Elements.Add (v, sl);
1320 } else if (compare_type == TypeManager.char_type){
1321 char v = (char) key;
1323 if (Elements.Contains (v))
1324 lname = v.ToString ();
1326 Elements.Add (v, sl);
1327 } else if (compare_type == TypeManager.byte_type){
1328 byte v = (byte) key;
1330 if (Elements.Contains (v))
1331 lname = v.ToString ();
1333 Elements.Add (v, sl);
1334 } else if (compare_type == TypeManager.sbyte_type){
1335 sbyte v = (sbyte) key;
1337 if (Elements.Contains (v))
1338 lname = v.ToString ();
1340 Elements.Add (v, sl);
1341 } else if (compare_type == TypeManager.short_type){
1342 short v = (short) key;
1344 if (Elements.Contains (v))
1345 lname = v.ToString ();
1347 Elements.Add (v, sl);
1348 } else if (compare_type == TypeManager.ushort_type){
1349 ushort v = (ushort) key;
1351 if (Elements.Contains (v))
1352 lname = v.ToString ();
1354 Elements.Add (v, sl);
1355 } else if (compare_type == TypeManager.string_type){
1356 if (key is NullLiteral){
1357 if (Elements.Contains (NullLiteral.Null))
1360 Elements.Add (NullLiteral.Null, null);
1362 string s = (string) key;
1364 if (Elements.Contains (s))
1367 Elements.Add (s, sl);
1369 } else if (compare_type == TypeManager.int32_type) {
1372 if (Elements.Contains (v))
1373 lname = v.ToString ();
1375 Elements.Add (v, sl);
1377 throw new Exception ("Unknown switch type!" +
1378 SwitchType + " " + compare_type);
1382 error152 ("case + " + lname);
1393 void EmitObjectInteger (ILGenerator ig, object k)
1396 IntConstant.EmitInt (ig, (int) k);
1397 else if (k is Constant){
1398 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1399 } else if (k is uint)
1400 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1402 LongConstant.EmitLong (ig, (long) k);
1403 else if (k is ulong)
1404 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1406 IntConstant.EmitInt (ig, (int) ((char) k));
1407 else if (k is sbyte)
1408 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1410 IntConstant.EmitInt (ig, (int) ((byte) k));
1412 throw new Exception ("Unhandled case");
1416 // This simple emit switch works, but does not take advantage of the
1417 // `switch' opcode. The swithc opcode uses a jump table that we are not
1418 // computing at this point
1420 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1422 ILGenerator ig = ec.ig;
1423 Label end_of_switch = ig.DefineLabel ();
1424 Label next_test = ig.DefineLabel ();
1425 Label null_target = ig.DefineLabel ();
1426 bool default_found = false;
1427 bool first_test = true;
1428 bool pending_goto_end = false;
1429 bool all_return = true;
1430 bool is_string = false;
1434 // Special processing for strings: we cant compare
1437 if (SwitchType == TypeManager.string_type){
1438 ig.Emit (OpCodes.Ldloc, val);
1441 if (Elements.Contains (NullLiteral.Null)){
1442 ig.Emit (OpCodes.Brfalse, null_target);
1444 ig.Emit (OpCodes.Brfalse, default_target);
1446 ig.Emit (OpCodes.Ldloc, val);
1447 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1448 ig.Emit (OpCodes.Stloc, val);
1451 foreach (SwitchSection ss in Sections){
1452 Label sec_begin = ig.DefineLabel ();
1454 if (pending_goto_end)
1455 ig.Emit (OpCodes.Br, end_of_switch);
1457 int label_count = ss.Labels.Count;
1459 foreach (SwitchLabel sl in ss.Labels){
1460 ig.MarkLabel (sl.ILLabel);
1463 ig.MarkLabel (next_test);
1464 next_test = ig.DefineLabel ();
1467 // If we are the default target
1469 if (sl.Label == null){
1470 ig.MarkLabel (default_target);
1471 default_found = true;
1473 object lit = sl.Converted;
1475 if (lit is NullLiteral){
1477 if (label_count == 1)
1478 ig.Emit (OpCodes.Br, next_test);
1483 StringConstant str = (StringConstant) lit;
1485 ig.Emit (OpCodes.Ldloc, val);
1486 ig.Emit (OpCodes.Ldstr, str.Value);
1487 if (label_count == 1)
1488 ig.Emit (OpCodes.Bne_Un, next_test);
1490 ig.Emit (OpCodes.Beq, sec_begin);
1492 ig.Emit (OpCodes.Ldloc, val);
1493 EmitObjectInteger (ig, lit);
1494 ig.Emit (OpCodes.Ceq);
1495 if (label_count == 1)
1496 ig.Emit (OpCodes.Brfalse, next_test);
1498 ig.Emit (OpCodes.Brtrue, sec_begin);
1502 if (label_count != 1)
1503 ig.Emit (OpCodes.Br, next_test);
1506 ig.MarkLabel (null_target);
1507 ig.MarkLabel (sec_begin);
1508 if (ss.Block.Emit (ec))
1509 pending_goto_end = false;
1512 pending_goto_end = true;
1517 ig.MarkLabel (default_target);
1518 ig.MarkLabel (next_test);
1519 ig.MarkLabel (end_of_switch);
1524 public override bool Emit (EmitContext ec)
1526 Expr = Expr.Resolve (ec);
1530 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1531 if (new_expr == null){
1532 Report.Error (151, loc, "An integer type or string was expected for switch");
1537 SwitchType = new_expr.Type;
1539 if (!CheckSwitch (ec))
1542 // Store variable for comparission purposes
1543 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1545 ec.ig.Emit (OpCodes.Stloc, value);
1547 ILGenerator ig = ec.ig;
1549 default_target = ig.DefineLabel ();
1552 // Setup the codegen context
1554 Label old_end = ec.LoopEnd;
1555 Switch old_switch = ec.Switch;
1557 ec.LoopEnd = ig.DefineLabel ();
1561 bool all_return = SimpleSwitchEmit (ec, value);
1563 // Restore context state.
1564 ig.MarkLabel (ec.LoopEnd);
1567 // FIXME: I am emitting a nop, because the switch performs
1568 // no analysis on whether something ever reaches the end
1570 // try: b (int a) { switch (a) { default: return 0; } }
1571 ig.Emit (OpCodes.Nop);
1574 // Restore the previous context
1576 ec.LoopEnd = old_end;
1577 ec.Switch = old_switch;
1580 // Because we have a nop at the end
1586 public class Lock : Statement {
1587 public readonly Expression Expr;
1588 public readonly Statement Statement;
1591 public Lock (Expression expr, Statement stmt, Location l)
1598 public override bool Emit (EmitContext ec)
1600 Expression e = Expr.Resolve (ec);
1606 if (type.IsValueType){
1607 Report.Error (185, loc, "lock statement requires the expression to be " +
1608 " a reference type (type is: `" +
1609 TypeManager.CSharpName (type) + "'");
1613 ILGenerator ig = ec.ig;
1614 LocalBuilder temp = ig.DeclareLocal (type);
1617 ig.Emit (OpCodes.Dup);
1618 ig.Emit (OpCodes.Stloc, temp);
1619 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1622 Label end = ig.BeginExceptionBlock ();
1623 bool old_in_try = ec.InTry;
1625 Label finish = ig.DefineLabel ();
1626 Statement.Emit (ec);
1627 ec.InTry = old_in_try;
1628 // ig.Emit (OpCodes.Leave, finish);
1630 ig.MarkLabel (finish);
1633 ig.BeginFinallyBlock ();
1634 ig.Emit (OpCodes.Ldloc, temp);
1635 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1636 ig.EndExceptionBlock ();
1642 public class Unchecked : Statement {
1643 public readonly Block Block;
1645 public Unchecked (Block b)
1650 public override bool Emit (EmitContext ec)
1652 bool previous_state = ec.CheckState;
1653 bool previous_state_const = ec.ConstantCheckState;
1656 ec.CheckState = false;
1657 ec.ConstantCheckState = false;
1658 val = Block.Emit (ec);
1659 ec.CheckState = previous_state;
1660 ec.ConstantCheckState = previous_state_const;
1666 public class Checked : Statement {
1667 public readonly Block Block;
1669 public Checked (Block b)
1674 public override bool Emit (EmitContext ec)
1676 bool previous_state = ec.CheckState;
1677 bool previous_state_const = ec.ConstantCheckState;
1680 ec.CheckState = true;
1681 ec.ConstantCheckState = true;
1682 val = Block.Emit (ec);
1683 ec.CheckState = previous_state;
1684 ec.ConstantCheckState = previous_state_const;
1690 public class Unsafe : Statement {
1691 public readonly Block Block;
1693 public Unsafe (Block b)
1698 public override bool Emit (EmitContext ec)
1700 bool previous_state = ec.InUnsafe;
1704 val = Block.Emit (ec);
1705 ec.InUnsafe = previous_state;
1714 public class Fixed : Statement {
1716 ArrayList declarators;
1717 Statement statement;
1720 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
1723 declarators = decls;
1728 public override bool Emit (EmitContext ec)
1730 ILGenerator ig = ec.ig;
1733 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
1737 foreach (Pair p in declarators){
1738 VariableInfo vi = (VariableInfo) p.First;
1739 Expression e = (Expression) p.Second;
1742 // The rules for the possible declarators are pretty wise,
1743 // but the production on the grammar is more concise.
1745 // So we have to enforce these rules here.
1747 // We do not resolve before doing the case 1 test,
1748 // because the grammar is explicit in that the token &
1749 // is present, so we need to test for this particular case.
1753 // Case 1: & object.
1755 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
1756 Expression child = ((Unary) e).Expr;
1759 if (child is ParameterReference || child is LocalVariableReference){
1762 "No need to use fixed statement for parameters or " +
1763 "local variable declarations (address is already " +
1772 child = ((Unary) e).Expr;
1774 if (!TypeManager.VerifyUnManaged (child.Type, loc))
1778 // Store pointer in pinned location
1781 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1783 statement.Emit (ec);
1785 // Clear the pinned variable.
1786 ig.Emit (OpCodes.Ldc_I4_0);
1787 ig.Emit (OpCodes.Conv_U);
1788 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1800 if (e.Type.IsArray){
1801 Type array_type = e.Type.GetElementType ();
1805 // Provided that array_type is unmanaged,
1807 if (!TypeManager.VerifyUnManaged (array_type, loc))
1811 // and T* is implicitly convertible to the
1812 // pointer type given in the fixed statement.
1814 ArrayPtr array_ptr = new ArrayPtr (e);
1816 Expression converted = Expression.ConvertImplicitRequired (
1817 ec, array_ptr, vi.VariableType, loc);
1818 if (converted == null)
1822 // Store pointer in pinned location
1824 converted.Emit (ec);
1826 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1828 statement.Emit (ec);
1830 // Clear the pinned variable.
1831 ig.Emit (OpCodes.Ldc_I4_0);
1832 ig.Emit (OpCodes.Conv_U);
1833 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1841 if (e.Type == TypeManager.string_type){
1842 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
1843 TypeManager.MakePinned (pinned_string);
1846 ig.Emit (OpCodes.Stloc, pinned_string);
1848 Expression sptr = new StringPtr (pinned_string);
1849 Expression converted = Expression.ConvertImplicitRequired (
1850 ec, sptr, vi.VariableType, loc);
1852 if (converted == null)
1855 converted.Emit (ec);
1856 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1858 statement.Emit (ec);
1860 // Clear the pinned variable
1861 ig.Emit (OpCodes.Ldnull);
1862 ig.Emit (OpCodes.Stloc, pinned_string);
1870 public class Catch {
1871 public readonly string Type;
1872 public readonly string Name;
1873 public readonly Block Block;
1874 public readonly Location Location;
1876 public Catch (string type, string name, Block block, Location l)
1885 public class Try : Statement {
1886 public readonly Block Fini, Block;
1887 public readonly ArrayList Specific;
1888 public readonly Catch General;
1891 // specific, general and fini might all be null.
1893 public Try (Block block, ArrayList specific, Catch general, Block fini)
1895 if (specific == null && general == null){
1896 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1900 this.Specific = specific;
1901 this.General = general;
1905 public override bool Emit (EmitContext ec)
1907 ILGenerator ig = ec.ig;
1909 Label finish = ig.DefineLabel ();;
1912 end = ig.BeginExceptionBlock ();
1913 bool old_in_try = ec.InTry;
1915 returns = Block.Emit (ec);
1916 ec.InTry = old_in_try;
1919 // System.Reflection.Emit provides this automatically:
1920 // ig.Emit (OpCodes.Leave, finish);
1922 bool old_in_catch = ec.InCatch;
1924 DeclSpace ds = ec.DeclSpace;
1926 foreach (Catch c in Specific){
1927 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1930 if (catch_type == null)
1933 ig.BeginCatchBlock (catch_type);
1935 if (c.Name != null){
1936 vi = c.Block.GetVariableInfo (c.Name);
1938 Console.WriteLine ("This should not happen! variable does not exist in this block");
1939 Environment.Exit (0);
1942 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1944 ig.Emit (OpCodes.Pop);
1949 if (General != null){
1950 ig.BeginCatchBlock (TypeManager.object_type);
1951 ig.Emit (OpCodes.Pop);
1952 General.Block.Emit (ec);
1954 ec.InCatch = old_in_catch;
1956 ig.MarkLabel (finish);
1958 ig.BeginFinallyBlock ();
1959 bool old_in_finally = ec.InFinally;
1960 ec.InFinally = true;
1962 ec.InFinally = old_in_finally;
1965 ig.EndExceptionBlock ();
1968 // FIXME: Is this correct?
1969 // Replace with `returns' and check test-18, maybe we can
1970 // perform an optimization here.
1977 // FIXME: We still do not support the expression variant of the using
1980 public class Using : Statement {
1981 object expression_or_block;
1982 Statement Statement;
1985 public Using (object expression_or_block, Statement stmt, Location l)
1987 this.expression_or_block = expression_or_block;
1993 // Emits the code for the case of using using a local variable declaration.
1995 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
1997 ILGenerator ig = ec.ig;
1998 Expression [] converted_vars;
1999 bool need_conv = false;
2000 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2007 // The type must be an IDisposable or an implicit conversion
2010 converted_vars = new Expression [var_list.Count];
2011 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2012 foreach (DictionaryEntry e in var_list){
2013 Expression var = (Expression) e.Key;
2015 var = var.Resolve (ec);
2019 converted_vars [i] = Expression.ConvertImplicit (
2020 ec, var, TypeManager.idisposable_type, loc);
2022 if (converted_vars [i] == null)
2030 bool old_in_try = ec.InTry;
2032 foreach (DictionaryEntry e in var_list){
2033 LocalVariableReference var = (LocalVariableReference) e.Key;
2034 Expression expr = (Expression) e.Value;
2037 a = new Assign (var, expr, loc);
2040 converted_vars [i] = var;
2044 ((ExpressionStatement) a).EmitStatement (ec);
2046 ig.BeginExceptionBlock ();
2049 Statement.Emit (ec);
2050 ec.InTry = old_in_try;
2052 bool old_in_finally = ec.InFinally;
2053 ec.InFinally = true;
2054 var_list.Reverse ();
2055 foreach (DictionaryEntry e in var_list){
2056 LocalVariableReference var = (LocalVariableReference) e.Key;
2057 Label skip = ig.DefineLabel ();
2060 ig.BeginFinallyBlock ();
2063 ig.Emit (OpCodes.Brfalse, skip);
2064 converted_vars [i].Emit (ec);
2065 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2066 ig.MarkLabel (skip);
2067 ig.EndExceptionBlock ();
2069 ec.InFinally = old_in_finally;
2074 bool EmitExpression (EmitContext ec, Expression expr)
2076 Type expr_type = expr.Type;
2077 Expression conv = null;
2079 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2080 conv = Expression.ConvertImplicit (
2081 ec, expr, TypeManager.idisposable_type, loc);
2088 // Make a copy of the expression and operate on that.
2090 ILGenerator ig = ec.ig;
2091 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2096 ig.Emit (OpCodes.Stloc, local_copy);
2098 bool old_in_try = ec.InTry;
2100 ig.BeginExceptionBlock ();
2101 Statement.Emit (ec);
2102 ec.InTry = old_in_try;
2104 Label skip = ig.DefineLabel ();
2105 bool old_in_finally = ec.InFinally;
2106 ig.BeginFinallyBlock ();
2107 ig.Emit (OpCodes.Ldloc, local_copy);
2108 ig.Emit (OpCodes.Brfalse, skip);
2109 ig.Emit (OpCodes.Ldloc, local_copy);
2110 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2111 ig.MarkLabel (skip);
2112 ec.InFinally = old_in_finally;
2113 ig.EndExceptionBlock ();
2118 public override bool Emit (EmitContext ec)
2120 if (expression_or_block is DictionaryEntry){
2121 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2122 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2124 return EmitLocalVariableDecls (ec, t, var_list);
2125 } if (expression_or_block is Expression){
2126 Expression e = (Expression) expression_or_block;
2132 return EmitExpression (ec, e);
2139 /// Implementation of the foreach C# statement
2141 public class Foreach : Statement {
2143 LocalVariableReference variable;
2145 Statement statement;
2148 public Foreach (string type, LocalVariableReference var, Expression expr,
2149 Statement stmt, Location l)
2152 this.variable = var;
2159 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2161 static MethodInfo FetchMethodMoveNext (Type t)
2163 MemberInfo [] move_next_list;
2165 move_next_list = TypeContainer.FindMembers (
2166 t, MemberTypes.Method,
2167 BindingFlags.Public | BindingFlags.Instance,
2168 Type.FilterName, "MoveNext");
2169 if (move_next_list == null || move_next_list.Length == 0)
2172 foreach (MemberInfo m in move_next_list){
2173 MethodInfo mi = (MethodInfo) m;
2176 args = TypeManager.GetArgumentTypes (mi);
2177 if (args != null && args.Length == 0){
2178 if (mi.ReturnType == TypeManager.bool_type)
2186 // Retrieves a `public T get_Current ()' method from the Type `t'
2188 static MethodInfo FetchMethodGetCurrent (Type t)
2190 MemberInfo [] move_next_list;
2192 move_next_list = TypeContainer.FindMembers (
2193 t, MemberTypes.Method,
2194 BindingFlags.Public | BindingFlags.Instance,
2195 Type.FilterName, "get_Current");
2196 if (move_next_list == null || move_next_list.Length == 0)
2199 foreach (MemberInfo m in move_next_list){
2200 MethodInfo mi = (MethodInfo) m;
2203 args = TypeManager.GetArgumentTypes (mi);
2204 if (args != null && args.Length == 0)
2211 // This struct records the helper methods used by the Foreach construct
2213 class ForeachHelperMethods {
2214 public EmitContext ec;
2215 public MethodInfo get_enumerator;
2216 public MethodInfo move_next;
2217 public MethodInfo get_current;
2219 public ForeachHelperMethods (EmitContext ec)
2225 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2230 if (!(m is MethodInfo))
2233 if (m.Name != "GetEnumerator")
2236 MethodInfo mi = (MethodInfo) m;
2237 Type [] args = TypeManager.GetArgumentTypes (mi);
2239 if (args.Length != 0)
2242 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2243 EmitContext ec = hm.ec;
2246 // Check whether GetEnumerator is accessible to us
2248 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2250 Type declaring = mi.DeclaringType;
2251 if (prot == MethodAttributes.Private){
2252 if (declaring != ec.ContainerType)
2254 } else if (prot == MethodAttributes.FamANDAssem){
2255 // If from a different assembly, false
2256 if (!(mi is MethodBuilder))
2259 // Are we being invoked from the same class, or from a derived method?
2261 if (ec.ContainerType != declaring){
2262 if (!ec.ContainerType.IsSubclassOf (declaring))
2265 } else if (prot == MethodAttributes.FamORAssem){
2266 if (!(mi is MethodBuilder ||
2267 ec.ContainerType == declaring ||
2268 ec.ContainerType.IsSubclassOf (declaring)))
2270 } if (prot == MethodAttributes.Family){
2271 if (!(ec.ContainerType == declaring ||
2272 ec.ContainerType.IsSubclassOf (declaring)))
2277 // Ok, we can access it, now make sure that we can do something
2278 // with this `GetEnumerator'
2280 if (mi.ReturnType == TypeManager.ienumerator_type ||
2281 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2282 hm.move_next = TypeManager.bool_movenext_void;
2283 hm.get_current = TypeManager.object_getcurrent_void;
2288 // Ok, so they dont return an IEnumerable, we will have to
2289 // find if they support the GetEnumerator pattern.
2291 Type return_type = mi.ReturnType;
2293 hm.move_next = FetchMethodMoveNext (return_type);
2294 if (hm.move_next == null)
2296 hm.get_current = FetchMethodGetCurrent (return_type);
2297 if (hm.get_current == null)
2304 /// This filter is used to find the GetEnumerator method
2305 /// on which IEnumerator operates
2307 static MemberFilter FilterEnumerator;
2311 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2314 void error1579 (Type t)
2316 Report.Error (1579, loc,
2317 "foreach statement cannot operate on variables of type `" +
2318 t.FullName + "' because that class does not provide a " +
2319 " GetEnumerator method or it is inaccessible");
2322 static bool TryType (Type t, ForeachHelperMethods hm)
2326 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2327 BindingFlags.Public | BindingFlags.NonPublic |
2328 BindingFlags.Instance,
2329 FilterEnumerator, hm);
2331 if (mi == null || mi.Length == 0)
2334 hm.get_enumerator = (MethodInfo) mi [0];
2339 // Looks for a usable GetEnumerator in the Type, and if found returns
2340 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2342 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2344 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2346 if (TryType (t, hm))
2350 // Now try to find the method in the interfaces
2353 Type [] ifaces = t.GetInterfaces ();
2355 foreach (Type i in ifaces){
2356 if (TryType (i, hm))
2361 // Since TypeBuilder.GetInterfaces only returns the interface
2362 // types for this type, we have to keep looping, but once
2363 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2364 // done, because it returns all the types
2366 if ((t is TypeBuilder))
2376 // FIXME: possible optimization.
2377 // We might be able to avoid creating `empty' if the type is the sam
2379 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2381 ILGenerator ig = ec.ig;
2382 LocalBuilder enumerator, disposable;
2383 Expression empty = new EmptyExpression ();
2387 // FIXME: maybe we can apply the same trick we do in the
2388 // array handling to avoid creating empty and conv in some cases.
2390 // Although it is not as important in this case, as the type
2391 // will not likely be object (what the enumerator will return).
2393 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2397 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2398 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2401 // Instantiate the enumerator
2403 if (expr.Type.IsValueType){
2404 if (expr is IMemoryLocation){
2405 IMemoryLocation ml = (IMemoryLocation) expr;
2407 ml.AddressOf (ec, AddressOp.Load);
2409 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2410 " does not implement IMemoryLocation");
2411 ig.Emit (OpCodes.Call, hm.get_enumerator);
2414 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2416 ig.Emit (OpCodes.Stloc, enumerator);
2419 // Protect the code in a try/finalize block, so that
2420 // if the beast implement IDisposable, we get rid of it
2422 Label l = ig.BeginExceptionBlock ();
2423 bool old_in_try = ec.InTry;
2426 Label end_try = ig.DefineLabel ();
2428 ig.MarkLabel (ec.LoopBegin);
2429 ig.Emit (OpCodes.Ldloc, enumerator);
2430 ig.Emit (OpCodes.Callvirt, hm.move_next);
2431 ig.Emit (OpCodes.Brfalse, end_try);
2432 ig.Emit (OpCodes.Ldloc, enumerator);
2433 ig.Emit (OpCodes.Callvirt, hm.get_current);
2434 variable.EmitAssign (ec, conv);
2435 statement.Emit (ec);
2436 ig.Emit (OpCodes.Br, ec.LoopBegin);
2437 ig.MarkLabel (end_try);
2438 ec.InTry = old_in_try;
2440 // The runtime provides this for us.
2441 // ig.Emit (OpCodes.Leave, end);
2444 // Now the finally block
2446 Label end_finally = ig.DefineLabel ();
2447 bool old_in_finally = ec.InFinally;
2448 ec.InFinally = true;
2449 ig.BeginFinallyBlock ();
2451 ig.Emit (OpCodes.Ldloc, enumerator);
2452 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2453 ig.Emit (OpCodes.Stloc, disposable);
2454 ig.Emit (OpCodes.Ldloc, disposable);
2455 ig.Emit (OpCodes.Brfalse, end_finally);
2456 ig.Emit (OpCodes.Ldloc, disposable);
2457 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2458 ig.MarkLabel (end_finally);
2459 ec.InFinally = old_in_finally;
2461 // The runtime generates this anyways.
2462 // ig.Emit (OpCodes.Endfinally);
2464 ig.EndExceptionBlock ();
2466 ig.MarkLabel (ec.LoopEnd);
2471 // FIXME: possible optimization.
2472 // We might be able to avoid creating `empty' if the type is the sam
2474 bool EmitArrayForeach (EmitContext ec, Type var_type)
2476 Type array_type = expr.Type;
2477 Type element_type = array_type.GetElementType ();
2478 Expression conv = null;
2479 Expression empty = new EmptyExpression (element_type);
2481 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2485 int rank = array_type.GetArrayRank ();
2486 ILGenerator ig = ec.ig;
2488 LocalBuilder copy = ig.DeclareLocal (array_type);
2491 // Make our copy of the array
2494 ig.Emit (OpCodes.Stloc, copy);
2497 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2501 ig.Emit (OpCodes.Ldc_I4_0);
2502 ig.Emit (OpCodes.Stloc, counter);
2503 test = ig.DefineLabel ();
2504 ig.Emit (OpCodes.Br, test);
2506 loop = ig.DefineLabel ();
2507 ig.MarkLabel (loop);
2509 ig.Emit (OpCodes.Ldloc, copy);
2510 ig.Emit (OpCodes.Ldloc, counter);
2511 ArrayAccess.EmitLoadOpcode (ig, var_type);
2513 variable.EmitAssign (ec, conv);
2515 statement.Emit (ec);
2517 ig.MarkLabel (ec.LoopBegin);
2518 ig.Emit (OpCodes.Ldloc, counter);
2519 ig.Emit (OpCodes.Ldc_I4_1);
2520 ig.Emit (OpCodes.Add);
2521 ig.Emit (OpCodes.Stloc, counter);
2523 ig.MarkLabel (test);
2524 ig.Emit (OpCodes.Ldloc, counter);
2525 ig.Emit (OpCodes.Ldloc, copy);
2526 ig.Emit (OpCodes.Ldlen);
2527 ig.Emit (OpCodes.Conv_I4);
2528 ig.Emit (OpCodes.Blt, loop);
2530 LocalBuilder [] dim_len = new LocalBuilder [rank];
2531 LocalBuilder [] dim_count = new LocalBuilder [rank];
2532 Label [] loop = new Label [rank];
2533 Label [] test = new Label [rank];
2536 for (dim = 0; dim < rank; dim++){
2537 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2538 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2539 test [dim] = ig.DefineLabel ();
2540 loop [dim] = ig.DefineLabel ();
2543 for (dim = 0; dim < rank; dim++){
2544 ig.Emit (OpCodes.Ldloc, copy);
2545 IntLiteral.EmitInt (ig, dim);
2546 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2547 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2550 for (dim = 0; dim < rank; dim++){
2551 ig.Emit (OpCodes.Ldc_I4_0);
2552 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2553 ig.Emit (OpCodes.Br, test [dim]);
2554 ig.MarkLabel (loop [dim]);
2557 ig.Emit (OpCodes.Ldloc, copy);
2558 for (dim = 0; dim < rank; dim++)
2559 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2562 // FIXME: Maybe we can cache the computation of `get'?
2564 Type [] args = new Type [rank];
2567 for (int i = 0; i < rank; i++)
2568 args [i] = TypeManager.int32_type;
2570 ModuleBuilder mb = RootContext.ModuleBuilder;
2571 get = mb.GetArrayMethod (
2573 CallingConventions.HasThis| CallingConventions.Standard,
2575 ig.Emit (OpCodes.Call, get);
2576 variable.EmitAssign (ec, conv);
2577 statement.Emit (ec);
2578 ig.MarkLabel (ec.LoopBegin);
2579 for (dim = rank - 1; dim >= 0; dim--){
2580 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2581 ig.Emit (OpCodes.Ldc_I4_1);
2582 ig.Emit (OpCodes.Add);
2583 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2585 ig.MarkLabel (test [dim]);
2586 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2587 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2588 ig.Emit (OpCodes.Blt, loop [dim]);
2591 ig.MarkLabel (ec.LoopEnd);
2596 public override bool Emit (EmitContext ec)
2601 expr = expr.Resolve (ec);
2605 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2606 if (var_type == null)
2610 // We need an instance variable. Not sure this is the best
2611 // way of doing this.
2613 // FIXME: When we implement propertyaccess, will those turn
2614 // out to return values in ExprClass? I think they should.
2616 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2617 expr.eclass == ExprClass.PropertyAccess)){
2618 error1579 (expr.Type);
2622 ILGenerator ig = ec.ig;
2624 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2625 bool old_inloop = ec.InLoop;
2626 ec.LoopBegin = ig.DefineLabel ();
2627 ec.LoopEnd = ig.DefineLabel ();
2630 if (expr.Type.IsArray)
2631 ret_val = EmitArrayForeach (ec, var_type);
2633 ForeachHelperMethods hm;
2635 hm = ProbeCollectionType (ec, expr.Type);
2639 ret_val = EmitCollectionForeach (ec, var_type, hm);
2642 ec.LoopBegin = old_begin;
2643 ec.LoopEnd = old_end;
2644 ec.InLoop = old_inloop;