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;
16 using System.Collections;
18 public abstract class Statement {
21 // Return value indicates whether the last instruction
22 // was a return instruction
24 public abstract bool Emit (EmitContext ec);
27 // Emits a bool expression. Generates a jump to the `t' label if true
28 // if defined, or to `f' if defined.
30 // t and f can not be both non-null
32 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
39 if (e.Type != TypeManager.bool_type)
40 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
45 31, "Can not convert the expression to a boolean");
53 if (u.Oper == Unary.Operator.LogicalNot){
56 u.EmitLogicalNot (ec);
65 ec.ig.Emit (OpCodes.Brfalse, l);
67 ec.ig.Emit (OpCodes.Brtrue, l);
70 ec.ig.Emit (OpCodes.Brtrue, l);
72 ec.ig.Emit (OpCodes.Brfalse, l);
80 public class EmptyStatement : Statement {
81 public override bool Emit (EmitContext ec)
87 public class If : Statement {
88 public readonly Expression Expr;
89 public readonly Statement TrueStatement;
90 public readonly Statement FalseStatement;
92 public If (Expression expr, Statement trueStatement)
95 TrueStatement = trueStatement;
98 public If (Expression expr,
99 Statement trueStatement,
100 Statement falseStatement)
103 TrueStatement = trueStatement;
104 FalseStatement = falseStatement;
107 public override bool Emit (EmitContext ec)
109 ILGenerator ig = ec.ig;
110 Label false_target = ig.DefineLabel ();
114 if (!EmitBoolExpression (ec, Expr, false_target, false))
117 is_ret = TrueStatement.Emit (ec);
119 if (FalseStatement != null){
120 bool branch_emitted = false;
122 end = ig.DefineLabel ();
124 ig.Emit (OpCodes.Br, end);
125 branch_emitted = true;
128 ig.MarkLabel (false_target);
129 is_ret = FalseStatement.Emit (ec);
134 ig.MarkLabel (false_target);
140 public class Do : Statement {
141 public readonly Expression Expr;
142 public readonly Statement EmbeddedStatement;
144 public Do (Statement statement, Expression boolExpr)
147 EmbeddedStatement = statement;
150 public override bool Emit (EmitContext ec)
152 ILGenerator ig = ec.ig;
153 Label loop = ig.DefineLabel ();
154 Label old_begin = ec.LoopBegin;
155 Label old_end = ec.LoopEnd;
156 bool old_inloop = ec.InLoop;
158 ec.LoopBegin = ig.DefineLabel ();
159 ec.LoopEnd = ig.DefineLabel ();
163 EmbeddedStatement.Emit (ec);
164 ig.MarkLabel (ec.LoopBegin);
165 EmitBoolExpression (ec, Expr, loop, true);
166 ig.MarkLabel (ec.LoopEnd);
168 ec.LoopBegin = old_begin;
169 ec.LoopEnd = old_end;
170 ec.InLoop = old_inloop;
176 public class While : Statement {
177 public readonly Expression Expr;
178 public readonly Statement Statement;
180 public While (Expression boolExpr, Statement statement)
183 Statement = statement;
186 public override bool Emit (EmitContext ec)
188 ILGenerator ig = ec.ig;
189 Label old_begin = ec.LoopBegin;
190 Label old_end = ec.LoopEnd;
191 bool old_inloop = ec.InLoop;
193 ec.LoopBegin = ig.DefineLabel ();
194 ec.LoopEnd = ig.DefineLabel ();
197 ig.MarkLabel (ec.LoopBegin);
198 EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
200 ig.Emit (OpCodes.Br, ec.LoopBegin);
201 ig.MarkLabel (ec.LoopEnd);
203 ec.LoopBegin = old_begin;
204 ec.LoopEnd = old_end;
205 ec.InLoop = old_inloop;
211 public class For : Statement {
212 public readonly Statement InitStatement;
213 public readonly Expression Test;
214 public readonly Statement Increment;
215 public readonly Statement Statement;
217 public For (Statement initStatement,
222 InitStatement = initStatement;
224 Increment = increment;
225 Statement = statement;
228 public override bool Emit (EmitContext ec)
230 ILGenerator ig = ec.ig;
231 Label old_begin = ec.LoopBegin;
232 Label old_end = ec.LoopEnd;
233 bool old_inloop = ec.InLoop;
234 Label loop = ig.DefineLabel ();
236 if (InitStatement != null)
237 if (! (InitStatement is EmptyStatement))
238 InitStatement.Emit (ec);
240 ec.LoopBegin = ig.DefineLabel ();
241 ec.LoopEnd = ig.DefineLabel ();
245 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
247 ig.MarkLabel (ec.LoopBegin);
248 if (!(Increment is EmptyStatement))
250 ig.Emit (OpCodes.Br, loop);
251 ig.MarkLabel (ec.LoopEnd);
253 ec.LoopBegin = old_begin;
254 ec.LoopEnd = old_end;
255 ec.InLoop = old_inloop;
260 public class StatementExpression : Statement {
261 public readonly ExpressionStatement Expr;
263 public StatementExpression (ExpressionStatement expr)
268 public override bool Emit (EmitContext ec)
270 ILGenerator ig = ec.ig;
273 ne = Expr.Resolve (ec);
275 if (ne is ExpressionStatement)
276 ((ExpressionStatement) ne).EmitStatement (ec);
279 ig.Emit (OpCodes.Pop);
287 public class Return : Statement {
288 public Expression Expr;
289 public readonly Location loc;
291 public Return (Expression expr, Location l)
297 public override bool Emit (EmitContext ec)
299 if (ec.ReturnType == null){
301 Report.Error (127, loc, "Return with a value not allowed here");
306 Report.Error (126, loc, "An object of type `" +
307 TypeManager.CSharpName (ec.ReturnType) + "' is " +
308 "expected for the return statement");
312 Expr = Expr.Resolve (ec);
316 if (Expr.Type != ec.ReturnType)
317 Expr = Expression.ConvertImplicitRequired (
318 ec, Expr, ec.ReturnType, loc);
326 ec.ig.Emit (OpCodes.Ret);
332 public class Goto : Statement {
336 public Goto (string label, Location l)
342 public string Target {
348 public override bool Emit (EmitContext ec)
350 Console.WriteLine ("Attempting to goto to: " + target);
356 public class Throw : Statement {
357 public readonly Expression Expr;
359 public Throw (Expression expr)
364 public override bool Emit (EmitContext ec)
366 Expression e = Expr.Resolve (ec);
372 ec.ig.Emit (OpCodes.Throw);
378 public class Break : Statement {
381 public Break (Location l)
386 public override bool Emit (EmitContext ec)
388 ILGenerator ig = ec.ig;
391 Report.Error (139, loc, "No enclosing loop to continue to");
395 ig.Emit (OpCodes.Br, ec.LoopEnd);
400 public class Continue : Statement {
403 public Continue (Location l)
408 public override bool Emit (EmitContext ec)
410 Label begin = ec.LoopBegin;
413 Report.Error (139, loc, "No enclosing loop to continue to");
417 ec.ig.Emit (OpCodes.Br, begin);
422 public class VariableInfo {
423 public readonly string Type;
424 public LocalBuilder LocalBuilder;
425 public Type VariableType;
426 public readonly Location Location;
430 public bool Assigned;
432 public VariableInfo (string type, Location l)
443 throw new Exception ("Unassigned idx for variable");
456 // Used for Label management
459 public class Block : Statement {
460 public readonly Block Parent;
461 public readonly bool Implicit;
462 public readonly string Label;
465 // The statements in this block
467 StatementCollection statements;
470 // An array of Blocks. We keep track of children just
471 // to generate the local variable declarations.
473 // Statements and child statements are handled through the
479 // Labels. (label, block) pairs.
484 // Keeps track of (name, type) pairs
489 // Maps variable names to ILGenerator.LocalBuilders
491 Hashtable local_builders;
495 public Block (Block parent)
498 parent.AddChild (this);
500 this.Parent = parent;
501 this.Implicit = false;
504 public Block (Block parent, bool implicit_block)
507 parent.AddChild (this);
509 this.Parent = parent;
510 this.Implicit = true;
513 public Block (Block parent, string labeled)
516 parent.AddChild (this);
518 this.Parent = parent;
519 this.Implicit = true;
523 public void AddChild (Block b)
525 if (children == null)
526 children = new ArrayList ();
532 // Adds a label to the current block.
536 // false if the name already exists in this block. true
540 public bool AddLabel (string name, Block block)
543 labels = new Hashtable ();
544 if (labels.Contains (name))
547 labels.Add (name, block);
551 public bool AddVariable (string type, string name, Location l)
553 if (variables == null)
554 variables = new Hashtable ();
556 if (GetVariableType (name) != null)
559 VariableInfo vi = new VariableInfo (type, l);
561 variables.Add (name, vi);
565 public Hashtable Variables {
571 public VariableInfo GetVariableInfo (string name)
573 if (variables != null) {
575 temp = variables [name];
578 return (VariableInfo) temp;
583 return Parent.GetVariableInfo (name);
588 public string GetVariableType (string name)
590 VariableInfo vi = GetVariableInfo (name);
599 // True if the variable named @name has been defined
602 public bool IsVariableDefined (string name)
604 return GetVariableType (name) != null;
608 // Use to fetch the statement associated with this label
610 public Statement this [string name] {
612 return (Statement) labels [name];
617 // A list of labels that were not used within this block
619 public string [] GetUnreferenced ()
621 // FIXME: Implement me
625 public StatementCollection Statements {
627 if (statements == null)
628 statements = new StatementCollection ();
634 public void AddStatement (Statement s)
636 if (statements == null)
637 statements = new StatementCollection ();
655 // Creates a compiler-internal identifier, this is
656 // used to create temporary variables that should not
657 // be seen by the application
659 int internal_id_serial;
660 public string MakeInternalID () {
661 string ret = internal_id_serial.ToString ();
663 internal_id_serial++;
668 // Emits the variable declarations and labels.
671 // tc: is our typecontainer (to resolve type references)
672 // ig: is the code generator:
673 // toplevel: the toplevel block. This is used for checking
674 // that no two labels with the same name are used.
676 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
679 // Process this block variables
681 if (variables != null){
682 local_builders = new Hashtable ();
684 foreach (DictionaryEntry de in variables){
685 string name = (string) de.Key;
686 VariableInfo vi = (VariableInfo) de.Value;
689 t = tc.LookupType (vi.Type, false);
694 vi.LocalBuilder = ig.DeclareLocal (t);
700 // Now, handle the children
702 if (children != null){
703 foreach (Block b in children)
704 b.EmitMeta (tc, ig, toplevel, count);
708 public void UsageWarning ()
712 if (variables != null){
713 foreach (DictionaryEntry de in variables){
714 VariableInfo vi = (VariableInfo) de.Value;
719 name = (string) de.Key;
723 219, vi.Location, "The variable `" + name +
724 "' is assigned but its value is never used");
727 168, vi.Location, "The variable `" +
729 "' is declared but never used");
734 if (children != null)
735 foreach (Block b in children)
739 public override bool Emit (EmitContext ec)
742 Block prev_block = ec.CurrentBlock;
744 ec.CurrentBlock = this;
745 foreach (Statement s in Statements)
746 is_ret = s.Emit (ec);
748 ec.CurrentBlock = prev_block;
753 public class SwitchLabel {
757 // if expr == null, then it is the default case.
759 public SwitchLabel (Expression expr)
764 public Expression Label {
771 public class SwitchSection {
772 // An array of SwitchLabels.
776 public SwitchSection (ArrayList labels, Block block)
778 this.labels = labels;
788 public ArrayList Labels {
795 public class Switch : Statement {
799 public Switch (Expression expr, ArrayList sections)
802 this.sections = sections;
805 public Expression Expr {
811 public ArrayList Sections {
817 public override bool Emit (EmitContext ec)
819 throw new Exception ("Unimplemented");
823 public class Lock : Statement {
824 public readonly Expression Expr;
825 public readonly Statement Statement;
828 public Lock (Expression expr, Statement stmt, Location l)
835 public override bool Emit (EmitContext ec)
837 Expression e = Expr.Resolve (ec);
843 if (type.IsValueType){
844 Report.Error (185, loc, "lock statement requires the expression to be " +
845 " a reference type (type is: `" +
846 TypeManager.CSharpName (type) + "'");
850 ILGenerator ig = ec.ig;
851 LocalBuilder temp = ig.DeclareLocal (type);
854 ig.Emit (OpCodes.Dup);
855 ig.Emit (OpCodes.Stloc, temp);
856 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
859 Label end = ig.BeginExceptionBlock ();
860 Label finish = ig.DefineLabel ();
862 // ig.Emit (OpCodes.Leave, finish);
864 ig.MarkLabel (finish);
867 ig.BeginFinallyBlock ();
868 ig.Emit (OpCodes.Ldloc, temp);
869 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
870 ig.EndExceptionBlock ();
876 public class Unchecked : Statement {
877 public readonly Block Block;
879 public Unchecked (Block b)
884 public override bool Emit (EmitContext ec)
886 bool previous_state = ec.CheckState;
889 ec.CheckState = false;
890 val = Block.Emit (ec);
891 ec.CheckState = previous_state;
897 public class Checked : Statement {
898 public readonly Block Block;
900 public Checked (Block b)
905 public override bool Emit (EmitContext ec)
907 bool previous_state = ec.CheckState;
910 ec.CheckState = true;
911 val = Block.Emit (ec);
912 ec.CheckState = previous_state;
919 public readonly string Type;
920 public readonly string Name;
921 public readonly Block Block;
923 public Catch (string type, string name, Block block)
931 public class Try : Statement {
932 public readonly Block Fini, Block;
933 public readonly ArrayList Specific;
934 public readonly Catch General;
937 // specific, general and fini might all be null.
939 public Try (Block block, ArrayList specific, Catch general, Block fini)
941 if (specific == null && general == null){
942 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
946 this.Specific = specific;
947 this.General = general;
951 public override bool Emit (EmitContext ec)
953 ILGenerator ig = ec.ig;
955 Label finish = ig.DefineLabel ();;
957 end = ig.BeginExceptionBlock ();
959 ig.Emit (OpCodes.Leave, finish);
961 foreach (Catch c in Specific){
962 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
965 if (catch_type == null)
968 ig.BeginCatchBlock (catch_type);
971 vi = c.Block.GetVariableInfo (c.Name);
973 Console.WriteLine ("This should not happen! variable does not exist in this block");
974 Environment.Exit (0);
977 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
979 ig.Emit (OpCodes.Pop);
984 if (General != null){
985 ig.BeginCatchBlock (TypeManager.object_type);
986 ig.Emit (OpCodes.Pop);
989 ig.MarkLabel (finish);
991 ig.BeginFinallyBlock ();
995 ig.EndExceptionBlock ();
1001 public class Using : Statement {
1002 object expression_or_block;
1003 Statement Statement;
1006 public Using (object expression_or_block, Statement stmt, Location l)
1008 this.expression_or_block = expression_or_block;
1013 public override bool Emit (EmitContext ec)
1016 // Expressions are simple.
1017 // The problem is with blocks, blocks might contain
1018 // more than one variable, ie like this:
1020 // using (a = new X (), b = new Y ()) stmt;
1022 // which is turned into:
1023 // using (a = new X ()) using (b = new Y ()) stmt;
1025 // The trick is that the block will contain a bunch
1026 // of potential Assign expressions
1029 // We need to signal an error if a variable lacks
1030 // an assignment. (210).
1032 // This is one solution. Another is to set a flag
1033 // when we get the USING token, and have declare_local_variables
1034 // do something *different* that we can better cope with
1036 throw new Exception ("Implement me!");
1040 public class Foreach : Statement {
1042 LocalVariableReference variable;
1044 Statement statement;
1047 public Foreach (string type, LocalVariableReference var, Expression expr,
1048 Statement stmt, Location l)
1051 this.variable = var;
1057 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1062 if (!(m is MethodInfo))
1065 if (m.Name != "GetEnumerator")
1068 MethodInfo mi = (MethodInfo) m;
1070 if (mi.ReturnType != TypeManager.ienumerator_type)
1073 Type [] args = TypeManager.GetArgumentTypes (mi);
1077 if (args.Length == 0)
1084 // This filter is used to find the GetEnumerator method
1085 // on which IEnumerator operates
1087 static MemberFilter FilterEnumerator;
1091 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1094 void error1579 (Type t)
1096 Report.Error (1579, loc,
1097 "foreach statement cannot operate on variables of type `" +
1098 t.FullName + "' because that class does not provide a " +
1099 " GetEnumerator method or it is inaccessible");
1102 MethodInfo ProbeCollectionType (Type t)
1106 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1107 BindingFlags.Public,
1108 FilterEnumerator, null);
1115 if (mi.Length == 0){
1120 return (MethodInfo) mi [0];
1124 // FIXME: possible optimization.
1125 // We might be able to avoid creating `empty' if the type is the sam
1127 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
1129 ILGenerator ig = ec.ig;
1130 LocalBuilder enumerator, disposable;
1131 Expression empty = new EmptyExpression ();
1135 // FIXME: maybe we can apply the same trick we do in the
1136 // array handling to avoid creating empty and conv in some cases.
1138 // Although it is not as important in this case, as the type
1139 // will not likely be object (what the enumerator will return).
1141 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1145 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1146 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1149 // Instantiate the enumerator
1151 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1152 Label end_try = ig.DefineLabel ();
1153 bool old_inloop = ec.InLoop;
1154 ec.LoopBegin = ig.DefineLabel ();
1155 ec.LoopEnd = ig.DefineLabel ();
1159 // FIXME: This code does not work for cases like:
1160 // foreach (int a in ValueTypeVariable){
1163 // The code should emit an ldarga instruction
1164 // for the ValueTypeVariable rather than a ldarg
1166 if (expr.Type.IsValueType){
1167 ig.Emit (OpCodes.Call, get_enum);
1170 ig.Emit (OpCodes.Callvirt, get_enum);
1172 ig.Emit (OpCodes.Stloc, enumerator);
1175 // Protect the code in a try/finalize block, so that
1176 // if the beast implement IDisposable, we get rid of it
1178 Label l = ig.BeginExceptionBlock ();
1179 ig.MarkLabel (ec.LoopBegin);
1180 ig.Emit (OpCodes.Ldloc, enumerator);
1181 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1182 ig.Emit (OpCodes.Brfalse, end_try);
1183 ig.Emit (OpCodes.Ldloc, enumerator);
1184 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1185 variable.EmitAssign (ec, conv);
1186 statement.Emit (ec);
1187 ig.Emit (OpCodes.Br, ec.LoopBegin);
1188 ig.MarkLabel (end_try);
1190 // The runtime provides this for us.
1191 // ig.Emit (OpCodes.Leave, end);
1194 // Now the finally block
1196 Label end_finally = ig.DefineLabel ();
1198 ig.BeginFinallyBlock ();
1199 ig.Emit (OpCodes.Ldloc, enumerator);
1200 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1201 ig.Emit (OpCodes.Stloc, disposable);
1202 ig.Emit (OpCodes.Ldloc, disposable);
1203 ig.Emit (OpCodes.Brfalse, end_finally);
1204 ig.Emit (OpCodes.Ldloc, disposable);
1205 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1206 ig.MarkLabel (end_finally);
1208 // The runtime generates this anyways.
1209 // ig.Emit (OpCodes.Endfinally);
1211 ig.EndExceptionBlock ();
1213 ig.MarkLabel (ec.LoopEnd);
1215 ec.LoopBegin = old_begin;
1216 ec.LoopEnd = old_end;
1217 ec.InLoop = old_inloop;
1223 // FIXME: possible optimization.
1224 // We might be able to avoid creating `empty' if the type is the sam
1226 bool EmitArrayForeach (EmitContext ec, Type var_type)
1228 Type array_type = expr.Type;
1229 Type element_type = array_type.GetElementType ();
1230 Expression conv = null;
1231 Expression empty = new EmptyExpression (var_type);
1233 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1237 int rank = array_type.GetArrayRank ();
1238 ILGenerator ig = ec.ig;
1240 LocalBuilder copy = ig.DeclareLocal (array_type);
1243 // Make our copy of the array
1246 ig.Emit (OpCodes.Stloc, copy);
1249 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
1253 ig.Emit (OpCodes.Ldc_I4_0);
1254 ig.Emit (OpCodes.Stloc, counter);
1255 test = ig.DefineLabel ();
1256 ig.Emit (OpCodes.Br, test);
1258 loop = ig.DefineLabel ();
1259 ig.MarkLabel (loop);
1261 ig.Emit (OpCodes.Ldloc, copy);
1262 ig.Emit (OpCodes.Ldloc, counter);
1263 ArrayAccess.EmitLoadOpcode (ig, var_type);
1265 variable.EmitAssign (ec, conv);
1267 statement.Emit (ec);
1269 ig.Emit (OpCodes.Ldloc, counter);
1270 ig.Emit (OpCodes.Ldc_I4_1);
1271 ig.Emit (OpCodes.Add);
1272 ig.Emit (OpCodes.Stloc, counter);
1274 ig.MarkLabel (test);
1275 ig.Emit (OpCodes.Ldloc, counter);
1276 ig.Emit (OpCodes.Ldloc, copy);
1277 ig.Emit (OpCodes.Ldlen);
1278 ig.Emit (OpCodes.Conv_I4);
1279 ig.Emit (OpCodes.Blt, loop);
1281 LocalBuilder [] dim_len = new LocalBuilder [rank];
1282 LocalBuilder [] dim_count = new LocalBuilder [rank];
1283 Label [] loop = new Label [rank];
1284 Label [] test = new Label [rank];
1287 for (dim = 0; dim < rank; dim++){
1288 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
1289 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
1290 test [dim] = ig.DefineLabel ();
1291 loop [dim] = ig.DefineLabel ();
1294 for (dim = 0; dim < rank; dim++){
1295 ig.Emit (OpCodes.Ldloc, copy);
1296 IntLiteral.EmitInt (ig, dim);
1297 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
1298 ig.Emit (OpCodes.Stloc, dim_len [dim]);
1301 for (dim = 0; dim < rank; dim++){
1302 ig.Emit (OpCodes.Ldc_I4_0);
1303 ig.Emit (OpCodes.Stloc, dim_count [dim]);
1304 ig.Emit (OpCodes.Br, test [dim]);
1305 ig.MarkLabel (loop [dim]);
1308 ig.Emit (OpCodes.Ldloc, copy);
1309 for (dim = 0; dim < rank; dim++)
1310 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1313 // FIXME: Maybe we can cache the computation of `get'?
1315 Type [] args = new Type [rank];
1318 for (int i = 0; i < rank; i++)
1319 args [i] = TypeManager.int32_type;
1321 ModuleBuilder mb = ec.TypeContainer.RootContext.ModuleBuilder;
1322 get = mb.GetArrayMethod (
1324 CallingConventions.HasThis| CallingConventions.Standard,
1326 ig.Emit (OpCodes.Call, get);
1327 variable.EmitAssign (ec, conv);
1328 statement.Emit (ec);
1329 for (dim = rank - 1; dim >= 0; dim--){
1330 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1331 ig.Emit (OpCodes.Ldc_I4_1);
1332 ig.Emit (OpCodes.Add);
1333 ig.Emit (OpCodes.Stloc, dim_count [dim]);
1335 ig.MarkLabel (test [dim]);
1336 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1337 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
1338 ig.Emit (OpCodes.Blt, loop [dim]);
1345 public override bool Emit (EmitContext ec)
1349 expr = expr.Resolve (ec);
1353 var_type = ec.TypeContainer.LookupType (type, false);
1354 if (var_type == null)
1358 // We need an instance variable. Not sure this is the best
1359 // way of doing this.
1361 // FIXME: When we implement propertyaccess, will those turn
1362 // out to return values in ExprClass? I think they should.
1364 if (!(expr.ExprClass == ExprClass.Variable || expr.ExprClass == ExprClass.Value)){
1365 error1579 (expr.Type);
1369 if (expr.Type.IsArray)
1370 return EmitArrayForeach (ec, var_type);
1372 MethodInfo get_enum;
1374 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
1377 return EmitCollectionForeach (ec, var_type, get_enum);