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;
14 namespace Mono.CSharp {
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.
29 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
36 if (e.Type != TypeManager.bool_type)
37 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
42 31, "Can not convert the expression to a boolean");
50 if (u.Oper == Unary.Operator.LogicalNot){
53 u.EmitLogicalNot (ec);
62 ec.ig.Emit (OpCodes.Brfalse, l);
64 ec.ig.Emit (OpCodes.Brtrue, l);
67 ec.ig.Emit (OpCodes.Brtrue, l);
69 ec.ig.Emit (OpCodes.Brfalse, l);
77 public class EmptyStatement : Statement {
78 public override bool Emit (EmitContext ec)
84 public class If : Statement {
85 public readonly Expression Expr;
86 public readonly Statement TrueStatement;
87 public readonly Statement FalseStatement;
89 public If (Expression expr, Statement trueStatement)
92 TrueStatement = trueStatement;
95 public If (Expression expr,
96 Statement trueStatement,
97 Statement falseStatement)
100 TrueStatement = trueStatement;
101 FalseStatement = falseStatement;
104 public override bool Emit (EmitContext ec)
106 ILGenerator ig = ec.ig;
107 Label false_target = ig.DefineLabel ();
111 if (!EmitBoolExpression (ec, Expr, false_target, false))
114 is_ret = TrueStatement.Emit (ec);
116 if (FalseStatement != null){
117 bool branch_emitted = false;
119 end = ig.DefineLabel ();
121 ig.Emit (OpCodes.Br, end);
122 branch_emitted = true;
125 ig.MarkLabel (false_target);
126 is_ret = FalseStatement.Emit (ec);
131 ig.MarkLabel (false_target);
137 public class Do : Statement {
138 public readonly Expression Expr;
139 public readonly Statement EmbeddedStatement;
141 public Do (Statement statement, Expression boolExpr)
144 EmbeddedStatement = statement;
147 public override bool Emit (EmitContext ec)
149 ILGenerator ig = ec.ig;
150 Label loop = ig.DefineLabel ();
151 Label old_begin = ec.LoopBegin;
152 Label old_end = ec.LoopEnd;
153 bool old_inloop = ec.InLoop;
155 ec.LoopBegin = ig.DefineLabel ();
156 ec.LoopEnd = ig.DefineLabel ();
160 EmbeddedStatement.Emit (ec);
161 ig.MarkLabel (ec.LoopBegin);
162 EmitBoolExpression (ec, Expr, loop, true);
163 ig.MarkLabel (ec.LoopEnd);
165 ec.LoopBegin = old_begin;
166 ec.LoopEnd = old_end;
167 ec.InLoop = old_inloop;
173 public class While : Statement {
174 public readonly Expression Expr;
175 public readonly Statement Statement;
177 public While (Expression boolExpr, Statement statement)
180 Statement = statement;
183 public override bool Emit (EmitContext ec)
185 ILGenerator ig = ec.ig;
186 Label old_begin = ec.LoopBegin;
187 Label old_end = ec.LoopEnd;
188 bool old_inloop = ec.InLoop;
190 ec.LoopBegin = ig.DefineLabel ();
191 ec.LoopEnd = ig.DefineLabel ();
194 ig.MarkLabel (ec.LoopBegin);
195 EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
197 ig.Emit (OpCodes.Br, ec.LoopBegin);
198 ig.MarkLabel (ec.LoopEnd);
200 ec.LoopBegin = old_begin;
201 ec.LoopEnd = old_end;
202 ec.InLoop = old_inloop;
208 public class For : Statement {
209 public readonly Statement InitStatement;
210 public readonly Expression Test;
211 public readonly Statement Increment;
212 public readonly Statement Statement;
214 public For (Statement initStatement,
219 InitStatement = initStatement;
221 Increment = increment;
222 Statement = statement;
225 public override bool Emit (EmitContext ec)
227 ILGenerator ig = ec.ig;
228 Label old_begin = ec.LoopBegin;
229 Label old_end = ec.LoopEnd;
230 bool old_inloop = ec.InLoop;
231 Label loop = ig.DefineLabel ();
233 if (InitStatement != null)
234 if (! (InitStatement is EmptyStatement))
235 InitStatement.Emit (ec);
237 ec.LoopBegin = ig.DefineLabel ();
238 ec.LoopEnd = ig.DefineLabel ();
242 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
244 ig.MarkLabel (ec.LoopBegin);
245 if (!(Increment is EmptyStatement))
247 ig.Emit (OpCodes.Br, loop);
248 ig.MarkLabel (ec.LoopEnd);
250 ec.LoopBegin = old_begin;
251 ec.LoopEnd = old_end;
252 ec.InLoop = old_inloop;
257 public class StatementExpression : Statement {
258 public readonly ExpressionStatement Expr;
260 public StatementExpression (ExpressionStatement expr)
265 public override bool Emit (EmitContext ec)
267 ILGenerator ig = ec.ig;
270 ne = Expr.Resolve (ec);
272 if (ne is ExpressionStatement)
273 ((ExpressionStatement) ne).EmitStatement (ec);
276 ig.Emit (OpCodes.Pop);
283 public override string ToString ()
285 return "StatementExpression (" + Expr + ")";
289 public class Return : Statement {
290 public Expression Expr;
291 public readonly Location loc;
293 public Return (Expression expr, Location l)
299 public override bool Emit (EmitContext ec)
301 if (ec.ReturnType == null){
303 Report.Error (127, loc, "Return with a value not allowed here");
308 Report.Error (126, loc, "An object of type `" +
309 TypeManager.CSharpName (ec.ReturnType) + "' is " +
310 "expected for the return statement");
314 Expr = Expr.Resolve (ec);
318 if (Expr.Type != ec.ReturnType)
319 Expr = Expression.ConvertImplicitRequired (
320 ec, Expr, ec.ReturnType, loc);
328 ec.ig.Emit (OpCodes.Ret);
334 public class Goto : Statement {
338 public Goto (string label, Location l)
344 public string Target {
350 public override bool Emit (EmitContext ec)
352 Console.WriteLine ("Attempting to goto to: " + target);
358 public class Throw : Statement {
359 public readonly Expression Expr;
361 public Throw (Expression expr)
366 public override bool Emit (EmitContext ec)
368 Expression e = Expr.Resolve (ec);
374 ec.ig.Emit (OpCodes.Throw);
380 public class Break : Statement {
383 public Break (Location l)
388 public override bool Emit (EmitContext ec)
390 ILGenerator ig = ec.ig;
393 Report.Error (139, loc, "No enclosing loop to continue to");
397 ig.Emit (OpCodes.Br, ec.LoopEnd);
402 public class Continue : Statement {
405 public Continue (Location l)
410 public override bool Emit (EmitContext ec)
412 Label begin = ec.LoopBegin;
415 Report.Error (139, loc, "No enclosing loop to continue to");
419 ec.ig.Emit (OpCodes.Br, begin);
424 public class VariableInfo {
425 public readonly string Type;
426 public LocalBuilder LocalBuilder;
427 public Type VariableType;
428 public readonly Location Location;
432 public bool Assigned;
434 public VariableInfo (string type, Location l)
445 throw new Exception ("Unassigned idx for variable");
458 /// Block represents a C# block.
462 /// This class is used in a number of places: either to represent
463 /// explicit blocks that the programmer places or implicit blocks.
465 /// Implicit blocks are used as labels or to introduce variable
468 public class Block : Statement {
469 public readonly Block Parent;
470 public readonly bool Implicit;
471 public readonly string Label;
474 // The statements in this block
476 StatementCollection statements;
479 // An array of Blocks. We keep track of children just
480 // to generate the local variable declarations.
482 // Statements and child statements are handled through the
488 // Labels. (label, block) pairs.
493 // Keeps track of (name, type) pairs
498 // Maps variable names to ILGenerator.LocalBuilders
500 Hashtable local_builders;
508 public Block (Block parent)
511 parent.AddChild (this);
513 this.Parent = parent;
514 this.Implicit = false;
519 public Block (Block parent, bool implicit_block)
522 parent.AddChild (this);
524 this.Parent = parent;
525 this.Implicit = true;
529 public Block (Block parent, string labeled)
532 parent.AddChild (this);
534 this.Parent = parent;
535 this.Implicit = true;
546 void AddChild (Block b)
548 if (children == null)
549 children = new ArrayList ();
555 /// Adds a label to the current block.
559 /// false if the name already exists in this block. true
563 public bool AddLabel (string name, Block block)
566 labels = new Hashtable ();
567 if (labels.Contains (name))
570 labels.Add (name, block);
574 public bool AddVariable (string type, string name, Location l)
576 if (variables == null)
577 variables = new Hashtable ();
579 if (GetVariableType (name) != null)
582 VariableInfo vi = new VariableInfo (type, l);
584 variables.Add (name, vi);
589 public Hashtable Variables {
595 public VariableInfo GetVariableInfo (string name)
597 if (variables != null) {
599 temp = variables [name];
602 return (VariableInfo) temp;
607 return Parent.GetVariableInfo (name);
612 public string GetVariableType (string name)
614 VariableInfo vi = GetVariableInfo (name);
623 /// True if the variable named @name has been defined
626 public bool IsVariableDefined (string name)
628 return GetVariableType (name) != null;
632 /// Use to fetch the statement associated with this label
634 public Statement this [string name] {
636 return (Statement) labels [name];
641 /// A list of labels that were not used within this block
643 public string [] GetUnreferenced ()
645 // FIXME: Implement me
649 public StatementCollection Statements {
651 if (statements == null)
652 statements = new StatementCollection ();
658 public void AddStatement (Statement s)
660 if (statements == null)
661 statements = new StatementCollection ();
679 /// Emits the variable declarations and labels.
682 /// tc: is our typecontainer (to resolve type references)
683 /// ig: is the code generator:
684 /// toplevel: the toplevel block. This is used for checking
685 /// that no two labels with the same name are used.
687 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
690 // Process this block variables
692 if (variables != null){
693 local_builders = new Hashtable ();
695 foreach (DictionaryEntry de in variables){
696 string name = (string) de.Key;
697 VariableInfo vi = (VariableInfo) de.Value;
700 t = tc.LookupType (vi.Type, false);
705 vi.LocalBuilder = ig.DeclareLocal (t);
711 // Now, handle the children
713 if (children != null){
714 foreach (Block b in children)
715 b.EmitMeta (tc, ig, toplevel, count);
719 public void UsageWarning ()
723 if (variables != null){
724 foreach (DictionaryEntry de in variables){
725 VariableInfo vi = (VariableInfo) de.Value;
730 name = (string) de.Key;
734 219, vi.Location, "The variable `" + name +
735 "' is assigned but its value is never used");
738 168, vi.Location, "The variable `" +
740 "' is declared but never used");
745 if (children != null)
746 foreach (Block b in children)
750 public override bool Emit (EmitContext ec)
753 Block prev_block = ec.CurrentBlock;
755 ec.CurrentBlock = this;
756 foreach (Statement s in Statements)
757 is_ret = s.Emit (ec);
759 ec.CurrentBlock = prev_block;
764 public class SwitchLabel {
768 // if expr == null, then it is the default case.
770 public SwitchLabel (Expression expr)
775 public Expression Label {
782 public class SwitchSection {
783 // An array of SwitchLabels.
787 public SwitchSection (ArrayList labels, Block block)
789 this.labels = labels;
799 public ArrayList Labels {
806 public class Switch : Statement {
810 public Switch (Expression expr, ArrayList sections)
813 this.sections = sections;
816 public Expression Expr {
822 public ArrayList Sections {
828 public override bool Emit (EmitContext ec)
830 throw new Exception ("Unimplemented");
834 public class Lock : Statement {
835 public readonly Expression Expr;
836 public readonly Statement Statement;
839 public Lock (Expression expr, Statement stmt, Location l)
846 public override bool Emit (EmitContext ec)
848 Expression e = Expr.Resolve (ec);
854 if (type.IsValueType){
855 Report.Error (185, loc, "lock statement requires the expression to be " +
856 " a reference type (type is: `" +
857 TypeManager.CSharpName (type) + "'");
861 ILGenerator ig = ec.ig;
862 LocalBuilder temp = ig.DeclareLocal (type);
865 ig.Emit (OpCodes.Dup);
866 ig.Emit (OpCodes.Stloc, temp);
867 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
870 Label end = ig.BeginExceptionBlock ();
871 Label finish = ig.DefineLabel ();
873 // ig.Emit (OpCodes.Leave, finish);
875 ig.MarkLabel (finish);
878 ig.BeginFinallyBlock ();
879 ig.Emit (OpCodes.Ldloc, temp);
880 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
881 ig.EndExceptionBlock ();
887 public class Unchecked : Statement {
888 public readonly Block Block;
890 public Unchecked (Block b)
895 public override bool Emit (EmitContext ec)
897 bool previous_state = ec.CheckState;
900 ec.CheckState = false;
901 val = Block.Emit (ec);
902 ec.CheckState = previous_state;
908 public class Checked : Statement {
909 public readonly Block Block;
911 public Checked (Block b)
916 public override bool Emit (EmitContext ec)
918 bool previous_state = ec.CheckState;
921 ec.CheckState = true;
922 val = Block.Emit (ec);
923 ec.CheckState = previous_state;
930 public readonly string Type;
931 public readonly string Name;
932 public readonly Block Block;
934 public Catch (string type, string name, Block block)
942 public class Try : Statement {
943 public readonly Block Fini, Block;
944 public readonly ArrayList Specific;
945 public readonly Catch General;
948 // specific, general and fini might all be null.
950 public Try (Block block, ArrayList specific, Catch general, Block fini)
952 if (specific == null && general == null){
953 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
957 this.Specific = specific;
958 this.General = general;
962 public override bool Emit (EmitContext ec)
964 ILGenerator ig = ec.ig;
966 Label finish = ig.DefineLabel ();;
968 end = ig.BeginExceptionBlock ();
970 ig.Emit (OpCodes.Leave, finish);
972 foreach (Catch c in Specific){
973 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
976 if (catch_type == null)
979 ig.BeginCatchBlock (catch_type);
982 vi = c.Block.GetVariableInfo (c.Name);
984 Console.WriteLine ("This should not happen! variable does not exist in this block");
985 Environment.Exit (0);
988 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
990 ig.Emit (OpCodes.Pop);
995 if (General != null){
996 ig.BeginCatchBlock (TypeManager.object_type);
997 ig.Emit (OpCodes.Pop);
1000 ig.MarkLabel (finish);
1002 ig.BeginFinallyBlock ();
1006 ig.EndExceptionBlock ();
1012 public class Using : Statement {
1013 object expression_or_block;
1014 Statement Statement;
1017 public Using (object expression_or_block, Statement stmt, Location l)
1019 this.expression_or_block = expression_or_block;
1024 public override bool Emit (EmitContext ec)
1027 // Expressions are simple.
1028 // The problem is with blocks, blocks might contain
1029 // more than one variable, ie like this:
1031 // using (a = new X (), b = new Y ()) stmt;
1033 // which is turned into:
1034 // using (a = new X ()) using (b = new Y ()) stmt;
1036 // The trick is that the block will contain a bunch
1037 // of potential Assign expressions
1040 // We need to signal an error if a variable lacks
1041 // an assignment. (210).
1043 // This is one solution. Another is to set a flag
1044 // when we get the USING token, and have declare_local_variables
1045 // do something *different* that we can better cope with
1047 throw new Exception ("Implement me!");
1051 public class Foreach : Statement {
1053 LocalVariableReference variable;
1055 Statement statement;
1058 public Foreach (string type, LocalVariableReference var, Expression expr,
1059 Statement stmt, Location l)
1062 this.variable = var;
1068 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1073 if (!(m is MethodInfo))
1076 if (m.Name != "GetEnumerator")
1079 MethodInfo mi = (MethodInfo) m;
1081 if (mi.ReturnType != TypeManager.ienumerator_type)
1084 Type [] args = TypeManager.GetArgumentTypes (mi);
1088 if (args.Length == 0)
1095 /// This filter is used to find the GetEnumerator method
1096 /// on which IEnumerator operates
1098 static MemberFilter FilterEnumerator;
1102 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1105 void error1579 (Type t)
1107 Report.Error (1579, loc,
1108 "foreach statement cannot operate on variables of type `" +
1109 t.FullName + "' because that class does not provide a " +
1110 " GetEnumerator method or it is inaccessible");
1113 MethodInfo ProbeCollectionType (Type t)
1117 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1118 BindingFlags.Public | BindingFlags.Instance,
1119 FilterEnumerator, null);
1126 if (mi.Length == 0){
1131 return (MethodInfo) mi [0];
1135 // FIXME: possible optimization.
1136 // We might be able to avoid creating `empty' if the type is the sam
1138 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
1140 ILGenerator ig = ec.ig;
1141 LocalBuilder enumerator, disposable;
1142 Expression empty = new EmptyExpression ();
1146 // FIXME: maybe we can apply the same trick we do in the
1147 // array handling to avoid creating empty and conv in some cases.
1149 // Although it is not as important in this case, as the type
1150 // will not likely be object (what the enumerator will return).
1152 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1156 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1157 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1160 // Instantiate the enumerator
1162 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1163 Label end_try = ig.DefineLabel ();
1164 bool old_inloop = ec.InLoop;
1165 ec.LoopBegin = ig.DefineLabel ();
1166 ec.LoopEnd = ig.DefineLabel ();
1170 // FIXME: This code does not work for cases like:
1171 // foreach (int a in ValueTypeVariable){
1174 // The code should emit an ldarga instruction
1175 // for the ValueTypeVariable rather than a ldarg
1177 if (expr.Type.IsValueType){
1178 ig.Emit (OpCodes.Call, get_enum);
1181 ig.Emit (OpCodes.Callvirt, get_enum);
1183 ig.Emit (OpCodes.Stloc, enumerator);
1186 // Protect the code in a try/finalize block, so that
1187 // if the beast implement IDisposable, we get rid of it
1189 Label l = ig.BeginExceptionBlock ();
1190 ig.MarkLabel (ec.LoopBegin);
1191 ig.Emit (OpCodes.Ldloc, enumerator);
1192 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1193 ig.Emit (OpCodes.Brfalse, end_try);
1194 ig.Emit (OpCodes.Ldloc, enumerator);
1195 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1196 variable.EmitAssign (ec, conv);
1197 statement.Emit (ec);
1198 ig.Emit (OpCodes.Br, ec.LoopBegin);
1199 ig.MarkLabel (end_try);
1201 // The runtime provides this for us.
1202 // ig.Emit (OpCodes.Leave, end);
1205 // Now the finally block
1207 Label end_finally = ig.DefineLabel ();
1209 ig.BeginFinallyBlock ();
1210 ig.Emit (OpCodes.Ldloc, enumerator);
1211 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1212 ig.Emit (OpCodes.Stloc, disposable);
1213 ig.Emit (OpCodes.Ldloc, disposable);
1214 ig.Emit (OpCodes.Brfalse, end_finally);
1215 ig.Emit (OpCodes.Ldloc, disposable);
1216 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1217 ig.MarkLabel (end_finally);
1219 // The runtime generates this anyways.
1220 // ig.Emit (OpCodes.Endfinally);
1222 ig.EndExceptionBlock ();
1224 ig.MarkLabel (ec.LoopEnd);
1226 ec.LoopBegin = old_begin;
1227 ec.LoopEnd = old_end;
1228 ec.InLoop = old_inloop;
1234 // FIXME: possible optimization.
1235 // We might be able to avoid creating `empty' if the type is the sam
1237 bool EmitArrayForeach (EmitContext ec, Type var_type)
1239 Type array_type = expr.Type;
1240 Type element_type = array_type.GetElementType ();
1241 Expression conv = null;
1242 Expression empty = new EmptyExpression (var_type);
1244 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1248 int rank = array_type.GetArrayRank ();
1249 ILGenerator ig = ec.ig;
1251 LocalBuilder copy = ig.DeclareLocal (array_type);
1254 // Make our copy of the array
1257 ig.Emit (OpCodes.Stloc, copy);
1260 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
1264 ig.Emit (OpCodes.Ldc_I4_0);
1265 ig.Emit (OpCodes.Stloc, counter);
1266 test = ig.DefineLabel ();
1267 ig.Emit (OpCodes.Br, test);
1269 loop = ig.DefineLabel ();
1270 ig.MarkLabel (loop);
1272 ig.Emit (OpCodes.Ldloc, copy);
1273 ig.Emit (OpCodes.Ldloc, counter);
1274 ArrayAccess.EmitLoadOpcode (ig, var_type);
1276 variable.EmitAssign (ec, conv);
1278 statement.Emit (ec);
1280 ig.Emit (OpCodes.Ldloc, counter);
1281 ig.Emit (OpCodes.Ldc_I4_1);
1282 ig.Emit (OpCodes.Add);
1283 ig.Emit (OpCodes.Stloc, counter);
1285 ig.MarkLabel (test);
1286 ig.Emit (OpCodes.Ldloc, counter);
1287 ig.Emit (OpCodes.Ldloc, copy);
1288 ig.Emit (OpCodes.Ldlen);
1289 ig.Emit (OpCodes.Conv_I4);
1290 ig.Emit (OpCodes.Blt, loop);
1292 LocalBuilder [] dim_len = new LocalBuilder [rank];
1293 LocalBuilder [] dim_count = new LocalBuilder [rank];
1294 Label [] loop = new Label [rank];
1295 Label [] test = new Label [rank];
1298 for (dim = 0; dim < rank; dim++){
1299 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
1300 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
1301 test [dim] = ig.DefineLabel ();
1302 loop [dim] = ig.DefineLabel ();
1305 for (dim = 0; dim < rank; dim++){
1306 ig.Emit (OpCodes.Ldloc, copy);
1307 IntLiteral.EmitInt (ig, dim);
1308 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
1309 ig.Emit (OpCodes.Stloc, dim_len [dim]);
1312 for (dim = 0; dim < rank; dim++){
1313 ig.Emit (OpCodes.Ldc_I4_0);
1314 ig.Emit (OpCodes.Stloc, dim_count [dim]);
1315 ig.Emit (OpCodes.Br, test [dim]);
1316 ig.MarkLabel (loop [dim]);
1319 ig.Emit (OpCodes.Ldloc, copy);
1320 for (dim = 0; dim < rank; dim++)
1321 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1324 // FIXME: Maybe we can cache the computation of `get'?
1326 Type [] args = new Type [rank];
1329 for (int i = 0; i < rank; i++)
1330 args [i] = TypeManager.int32_type;
1332 ModuleBuilder mb = ec.TypeContainer.RootContext.ModuleBuilder;
1333 get = mb.GetArrayMethod (
1335 CallingConventions.HasThis| CallingConventions.Standard,
1337 ig.Emit (OpCodes.Call, get);
1338 variable.EmitAssign (ec, conv);
1339 statement.Emit (ec);
1340 for (dim = rank - 1; dim >= 0; dim--){
1341 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1342 ig.Emit (OpCodes.Ldc_I4_1);
1343 ig.Emit (OpCodes.Add);
1344 ig.Emit (OpCodes.Stloc, dim_count [dim]);
1346 ig.MarkLabel (test [dim]);
1347 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1348 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
1349 ig.Emit (OpCodes.Blt, loop [dim]);
1356 public override bool Emit (EmitContext ec)
1360 expr = expr.Resolve (ec);
1364 var_type = ec.TypeContainer.LookupType (type, false);
1365 if (var_type == null)
1369 // We need an instance variable. Not sure this is the best
1370 // way of doing this.
1372 // FIXME: When we implement propertyaccess, will those turn
1373 // out to return values in ExprClass? I think they should.
1375 if (!(expr.ExprClass == ExprClass.Variable || expr.ExprClass == ExprClass.Value)){
1376 error1579 (expr.Type);
1380 if (expr.Type.IsArray)
1381 return EmitArrayForeach (ec, var_type);
1383 MethodInfo get_enum;
1385 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
1388 return EmitCollectionForeach (ec, var_type, get_enum);