2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001, 2002 Ximian, Inc.
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
15 namespace Mono.CSharp {
17 using System.Collections;
19 public abstract class Statement {
23 /// Resolves the statement, true means that all sub-statements
26 public virtual bool Resolve (EmitContext ec)
32 /// Return value indicates whether all code paths emitted return.
34 public abstract bool Emit (EmitContext ec);
36 public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
42 if (e.Type != TypeManager.bool_type){
43 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
49 31, loc, "Can not convert the expression to a boolean");
52 if (CodeGen.SymbolWriter != null)
59 /// Emits a bool expression.
61 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
62 Label target, bool isTrue)
64 ILGenerator ig = ec.ig;
67 if (bool_expr is Unary){
68 Unary u = (Unary) bool_expr;
70 if (u.Oper == Unary.Operator.LogicalNot){
73 u.EmitLogicalNot (ec);
82 ig.Emit (OpCodes.Brfalse, target);
84 ig.Emit (OpCodes.Brtrue, target);
87 ig.Emit (OpCodes.Brtrue, target);
89 ig.Emit (OpCodes.Brfalse, target);
93 public static void Warning_DeadCodeFound (Location loc)
95 Report.Warning (162, loc, "Unreachable code detected");
99 public class EmptyStatement : Statement {
100 public override bool Resolve (EmitContext ec)
105 public override bool Emit (EmitContext ec)
111 public class If : Statement {
112 public readonly Expression Expr;
113 public readonly Statement TrueStatement;
114 public readonly Statement FalseStatement;
116 public If (Expression expr, Statement trueStatement, Location l)
119 TrueStatement = trueStatement;
123 public If (Expression expr,
124 Statement trueStatement,
125 Statement falseStatement,
129 TrueStatement = trueStatement;
130 FalseStatement = falseStatement;
134 public override bool Resolve (EmitContext ec)
136 if (TrueStatement.Resolve (ec)){
137 if (FalseStatement != null){
138 if (FalseStatement.Resolve (ec))
147 public override bool Emit (EmitContext ec)
149 ILGenerator ig = ec.ig;
150 Label false_target = ig.DefineLabel ();
152 bool is_true_ret, is_false_ret;
153 Expression bool_expr;
155 bool_expr = ResolveBoolean (ec, Expr, loc);
156 if (bool_expr == null)
160 // Dead code elimination
162 if (bool_expr is BoolConstant){
163 bool take = ((BoolConstant) bool_expr).Value;
166 if (FalseStatement != null){
167 Warning_DeadCodeFound (FalseStatement.loc);
169 return TrueStatement.Emit (ec);
171 Warning_DeadCodeFound (TrueStatement.loc);
172 if (FalseStatement != null)
173 return FalseStatement.Emit (ec);
177 EmitBoolExpression (ec, bool_expr, false_target, false);
179 is_true_ret = TrueStatement.Emit (ec);
180 is_false_ret = is_true_ret;
182 if (FalseStatement != null){
183 bool branch_emitted = false;
185 end = ig.DefineLabel ();
187 ig.Emit (OpCodes.Br, end);
188 branch_emitted = true;
191 ig.MarkLabel (false_target);
192 is_false_ret = FalseStatement.Emit (ec);
197 ig.MarkLabel (false_target);
198 is_false_ret = false;
201 return is_true_ret && is_false_ret;
205 public class Do : Statement {
206 public readonly Expression Expr;
207 public readonly Statement EmbeddedStatement;
209 public Do (Statement statement, Expression boolExpr, Location l)
212 EmbeddedStatement = statement;
216 public override bool Resolve (EmitContext ec)
218 return EmbeddedStatement.Resolve (ec);
221 public override bool Emit (EmitContext ec)
223 ILGenerator ig = ec.ig;
224 Label loop = ig.DefineLabel ();
225 Label old_begin = ec.LoopBegin;
226 Label old_end = ec.LoopEnd;
227 bool old_inloop = ec.InLoop;
228 Expression bool_expr;
230 bool_expr = ResolveBoolean (ec, Expr, loc);
231 if (bool_expr == null)
234 ec.LoopBegin = ig.DefineLabel ();
235 ec.LoopEnd = ig.DefineLabel ();
239 EmbeddedStatement.Emit (ec);
240 ig.MarkLabel (ec.LoopBegin);
243 // Dead code elimination
245 if (bool_expr is BoolConstant){
246 bool res = ((BoolConstant) bool_expr).Value;
249 ec.ig.Emit (OpCodes.Br, loop);
251 EmitBoolExpression (ec, bool_expr, loop, true);
253 ig.MarkLabel (ec.LoopEnd);
255 ec.LoopBegin = old_begin;
256 ec.LoopEnd = old_end;
257 ec.InLoop = old_inloop;
260 // Inform whether we are infinite or not
262 if (bool_expr is BoolConstant){
263 BoolConstant bc = (BoolConstant) bool_expr;
265 if (bc.Value == true)
273 public class While : Statement {
274 public readonly Expression Expr;
275 public readonly Statement Statement;
277 public While (Expression boolExpr, Statement statement, Location l)
280 Statement = statement;
284 public override bool Resolve (EmitContext ec)
286 return Statement.Resolve (ec);
289 public override bool Emit (EmitContext ec)
291 ILGenerator ig = ec.ig;
292 Label old_begin = ec.LoopBegin;
293 Label old_end = ec.LoopEnd;
294 bool old_inloop = ec.InLoop;
295 Label while_loop = ig.DefineLabel ();
298 Expression bool_expr = ResolveBoolean (ec, Expr, loc);
300 if (bool_expr == null)
303 ec.LoopBegin = ig.DefineLabel ();
304 ec.LoopEnd = ig.DefineLabel ();
307 ig.Emit (OpCodes.Br, ec.LoopBegin);
308 ig.MarkLabel (while_loop);
311 // Inform whether we are infinite or not
313 if (bool_expr is BoolConstant){
314 BoolConstant bc = (BoolConstant) bool_expr;
316 ig.MarkLabel (ec.LoopBegin);
317 if (bc.Value == false){
318 Warning_DeadCodeFound (Statement.loc);
322 ig.Emit (OpCodes.Br, ec.LoopBegin);
325 // Inform that we are infinite (ie, `we return')
329 ig.MarkLabel (ec.LoopEnd);
333 ig.MarkLabel (ec.LoopBegin);
335 EmitBoolExpression (ec, bool_expr, while_loop, true);
336 ig.MarkLabel (ec.LoopEnd);
341 ec.LoopBegin = old_begin;
342 ec.LoopEnd = old_end;
343 ec.InLoop = old_inloop;
349 public class For : Statement {
350 public readonly Statement InitStatement;
351 public readonly Expression Test;
352 public readonly Statement Increment;
353 public readonly Statement Statement;
355 public For (Statement initStatement,
361 InitStatement = initStatement;
363 Increment = increment;
364 Statement = statement;
368 public override bool Resolve (EmitContext ec)
373 if (InitStatement != null)
374 init = InitStatement.Resolve (ec);
376 if (Increment != null)
377 incr = Increment.Resolve (ec);
379 return Statement.Resolve (ec) && init && incr;
382 public override bool Emit (EmitContext ec)
384 ILGenerator ig = ec.ig;
385 Label old_begin = ec.LoopBegin;
386 Label old_end = ec.LoopEnd;
387 bool old_inloop = ec.InLoop;
388 Label loop = ig.DefineLabel ();
389 Expression bool_expr = null;
391 if (InitStatement != null)
392 if (! (InitStatement is EmptyStatement))
393 InitStatement.Emit (ec);
395 ec.LoopBegin = ig.DefineLabel ();
396 ec.LoopEnd = ig.DefineLabel ();
402 // If test is null, there is no test, and we are just
406 bool_expr = ResolveBoolean (ec, Test, loc);
407 if (bool_expr == null)
410 EmitBoolExpression (ec, bool_expr, ec.LoopEnd, false);
414 ig.MarkLabel (ec.LoopBegin);
415 if (!(Increment is EmptyStatement))
417 ig.Emit (OpCodes.Br, loop);
418 ig.MarkLabel (ec.LoopEnd);
420 ec.LoopBegin = old_begin;
421 ec.LoopEnd = old_end;
422 ec.InLoop = old_inloop;
425 // Inform whether we are infinite or not
428 if (bool_expr is BoolConstant){
429 BoolConstant bc = (BoolConstant) bool_expr;
440 public class StatementExpression : Statement {
441 public readonly ExpressionStatement Expr;
443 public StatementExpression (ExpressionStatement expr, Location l)
449 public override bool Resolve (EmitContext ec)
454 public override bool Emit (EmitContext ec)
456 ILGenerator ig = ec.ig;
459 ne = Expr.Resolve (ec);
461 if (ne is ExpressionStatement)
462 ((ExpressionStatement) ne).EmitStatement (ec);
465 ig.Emit (OpCodes.Pop);
472 public override string ToString ()
474 return "StatementExpression (" + Expr + ")";
479 /// Implements the return statement
481 public class Return : Statement {
482 public Expression Expr;
484 public Return (Expression expr, Location l)
490 public override bool Resolve (EmitContext ec)
495 public override bool Emit (EmitContext ec)
498 Report.Error (157,loc,"Control can not leave the body of the finally block");
502 if (ec.ReturnType == null){
504 Report.Error (127, loc, "Return with a value not allowed here");
509 Report.Error (126, loc, "An object of type `" +
510 TypeManager.CSharpName (ec.ReturnType) + "' is " +
511 "expected for the return statement");
515 Expr = Expr.Resolve (ec);
519 if (Expr.Type != ec.ReturnType)
520 Expr = Expression.ConvertImplicitRequired (
521 ec, Expr, ec.ReturnType, loc);
528 if (ec.InTry || ec.InCatch)
529 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
532 if (ec.InTry || ec.InCatch)
533 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
535 ec.ig.Emit (OpCodes.Ret);
541 public class Goto : Statement {
545 public override bool Resolve (EmitContext ec)
550 public Goto (Block parent_block, string label, Location l)
552 block = parent_block;
557 public string Target {
563 public override bool Emit (EmitContext ec)
565 LabeledStatement label = block.LookupLabel (target);
569 // Maybe we should catch this before?
573 "No such label `" + target + "' in this scope");
576 Label l = label.LabelTarget (ec);
577 ec.ig.Emit (OpCodes.Br, l);
583 public class LabeledStatement : Statement {
588 public LabeledStatement (string label_name)
590 this.label_name = label_name;
593 public Label LabelTarget (EmitContext ec)
597 label = ec.ig.DefineLabel ();
603 public override bool Emit (EmitContext ec)
606 ec.ig.MarkLabel (label);
614 /// `goto default' statement
616 public class GotoDefault : Statement {
618 public GotoDefault (Location l)
623 public override bool Emit (EmitContext ec)
625 if (ec.Switch == null){
626 Report.Error (153, loc, "goto default is only valid in a switch statement");
630 if (!ec.Switch.GotDefault){
631 Report.Error (159, loc, "No default target on switch statement");
634 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
640 /// `goto case' statement
642 public class GotoCase : Statement {
645 public GotoCase (Expression e, Location l)
651 public override bool Emit (EmitContext ec)
653 if (ec.Switch == null){
654 Report.Error (153, loc, "goto case is only valid in a switch statement");
658 expr = expr.Resolve (ec);
662 if (!(expr is Constant)){
663 Report.Error (159, loc, "Target expression for goto case is not constant");
667 object val = Expression.ConvertIntLiteral (
668 (Constant) expr, ec.Switch.SwitchType, loc);
673 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
678 "No such label 'case " + val + "': for the goto case");
681 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
686 public class Throw : Statement {
687 public readonly Expression Expr;
689 public Throw (Expression expr, Location l)
695 public override bool Emit (EmitContext ec)
699 ec.ig.Emit (OpCodes.Rethrow);
703 "A throw statement with no argument is only " +
704 "allowed in a catch clause");
709 Expression e = Expr.Resolve (ec);
716 ec.ig.Emit (OpCodes.Throw);
722 public class Break : Statement {
724 public Break (Location l)
729 public override bool Emit (EmitContext ec)
731 ILGenerator ig = ec.ig;
733 if (ec.InLoop == false && ec.Switch == null){
734 Report.Error (139, loc, "No enclosing loop or switch to continue to");
738 ig.Emit (OpCodes.Br, ec.LoopEnd);
743 public class Continue : Statement {
745 public Continue (Location l)
750 public override bool Emit (EmitContext ec)
752 Label begin = ec.LoopBegin;
755 Report.Error (139, loc, "No enclosing loop to continue to");
760 // UGH: Non trivial. This Br might cross a try/catch boundary
764 // try { ... } catch { continue; }
768 // try {} catch { while () { continue; }}
770 ec.ig.Emit (OpCodes.Br, begin);
775 public class VariableInfo {
776 public readonly string Type;
777 public LocalBuilder LocalBuilder;
778 public Type VariableType;
779 public readonly Location Location;
782 public bool Assigned;
783 public bool ReadOnly;
785 public VariableInfo (string type, Location l)
792 public void MakePinned ()
794 TypeManager.MakePinned (LocalBuilder);
799 /// Block represents a C# block.
803 /// This class is used in a number of places: either to represent
804 /// explicit blocks that the programmer places or implicit blocks.
806 /// Implicit blocks are used as labels or to introduce variable
809 public class Block : Statement {
810 public readonly Block Parent;
811 public readonly bool Implicit;
812 public readonly Location StartLocation;
813 public Location EndLocation;
816 // The statements in this block
818 ArrayList statements;
821 // An array of Blocks. We keep track of children just
822 // to generate the local variable declarations.
824 // Statements and child statements are handled through the
830 // Labels. (label, block) pairs.
835 // Keeps track of (name, type) pairs
840 // Keeps track of constants
844 // Maps variable names to ILGenerator.LocalBuilders
846 Hashtable local_builders;
854 public Block (Block parent)
855 : this (parent, false, Location.Null, Location.Null)
858 public Block (Block parent, bool implicit_block)
859 : this (parent, implicit_block, Location.Null, Location.Null)
862 public Block (Block parent, Location start, Location end)
863 : this (parent, false, start, end)
866 public Block (Block parent, bool implicit_block, Location start, Location end)
869 parent.AddChild (this);
871 this.Parent = parent;
872 this.Implicit = implicit_block;
873 this.StartLocation = start;
874 this.EndLocation = end;
877 statements = new ArrayList ();
886 void AddChild (Block b)
888 if (children == null)
889 children = new ArrayList ();
894 public void SetEndLocation (Location loc)
900 /// Adds a label to the current block.
904 /// false if the name already exists in this block. true
908 public bool AddLabel (string name, LabeledStatement target)
911 labels = new Hashtable ();
912 if (labels.Contains (name))
915 labels.Add (name, target);
919 public LabeledStatement LookupLabel (string name)
922 if (labels.Contains (name))
923 return ((LabeledStatement) labels [name]);
927 return Parent.LookupLabel (name);
932 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
934 if (variables == null)
935 variables = new Hashtable ();
937 if (GetVariableType (name) != null)
942 Parameter p = pars.GetParameterByName (name, out idx);
947 VariableInfo vi = new VariableInfo (type, l);
949 variables.Add (name, vi);
951 // Console.WriteLine ("Adding {0} to {1}", name, ID);
955 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
957 if (AddVariable (type, name, pars, l) == null)
960 if (constants == null)
961 constants = new Hashtable ();
963 constants.Add (name, value);
967 public Hashtable Variables {
973 public VariableInfo GetVariableInfo (string name)
975 if (variables != null) {
977 temp = variables [name];
980 return (VariableInfo) temp;
985 return Parent.GetVariableInfo (name);
990 public string GetVariableType (string name)
992 VariableInfo vi = GetVariableInfo (name);
1000 public Expression GetConstantExpression (string name)
1002 if (constants != null) {
1004 temp = constants [name];
1007 return (Expression) temp;
1011 return Parent.GetConstantExpression (name);
1017 /// True if the variable named @name has been defined
1020 public bool IsVariableDefined (string name)
1022 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1023 if (variables != null) {
1024 if (variables.Contains (name))
1029 return Parent.IsVariableDefined (name);
1035 /// True if the variable named @name is a constant
1037 public bool IsConstant (string name)
1039 Expression e = null;
1041 e = GetConstantExpression (name);
1047 /// Use to fetch the statement associated with this label
1049 public Statement this [string name] {
1051 return (Statement) labels [name];
1056 /// A list of labels that were not used within this block
1058 public string [] GetUnreferenced ()
1060 // FIXME: Implement me
1064 public void AddStatement (Statement s)
1082 /// Emits the variable declarations and labels.
1085 /// tc: is our typecontainer (to resolve type references)
1086 /// ig: is the code generator:
1087 /// toplevel: the toplevel block. This is used for checking
1088 /// that no two labels with the same name are used.
1090 public void EmitMeta (EmitContext ec, Block toplevel)
1092 DeclSpace ds = ec.DeclSpace;
1093 ILGenerator ig = ec.ig;
1096 // Process this block variables
1098 if (variables != null){
1099 local_builders = new Hashtable ();
1101 foreach (DictionaryEntry de in variables){
1102 string name = (string) de.Key;
1103 VariableInfo vi = (VariableInfo) de.Value;
1106 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
1110 vi.VariableType = t;
1111 vi.LocalBuilder = ig.DeclareLocal (t);
1113 if (CodeGen.SymbolWriter != null)
1114 vi.LocalBuilder.SetLocalSymInfo (name);
1116 if (constants == null)
1119 Expression cv = (Expression) constants [name];
1123 Expression e = cv.Resolve (ec);
1127 if (!(e is Constant)){
1128 Report.Error (133, vi.Location,
1129 "The expression being assigned to `" +
1130 name + "' must be constant (" + e + ")");
1134 constants.Remove (name);
1135 constants.Add (name, e);
1140 // Now, handle the children
1142 if (children != null){
1143 foreach (Block b in children)
1144 b.EmitMeta (ec, toplevel);
1148 public void UsageWarning ()
1152 if (variables != null){
1153 foreach (DictionaryEntry de in variables){
1154 VariableInfo vi = (VariableInfo) de.Value;
1159 name = (string) de.Key;
1163 219, vi.Location, "The variable `" + name +
1164 "' is assigned but its value is never used");
1167 168, vi.Location, "The variable `" +
1169 "' is declared but never used");
1174 if (children != null)
1175 foreach (Block b in children)
1179 public override bool Resolve (EmitContext ec)
1181 foreach (Statement s in statements){
1182 if (s.Resolve (ec) == false)
1189 public override bool Emit (EmitContext ec)
1191 bool is_ret = false;
1192 Block prev_block = ec.CurrentBlock;
1194 ec.CurrentBlock = this;
1196 if (CodeGen.SymbolWriter != null) {
1197 ec.Mark (StartLocation);
1199 foreach (Statement s in statements) {
1202 is_ret = s.Emit (ec);
1205 ec.Mark (EndLocation);
1207 foreach (Statement s in statements)
1208 is_ret = s.Emit (ec);
1211 ec.CurrentBlock = prev_block;
1216 public class SwitchLabel {
1219 public Location loc;
1220 public Label ILLabel;
1221 public Label ILLabelCode;
1224 // if expr == null, then it is the default case.
1226 public SwitchLabel (Expression expr, Location l)
1232 public Expression Label {
1238 public object Converted {
1245 // Resolves the expression, reduces it to a literal if possible
1246 // and then converts it to the requested type.
1248 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1250 ILLabel = ec.ig.DefineLabel ();
1251 ILLabelCode = ec.ig.DefineLabel ();
1256 Expression e = label.Resolve (ec);
1261 if (!(e is Constant)){
1262 Console.WriteLine ("Value is: " + label);
1263 Report.Error (150, loc, "A constant value is expected");
1267 if (e is StringConstant || e is NullLiteral){
1268 if (required_type == TypeManager.string_type){
1270 ILLabel = ec.ig.DefineLabel ();
1275 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1276 if (converted == null)
1283 public class SwitchSection {
1284 // An array of SwitchLabels.
1285 public readonly ArrayList Labels;
1286 public readonly Block Block;
1288 public SwitchSection (ArrayList labels, Block block)
1295 public class Switch : Statement {
1296 public readonly ArrayList Sections;
1297 public Expression Expr;
1300 /// Maps constants whose type type SwitchType to their SwitchLabels.
1302 public Hashtable Elements;
1305 /// The governing switch type
1307 public Type SwitchType;
1313 Label default_target;
1316 // The types allowed to be implicitly cast from
1317 // on the governing type
1319 static Type [] allowed_types;
1321 public Switch (Expression e, ArrayList sects, Location l)
1328 public bool GotDefault {
1334 public Label DefaultTarget {
1336 return default_target;
1341 // Determines the governing type for a switch. The returned
1342 // expression might be the expression from the switch, or an
1343 // expression that includes any potential conversions to the
1344 // integral types or to string.
1346 Expression SwitchGoverningType (EmitContext ec, Type t)
1348 if (t == TypeManager.int32_type ||
1349 t == TypeManager.uint32_type ||
1350 t == TypeManager.char_type ||
1351 t == TypeManager.byte_type ||
1352 t == TypeManager.sbyte_type ||
1353 t == TypeManager.ushort_type ||
1354 t == TypeManager.short_type ||
1355 t == TypeManager.uint64_type ||
1356 t == TypeManager.int64_type ||
1357 t == TypeManager.string_type ||
1358 t == TypeManager.bool_type ||
1359 t.IsSubclassOf (TypeManager.enum_type))
1362 if (allowed_types == null){
1363 allowed_types = new Type [] {
1364 TypeManager.sbyte_type,
1365 TypeManager.byte_type,
1366 TypeManager.short_type,
1367 TypeManager.ushort_type,
1368 TypeManager.int32_type,
1369 TypeManager.uint32_type,
1370 TypeManager.int64_type,
1371 TypeManager.uint64_type,
1372 TypeManager.char_type,
1373 TypeManager.bool_type,
1374 TypeManager.string_type
1379 // Try to find a *user* defined implicit conversion.
1381 // If there is no implicit conversion, or if there are multiple
1382 // conversions, we have to report an error
1384 Expression converted = null;
1385 foreach (Type tt in allowed_types){
1388 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1392 if (converted != null){
1393 Report.Error (-12, loc, "More than one conversion to an integral " +
1394 " type exists for type `" +
1395 TypeManager.CSharpName (Expr.Type)+"'");
1403 void error152 (string n)
1406 152, "The label `" + n + ":' " +
1407 "is already present on this switch statement");
1411 // Performs the basic sanity checks on the switch statement
1412 // (looks for duplicate keys and non-constant expressions).
1414 // It also returns a hashtable with the keys that we will later
1415 // use to compute the switch tables
1417 bool CheckSwitch (EmitContext ec)
1421 Elements = new Hashtable ();
1423 got_default = false;
1425 if (TypeManager.IsEnumType (SwitchType)){
1426 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1428 compare_type = SwitchType;
1430 foreach (SwitchSection ss in Sections){
1431 foreach (SwitchLabel sl in ss.Labels){
1432 if (!sl.ResolveAndReduce (ec, SwitchType)){
1437 if (sl.Label == null){
1439 error152 ("default");
1446 object key = sl.Converted;
1448 if (key is Constant)
1449 key = ((Constant) key).GetValue ();
1452 key = NullLiteral.Null;
1454 string lname = null;
1455 if (compare_type == TypeManager.uint64_type){
1456 ulong v = (ulong) key;
1458 if (Elements.Contains (v))
1459 lname = v.ToString ();
1461 Elements.Add (v, sl);
1462 } else if (compare_type == TypeManager.int64_type){
1463 long v = (long) key;
1465 if (Elements.Contains (v))
1466 lname = v.ToString ();
1468 Elements.Add (v, sl);
1469 } else if (compare_type == TypeManager.uint32_type){
1470 uint v = (uint) key;
1472 if (Elements.Contains (v))
1473 lname = v.ToString ();
1475 Elements.Add (v, sl);
1476 } else if (compare_type == TypeManager.char_type){
1477 char v = (char) key;
1479 if (Elements.Contains (v))
1480 lname = v.ToString ();
1482 Elements.Add (v, sl);
1483 } else if (compare_type == TypeManager.byte_type){
1484 byte v = (byte) key;
1486 if (Elements.Contains (v))
1487 lname = v.ToString ();
1489 Elements.Add (v, sl);
1490 } else if (compare_type == TypeManager.sbyte_type){
1491 sbyte v = (sbyte) key;
1493 if (Elements.Contains (v))
1494 lname = v.ToString ();
1496 Elements.Add (v, sl);
1497 } else if (compare_type == TypeManager.short_type){
1498 short v = (short) key;
1500 if (Elements.Contains (v))
1501 lname = v.ToString ();
1503 Elements.Add (v, sl);
1504 } else if (compare_type == TypeManager.ushort_type){
1505 ushort v = (ushort) key;
1507 if (Elements.Contains (v))
1508 lname = v.ToString ();
1510 Elements.Add (v, sl);
1511 } else if (compare_type == TypeManager.string_type){
1512 if (key is NullLiteral){
1513 if (Elements.Contains (NullLiteral.Null))
1516 Elements.Add (NullLiteral.Null, null);
1518 string s = (string) key;
1520 if (Elements.Contains (s))
1523 Elements.Add (s, sl);
1525 } else if (compare_type == TypeManager.int32_type) {
1528 if (Elements.Contains (v))
1529 lname = v.ToString ();
1531 Elements.Add (v, sl);
1532 } else if (compare_type == TypeManager.bool_type) {
1533 bool v = (bool) key;
1535 if (Elements.Contains (v))
1536 lname = v.ToString ();
1538 Elements.Add (v, sl);
1542 throw new Exception ("Unknown switch type!" +
1543 SwitchType + " " + compare_type);
1547 error152 ("case + " + lname);
1558 void EmitObjectInteger (ILGenerator ig, object k)
1561 IntConstant.EmitInt (ig, (int) k);
1562 else if (k is Constant) {
1563 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1566 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1569 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1571 IntConstant.EmitInt (ig, (int) (long) k);
1572 ig.Emit (OpCodes.Conv_I8);
1575 LongConstant.EmitLong (ig, (long) k);
1577 else if (k is ulong)
1579 if ((ulong) k < (1L<<32))
1581 IntConstant.EmitInt (ig, (int) (long) k);
1582 ig.Emit (OpCodes.Conv_U8);
1586 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1590 IntConstant.EmitInt (ig, (int) ((char) k));
1591 else if (k is sbyte)
1592 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1594 IntConstant.EmitInt (ig, (int) ((byte) k));
1595 else if (k is short)
1596 IntConstant.EmitInt (ig, (int) ((short) k));
1597 else if (k is ushort)
1598 IntConstant.EmitInt (ig, (int) ((ushort) k));
1600 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1602 throw new Exception ("Unhandled case");
1605 // structure used to hold blocks of keys while calculating table switch
1606 class KeyBlock : IComparable
1608 public KeyBlock (long _nFirst)
1610 nFirst = nLast = _nFirst;
1614 public ArrayList rgKeys = null;
1617 get { return (int) (nLast - nFirst + 1); }
1619 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1621 return kbLast.nLast - kbFirst.nFirst + 1;
1623 public int CompareTo (object obj)
1625 KeyBlock kb = (KeyBlock) obj;
1626 int nLength = Length;
1627 int nLengthOther = kb.Length;
1628 if (nLengthOther == nLength)
1629 return (int) (kb.nFirst - nFirst);
1630 return nLength - nLengthOther;
1635 /// This method emits code for a lookup-based switch statement (non-string)
1636 /// Basically it groups the cases into blocks that are at least half full,
1637 /// and then spits out individual lookup opcodes for each block.
1638 /// It emits the longest blocks first, and short blocks are just
1639 /// handled with direct compares.
1641 /// <param name="ec"></param>
1642 /// <param name="val"></param>
1643 /// <returns></returns>
1644 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1646 int cElements = Elements.Count;
1647 object [] rgKeys = new object [cElements];
1648 Elements.Keys.CopyTo (rgKeys, 0);
1649 Array.Sort (rgKeys);
1651 // initialize the block list with one element per key
1652 ArrayList rgKeyBlocks = new ArrayList ();
1653 foreach (object key in rgKeys)
1654 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1657 // iteratively merge the blocks while they are at least half full
1658 // there's probably a really cool way to do this with a tree...
1659 while (rgKeyBlocks.Count > 1)
1661 ArrayList rgKeyBlocksNew = new ArrayList ();
1662 kbCurr = (KeyBlock) rgKeyBlocks [0];
1663 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1665 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1666 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1669 kbCurr.nLast = kb.nLast;
1673 // start a new block
1674 rgKeyBlocksNew.Add (kbCurr);
1678 rgKeyBlocksNew.Add (kbCurr);
1679 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1681 rgKeyBlocks = rgKeyBlocksNew;
1684 // initialize the key lists
1685 foreach (KeyBlock kb in rgKeyBlocks)
1686 kb.rgKeys = new ArrayList ();
1688 // fill the key lists
1690 kbCurr = (KeyBlock) rgKeyBlocks [0];
1691 foreach (object key in rgKeys)
1693 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1695 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1696 kbCurr.rgKeys.Add (key);
1699 // sort the blocks so we can tackle the largest ones first
1700 rgKeyBlocks.Sort ();
1702 // okay now we can start...
1703 ILGenerator ig = ec.ig;
1704 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1705 Label lblDefault = new Label ();
1706 Type typeKeys = rgKeys [0].GetType (); // used for conversions
1708 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1710 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1711 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1714 foreach (object key in kb.rgKeys)
1716 ig.Emit (OpCodes.Ldloc, val);
1717 EmitObjectInteger (ig, key);
1718 SwitchLabel sl = (SwitchLabel) Elements [key];
1719 ig.Emit (OpCodes.Beq, sl.ILLabel);
1724 // TODO: if all the keys in the block are the same and there are
1725 // no gaps/defaults then just use a range-check.
1726 if (SwitchType == TypeManager.int64_type ||
1727 SwitchType == TypeManager.uint64_type)
1729 // TODO: optimize constant/I4 cases
1731 // check block range (could be > 2^31)
1732 ig.Emit (OpCodes.Ldloc, val);
1733 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1734 ig.Emit (OpCodes.Blt, lblDefault);
1735 ig.Emit (OpCodes.Ldloc, val);
1736 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1737 ig.Emit (OpCodes.Bgt, lblDefault);
1740 ig.Emit (OpCodes.Ldloc, val);
1743 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1744 ig.Emit (OpCodes.Sub);
1746 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1751 ig.Emit (OpCodes.Ldloc, val);
1752 int nFirst = (int) kb.nFirst;
1755 IntConstant.EmitInt (ig, nFirst);
1756 ig.Emit (OpCodes.Sub);
1758 else if (nFirst < 0)
1760 IntConstant.EmitInt (ig, -nFirst);
1761 ig.Emit (OpCodes.Add);
1765 // first, build the list of labels for the switch
1767 int cJumps = kb.Length;
1768 Label [] rgLabels = new Label [cJumps];
1769 for (int iJump = 0; iJump < cJumps; iJump++)
1771 object key = kb.rgKeys [iKey];
1772 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1774 SwitchLabel sl = (SwitchLabel) Elements [key];
1775 rgLabels [iJump] = sl.ILLabel;
1779 rgLabels [iJump] = lblDefault;
1781 // emit the switch opcode
1782 ig.Emit (OpCodes.Switch, rgLabels);
1785 // mark the default for this block
1787 ig.MarkLabel (lblDefault);
1790 // TODO: find the default case and emit it here,
1791 // to prevent having to do the following jump.
1792 // make sure to mark other labels in the default section
1794 // the last default just goes to the end
1795 ig.Emit (OpCodes.Br, lblDefault);
1797 // now emit the code for the sections
1798 bool fFoundDefault = false;
1799 bool fAllReturn = true;
1800 foreach (SwitchSection ss in Sections)
1802 foreach (SwitchLabel sl in ss.Labels)
1804 ig.MarkLabel (sl.ILLabel);
1805 ig.MarkLabel (sl.ILLabelCode);
1806 if (sl.Label == null)
1808 ig.MarkLabel (lblDefault);
1809 fFoundDefault = true;
1812 fAllReturn &= ss.Block.Emit (ec);
1813 //ig.Emit (OpCodes.Br, lblEnd);
1817 ig.MarkLabel (lblDefault);
1818 ig.MarkLabel (lblEnd);
1823 // This simple emit switch works, but does not take advantage of the
1825 // TODO: remove non-string logic from here
1826 // TODO: binary search strings?
1828 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1830 ILGenerator ig = ec.ig;
1831 Label end_of_switch = ig.DefineLabel ();
1832 Label next_test = ig.DefineLabel ();
1833 Label null_target = ig.DefineLabel ();
1834 bool default_found = false;
1835 bool first_test = true;
1836 bool pending_goto_end = false;
1837 bool all_return = true;
1838 bool is_string = false;
1842 // Special processing for strings: we cant compare
1845 if (SwitchType == TypeManager.string_type){
1846 ig.Emit (OpCodes.Ldloc, val);
1849 if (Elements.Contains (NullLiteral.Null)){
1850 ig.Emit (OpCodes.Brfalse, null_target);
1852 ig.Emit (OpCodes.Brfalse, default_target);
1854 ig.Emit (OpCodes.Ldloc, val);
1855 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1856 ig.Emit (OpCodes.Stloc, val);
1859 SwitchSection last_section;
1860 last_section = (SwitchSection) Sections [Sections.Count-1];
1862 foreach (SwitchSection ss in Sections){
1863 Label sec_begin = ig.DefineLabel ();
1865 if (pending_goto_end)
1866 ig.Emit (OpCodes.Br, end_of_switch);
1868 int label_count = ss.Labels.Count;
1870 foreach (SwitchLabel sl in ss.Labels){
1871 ig.MarkLabel (sl.ILLabel);
1874 ig.MarkLabel (next_test);
1875 next_test = ig.DefineLabel ();
1878 // If we are the default target
1880 if (sl.Label == null){
1881 ig.MarkLabel (default_target);
1882 default_found = true;
1884 object lit = sl.Converted;
1886 if (lit is NullLiteral){
1888 if (label_count == 1)
1889 ig.Emit (OpCodes.Br, next_test);
1894 StringConstant str = (StringConstant) lit;
1896 ig.Emit (OpCodes.Ldloc, val);
1897 ig.Emit (OpCodes.Ldstr, str.Value);
1898 if (label_count == 1)
1899 ig.Emit (OpCodes.Bne_Un, next_test);
1901 ig.Emit (OpCodes.Beq, sec_begin);
1903 ig.Emit (OpCodes.Ldloc, val);
1904 EmitObjectInteger (ig, lit);
1905 ig.Emit (OpCodes.Ceq);
1906 if (label_count == 1)
1907 ig.Emit (OpCodes.Brfalse, next_test);
1909 ig.Emit (OpCodes.Brtrue, sec_begin);
1913 if (label_count != 1 && ss != last_section)
1914 ig.Emit (OpCodes.Br, next_test);
1917 ig.MarkLabel (null_target);
1918 ig.MarkLabel (sec_begin);
1919 foreach (SwitchLabel sl in ss.Labels)
\r
1920 ig.MarkLabel (sl.ILLabelCode);
1921 if (ss.Block.Emit (ec))
1922 pending_goto_end = false;
1925 pending_goto_end = true;
1929 if (!default_found){
1930 ig.MarkLabel (default_target);
1933 ig.MarkLabel (next_test);
1934 ig.MarkLabel (end_of_switch);
1939 public override bool Resolve (EmitContext ec)
1941 foreach (SwitchSection ss in Sections){
1942 if (ss.Block.Resolve (ec) != true)
1949 public override bool Emit (EmitContext ec)
1951 Expr = Expr.Resolve (ec);
1955 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1956 if (new_expr == null){
1957 Report.Error (151, loc, "An integer type or string was expected for switch");
1962 SwitchType = new_expr.Type;
1964 if (!CheckSwitch (ec))
1967 // Store variable for comparission purposes
1968 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1970 ec.ig.Emit (OpCodes.Stloc, value);
1972 ILGenerator ig = ec.ig;
1974 default_target = ig.DefineLabel ();
1977 // Setup the codegen context
1979 Label old_end = ec.LoopEnd;
1980 Switch old_switch = ec.Switch;
1982 ec.LoopEnd = ig.DefineLabel ();
1987 if (SwitchType == TypeManager.string_type)
1988 all_return = SimpleSwitchEmit (ec, value);
1990 all_return = TableSwitchEmit (ec, value);
1992 // Restore context state.
1993 ig.MarkLabel (ec.LoopEnd);
1996 // Restore the previous context
1998 ec.LoopEnd = old_end;
1999 ec.Switch = old_switch;
2005 public class Lock : Statement {
2006 public readonly Expression Expr;
2007 public readonly Statement Statement;
2009 public Lock (Expression expr, Statement stmt, Location l)
2016 public override bool Resolve (EmitContext ec)
2018 return Statement.Resolve (ec);
2021 public override bool Emit (EmitContext ec)
2023 Expression e = Expr.Resolve (ec);
2029 if (type.IsValueType){
2030 Report.Error (185, loc, "lock statement requires the expression to be " +
2031 " a reference type (type is: `" +
2032 TypeManager.CSharpName (type) + "'");
2036 ILGenerator ig = ec.ig;
2037 LocalBuilder temp = ig.DeclareLocal (type);
2040 ig.Emit (OpCodes.Dup);
2041 ig.Emit (OpCodes.Stloc, temp);
2042 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2045 Label end = ig.BeginExceptionBlock ();
2046 bool old_in_try = ec.InTry;
2048 Label finish = ig.DefineLabel ();
2049 Statement.Emit (ec);
2050 ec.InTry = old_in_try;
2051 // ig.Emit (OpCodes.Leave, finish);
2053 ig.MarkLabel (finish);
2056 ig.BeginFinallyBlock ();
2057 ig.Emit (OpCodes.Ldloc, temp);
2058 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2059 ig.EndExceptionBlock ();
2065 public class Unchecked : Statement {
2066 public readonly Block Block;
2068 public Unchecked (Block b)
2073 public override bool Resolve (EmitContext ec)
2075 return Block.Resolve (ec);
2078 public override bool Emit (EmitContext ec)
2080 bool previous_state = ec.CheckState;
2081 bool previous_state_const = ec.ConstantCheckState;
2084 ec.CheckState = false;
2085 ec.ConstantCheckState = false;
2086 val = Block.Emit (ec);
2087 ec.CheckState = previous_state;
2088 ec.ConstantCheckState = previous_state_const;
2094 public class Checked : Statement {
2095 public readonly Block Block;
2097 public Checked (Block b)
2102 public override bool Resolve (EmitContext ec)
2104 return Block.Resolve (ec);
2107 public override bool Emit (EmitContext ec)
2109 bool previous_state = ec.CheckState;
2110 bool previous_state_const = ec.ConstantCheckState;
2113 ec.CheckState = true;
2114 ec.ConstantCheckState = true;
2115 val = Block.Emit (ec);
2116 ec.CheckState = previous_state;
2117 ec.ConstantCheckState = previous_state_const;
2123 public class Unsafe : Statement {
2124 public readonly Block Block;
2126 public Unsafe (Block b)
2131 public override bool Resolve (EmitContext ec)
2133 return Block.Resolve (ec);
2136 public override bool Emit (EmitContext ec)
2138 bool previous_state = ec.InUnsafe;
2142 val = Block.Emit (ec);
2143 ec.InUnsafe = previous_state;
2152 public class Fixed : Statement {
2154 ArrayList declarators;
2155 Statement statement;
2157 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
2160 declarators = decls;
2165 public override bool Resolve (EmitContext ec)
2167 return statement.Resolve (ec);
2170 public override bool Emit (EmitContext ec)
2172 ILGenerator ig = ec.ig;
2175 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2179 foreach (Pair p in declarators){
2180 VariableInfo vi = (VariableInfo) p.First;
2181 Expression e = (Expression) p.Second;
2184 // The rules for the possible declarators are pretty wise,
2185 // but the production on the grammar is more concise.
2187 // So we have to enforce these rules here.
2189 // We do not resolve before doing the case 1 test,
2190 // because the grammar is explicit in that the token &
2191 // is present, so we need to test for this particular case.
2195 // Case 1: & object.
2197 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2198 Expression child = ((Unary) e).Expr;
2201 if (child is ParameterReference || child is LocalVariableReference){
2204 "No need to use fixed statement for parameters or " +
2205 "local variable declarations (address is already " +
2214 child = ((Unary) e).Expr;
2216 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2220 // Store pointer in pinned location
2223 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2225 statement.Emit (ec);
2227 // Clear the pinned variable.
2228 ig.Emit (OpCodes.Ldc_I4_0);
2229 ig.Emit (OpCodes.Conv_U);
2230 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2242 if (e.Type.IsArray){
2243 Type array_type = e.Type.GetElementType ();
2247 // Provided that array_type is unmanaged,
2249 if (!TypeManager.VerifyUnManaged (array_type, loc))
2253 // and T* is implicitly convertible to the
2254 // pointer type given in the fixed statement.
2256 ArrayPtr array_ptr = new ArrayPtr (e);
2258 Expression converted = Expression.ConvertImplicitRequired (
2259 ec, array_ptr, vi.VariableType, loc);
2260 if (converted == null)
2264 // Store pointer in pinned location
2266 converted.Emit (ec);
2268 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2270 statement.Emit (ec);
2272 // Clear the pinned variable.
2273 ig.Emit (OpCodes.Ldc_I4_0);
2274 ig.Emit (OpCodes.Conv_U);
2275 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2283 if (e.Type == TypeManager.string_type){
2284 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2285 TypeManager.MakePinned (pinned_string);
2288 ig.Emit (OpCodes.Stloc, pinned_string);
2290 Expression sptr = new StringPtr (pinned_string);
2291 Expression converted = Expression.ConvertImplicitRequired (
2292 ec, sptr, vi.VariableType, loc);
2294 if (converted == null)
2297 converted.Emit (ec);
2298 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2300 statement.Emit (ec);
2302 // Clear the pinned variable
2303 ig.Emit (OpCodes.Ldnull);
2304 ig.Emit (OpCodes.Stloc, pinned_string);
2312 public class Catch {
2313 public readonly string Type;
2314 public readonly string Name;
2315 public readonly Block Block;
2316 public readonly Location Location;
2318 public Catch (string type, string name, Block block, Location l)
2327 public class Try : Statement {
2328 public readonly Block Fini, Block;
2329 public readonly ArrayList Specific;
2330 public readonly Catch General;
2333 // specific, general and fini might all be null.
2335 public Try (Block block, ArrayList specific, Catch general, Block fini)
2337 if (specific == null && general == null){
2338 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2342 this.Specific = specific;
2343 this.General = general;
2347 public override bool Resolve (EmitContext ec)
2351 if (General != null)
2352 if (!General.Block.Resolve (ec))
2355 foreach (Catch c in Specific){
2356 if (!c.Block.Resolve (ec))
2360 if (!Block.Resolve (ec))
2364 if (!Fini.Resolve (ec))
2370 public override bool Emit (EmitContext ec)
2372 ILGenerator ig = ec.ig;
2374 Label finish = ig.DefineLabel ();;
2377 end = ig.BeginExceptionBlock ();
2378 bool old_in_try = ec.InTry;
2380 returns = Block.Emit (ec);
2381 ec.InTry = old_in_try;
2384 // System.Reflection.Emit provides this automatically:
2385 // ig.Emit (OpCodes.Leave, finish);
2387 bool old_in_catch = ec.InCatch;
2389 DeclSpace ds = ec.DeclSpace;
2391 foreach (Catch c in Specific){
2392 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2395 if (catch_type == null)
2398 ig.BeginCatchBlock (catch_type);
2400 if (c.Name != null){
2401 vi = c.Block.GetVariableInfo (c.Name);
2403 Console.WriteLine ("This should not happen! variable does not exist in this block");
2404 Environment.Exit (0);
2407 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2409 ig.Emit (OpCodes.Pop);
2411 if (!c.Block.Emit (ec))
2415 if (General != null){
2416 ig.BeginCatchBlock (TypeManager.object_type);
2417 ig.Emit (OpCodes.Pop);
2418 if (!General.Block.Emit (ec))
2421 ec.InCatch = old_in_catch;
2423 ig.MarkLabel (finish);
2425 ig.BeginFinallyBlock ();
2426 bool old_in_finally = ec.InFinally;
2427 ec.InFinally = true;
2429 ec.InFinally = old_in_finally;
2432 ig.EndExceptionBlock ();
2435 // FIXME: Is this correct?
2436 // Replace with `returns' and check test-18, maybe we can
2437 // perform an optimization here.
2444 // FIXME: We still do not support the expression variant of the using
2447 public class Using : Statement {
2448 object expression_or_block;
2449 Statement Statement;
2451 public Using (object expression_or_block, Statement stmt, Location l)
2453 this.expression_or_block = expression_or_block;
2459 // Emits the code for the case of using using a local variable declaration.
2461 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2463 ILGenerator ig = ec.ig;
2464 Expression [] converted_vars;
2465 bool need_conv = false;
2466 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2473 // The type must be an IDisposable or an implicit conversion
2476 converted_vars = new Expression [var_list.Count];
2477 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2478 foreach (DictionaryEntry e in var_list){
2479 Expression var = (Expression) e.Key;
2481 var = var.Resolve (ec);
2485 converted_vars [i] = Expression.ConvertImplicit (
2486 ec, var, TypeManager.idisposable_type, loc);
2488 if (converted_vars [i] == null)
2496 bool old_in_try = ec.InTry;
2498 foreach (DictionaryEntry e in var_list){
2499 LocalVariableReference var = (LocalVariableReference) e.Key;
2500 Expression expr = (Expression) e.Value;
2503 a = new Assign (var, expr, loc);
2506 converted_vars [i] = var;
2510 ((ExpressionStatement) a).EmitStatement (ec);
2512 ig.BeginExceptionBlock ();
2515 Statement.Emit (ec);
2516 ec.InTry = old_in_try;
2518 bool old_in_finally = ec.InFinally;
2519 ec.InFinally = true;
2520 var_list.Reverse ();
2521 foreach (DictionaryEntry e in var_list){
2522 LocalVariableReference var = (LocalVariableReference) e.Key;
2523 Label skip = ig.DefineLabel ();
2526 ig.BeginFinallyBlock ();
2529 ig.Emit (OpCodes.Brfalse, skip);
2530 converted_vars [i].Emit (ec);
2531 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2532 ig.MarkLabel (skip);
2533 ig.EndExceptionBlock ();
2535 ec.InFinally = old_in_finally;
2540 bool EmitExpression (EmitContext ec, Expression expr)
2542 Type expr_type = expr.Type;
2543 Expression conv = null;
2545 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2546 conv = Expression.ConvertImplicit (
2547 ec, expr, TypeManager.idisposable_type, loc);
2554 // Make a copy of the expression and operate on that.
2556 ILGenerator ig = ec.ig;
2557 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2562 ig.Emit (OpCodes.Stloc, local_copy);
2564 bool old_in_try = ec.InTry;
2566 ig.BeginExceptionBlock ();
2567 Statement.Emit (ec);
2568 ec.InTry = old_in_try;
2570 Label skip = ig.DefineLabel ();
2571 bool old_in_finally = ec.InFinally;
2572 ig.BeginFinallyBlock ();
2573 ig.Emit (OpCodes.Ldloc, local_copy);
2574 ig.Emit (OpCodes.Brfalse, skip);
2575 ig.Emit (OpCodes.Ldloc, local_copy);
2576 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2577 ig.MarkLabel (skip);
2578 ec.InFinally = old_in_finally;
2579 ig.EndExceptionBlock ();
2584 public override bool Resolve (EmitContext ec)
2586 return Statement.Resolve (ec);
2589 public override bool Emit (EmitContext ec)
2591 if (expression_or_block is DictionaryEntry){
2592 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2593 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2595 return EmitLocalVariableDecls (ec, t, var_list);
2596 } if (expression_or_block is Expression){
2597 Expression e = (Expression) expression_or_block;
2603 return EmitExpression (ec, e);
2610 /// Implementation of the foreach C# statement
2612 public class Foreach : Statement {
2614 LocalVariableReference variable;
2616 Statement statement;
2618 public Foreach (string type, LocalVariableReference var, Expression expr,
2619 Statement stmt, Location l)
2622 this.variable = var;
2628 public override bool Resolve (EmitContext ec)
2630 return statement.Resolve (ec);
2634 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2636 static MethodInfo FetchMethodMoveNext (Type t)
2638 MemberInfo [] move_next_list;
2640 move_next_list = TypeContainer.FindMembers (
2641 t, MemberTypes.Method,
2642 BindingFlags.Public | BindingFlags.Instance,
2643 Type.FilterName, "MoveNext");
2644 if (move_next_list == null || move_next_list.Length == 0)
2647 foreach (MemberInfo m in move_next_list){
2648 MethodInfo mi = (MethodInfo) m;
2651 args = TypeManager.GetArgumentTypes (mi);
2652 if (args != null && args.Length == 0){
2653 if (mi.ReturnType == TypeManager.bool_type)
2661 // Retrieves a `public T get_Current ()' method from the Type `t'
2663 static MethodInfo FetchMethodGetCurrent (Type t)
2665 MemberInfo [] move_next_list;
2667 move_next_list = TypeContainer.FindMembers (
2668 t, MemberTypes.Method,
2669 BindingFlags.Public | BindingFlags.Instance,
2670 Type.FilterName, "get_Current");
2671 if (move_next_list == null || move_next_list.Length == 0)
2674 foreach (MemberInfo m in move_next_list){
2675 MethodInfo mi = (MethodInfo) m;
2678 args = TypeManager.GetArgumentTypes (mi);
2679 if (args != null && args.Length == 0)
2686 // This struct records the helper methods used by the Foreach construct
2688 class ForeachHelperMethods {
2689 public EmitContext ec;
2690 public MethodInfo get_enumerator;
2691 public MethodInfo move_next;
2692 public MethodInfo get_current;
2694 public ForeachHelperMethods (EmitContext ec)
2700 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2705 if (!(m is MethodInfo))
2708 if (m.Name != "GetEnumerator")
2711 MethodInfo mi = (MethodInfo) m;
2712 Type [] args = TypeManager.GetArgumentTypes (mi);
2714 if (args.Length != 0)
2717 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2718 EmitContext ec = hm.ec;
2721 // Check whether GetEnumerator is accessible to us
2723 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2725 Type declaring = mi.DeclaringType;
2726 if (prot == MethodAttributes.Private){
2727 if (declaring != ec.ContainerType)
2729 } else if (prot == MethodAttributes.FamANDAssem){
2730 // If from a different assembly, false
2731 if (!(mi is MethodBuilder))
2734 // Are we being invoked from the same class, or from a derived method?
2736 if (ec.ContainerType != declaring){
2737 if (!ec.ContainerType.IsSubclassOf (declaring))
2740 } else if (prot == MethodAttributes.FamORAssem){
2741 if (!(mi is MethodBuilder ||
2742 ec.ContainerType == declaring ||
2743 ec.ContainerType.IsSubclassOf (declaring)))
2745 } if (prot == MethodAttributes.Family){
2746 if (!(ec.ContainerType == declaring ||
2747 ec.ContainerType.IsSubclassOf (declaring)))
2752 // Ok, we can access it, now make sure that we can do something
2753 // with this `GetEnumerator'
2755 if (mi.ReturnType == TypeManager.ienumerator_type ||
2756 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2757 hm.move_next = TypeManager.bool_movenext_void;
2758 hm.get_current = TypeManager.object_getcurrent_void;
2763 // Ok, so they dont return an IEnumerable, we will have to
2764 // find if they support the GetEnumerator pattern.
2766 Type return_type = mi.ReturnType;
2768 hm.move_next = FetchMethodMoveNext (return_type);
2769 if (hm.move_next == null)
2771 hm.get_current = FetchMethodGetCurrent (return_type);
2772 if (hm.get_current == null)
2779 /// This filter is used to find the GetEnumerator method
2780 /// on which IEnumerator operates
2782 static MemberFilter FilterEnumerator;
2786 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2789 void error1579 (Type t)
2791 Report.Error (1579, loc,
2792 "foreach statement cannot operate on variables of type `" +
2793 t.FullName + "' because that class does not provide a " +
2794 " GetEnumerator method or it is inaccessible");
2797 static bool TryType (Type t, ForeachHelperMethods hm)
2801 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2802 BindingFlags.Public | BindingFlags.NonPublic |
2803 BindingFlags.Instance,
2804 FilterEnumerator, hm);
2806 if (mi == null || mi.Length == 0)
2809 hm.get_enumerator = (MethodInfo) mi [0];
2814 // Looks for a usable GetEnumerator in the Type, and if found returns
2815 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2817 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2819 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2821 if (TryType (t, hm))
2825 // Now try to find the method in the interfaces
2828 Type [] ifaces = t.GetInterfaces ();
2830 foreach (Type i in ifaces){
2831 if (TryType (i, hm))
2836 // Since TypeBuilder.GetInterfaces only returns the interface
2837 // types for this type, we have to keep looping, but once
2838 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2839 // done, because it returns all the types
2841 if ((t is TypeBuilder))
2851 // FIXME: possible optimization.
2852 // We might be able to avoid creating `empty' if the type is the sam
2854 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2856 ILGenerator ig = ec.ig;
2857 LocalBuilder enumerator, disposable;
2858 Expression empty = new EmptyExpression ();
2862 // FIXME: maybe we can apply the same trick we do in the
2863 // array handling to avoid creating empty and conv in some cases.
2865 // Although it is not as important in this case, as the type
2866 // will not likely be object (what the enumerator will return).
2868 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2872 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2873 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2876 // Instantiate the enumerator
2878 if (expr.Type.IsValueType){
2879 if (expr is IMemoryLocation){
2880 IMemoryLocation ml = (IMemoryLocation) expr;
2882 ml.AddressOf (ec, AddressOp.Load);
2884 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2885 " does not implement IMemoryLocation");
2886 ig.Emit (OpCodes.Call, hm.get_enumerator);
2889 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2891 ig.Emit (OpCodes.Stloc, enumerator);
2894 // Protect the code in a try/finalize block, so that
2895 // if the beast implement IDisposable, we get rid of it
2897 Label l = ig.BeginExceptionBlock ();
2898 bool old_in_try = ec.InTry;
2901 Label end_try = ig.DefineLabel ();
2903 ig.MarkLabel (ec.LoopBegin);
2904 ig.Emit (OpCodes.Ldloc, enumerator);
2905 ig.Emit (OpCodes.Callvirt, hm.move_next);
2906 ig.Emit (OpCodes.Brfalse, end_try);
2907 ig.Emit (OpCodes.Ldloc, enumerator);
2908 ig.Emit (OpCodes.Callvirt, hm.get_current);
2909 variable.EmitAssign (ec, conv);
2910 statement.Emit (ec);
2911 ig.Emit (OpCodes.Br, ec.LoopBegin);
2912 ig.MarkLabel (end_try);
2913 ec.InTry = old_in_try;
2915 // The runtime provides this for us.
2916 // ig.Emit (OpCodes.Leave, end);
2919 // Now the finally block
2921 Label end_finally = ig.DefineLabel ();
2922 bool old_in_finally = ec.InFinally;
2923 ec.InFinally = true;
2924 ig.BeginFinallyBlock ();
2926 ig.Emit (OpCodes.Ldloc, enumerator);
2927 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2928 ig.Emit (OpCodes.Stloc, disposable);
2929 ig.Emit (OpCodes.Ldloc, disposable);
2930 ig.Emit (OpCodes.Brfalse, end_finally);
2931 ig.Emit (OpCodes.Ldloc, disposable);
2932 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2933 ig.MarkLabel (end_finally);
2934 ec.InFinally = old_in_finally;
2936 // The runtime generates this anyways.
2937 // ig.Emit (OpCodes.Endfinally);
2939 ig.EndExceptionBlock ();
2941 ig.MarkLabel (ec.LoopEnd);
2946 // FIXME: possible optimization.
2947 // We might be able to avoid creating `empty' if the type is the sam
2949 bool EmitArrayForeach (EmitContext ec, Type var_type)
2951 Type array_type = expr.Type;
2952 Type element_type = array_type.GetElementType ();
2953 Expression conv = null;
2954 Expression empty = new EmptyExpression (element_type);
2956 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2960 int rank = array_type.GetArrayRank ();
2961 ILGenerator ig = ec.ig;
2963 LocalBuilder copy = ig.DeclareLocal (array_type);
2966 // Make our copy of the array
2969 ig.Emit (OpCodes.Stloc, copy);
2972 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2976 ig.Emit (OpCodes.Ldc_I4_0);
2977 ig.Emit (OpCodes.Stloc, counter);
2978 test = ig.DefineLabel ();
2979 ig.Emit (OpCodes.Br, test);
2981 loop = ig.DefineLabel ();
2982 ig.MarkLabel (loop);
2984 ig.Emit (OpCodes.Ldloc, copy);
2985 ig.Emit (OpCodes.Ldloc, counter);
2986 ArrayAccess.EmitLoadOpcode (ig, var_type);
2988 variable.EmitAssign (ec, conv);
2990 statement.Emit (ec);
2992 ig.MarkLabel (ec.LoopBegin);
2993 ig.Emit (OpCodes.Ldloc, counter);
2994 ig.Emit (OpCodes.Ldc_I4_1);
2995 ig.Emit (OpCodes.Add);
2996 ig.Emit (OpCodes.Stloc, counter);
2998 ig.MarkLabel (test);
2999 ig.Emit (OpCodes.Ldloc, counter);
3000 ig.Emit (OpCodes.Ldloc, copy);
3001 ig.Emit (OpCodes.Ldlen);
3002 ig.Emit (OpCodes.Conv_I4);
3003 ig.Emit (OpCodes.Blt, loop);
3005 LocalBuilder [] dim_len = new LocalBuilder [rank];
3006 LocalBuilder [] dim_count = new LocalBuilder [rank];
3007 Label [] loop = new Label [rank];
3008 Label [] test = new Label [rank];
3011 for (dim = 0; dim < rank; dim++){
3012 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
3013 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
3014 test [dim] = ig.DefineLabel ();
3015 loop [dim] = ig.DefineLabel ();
3018 for (dim = 0; dim < rank; dim++){
3019 ig.Emit (OpCodes.Ldloc, copy);
3020 IntLiteral.EmitInt (ig, dim);
3021 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
3022 ig.Emit (OpCodes.Stloc, dim_len [dim]);
3025 for (dim = 0; dim < rank; dim++){
3026 ig.Emit (OpCodes.Ldc_I4_0);
3027 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3028 ig.Emit (OpCodes.Br, test [dim]);
3029 ig.MarkLabel (loop [dim]);
3032 ig.Emit (OpCodes.Ldloc, copy);
3033 for (dim = 0; dim < rank; dim++)
3034 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3037 // FIXME: Maybe we can cache the computation of `get'?
3039 Type [] args = new Type [rank];
3042 for (int i = 0; i < rank; i++)
3043 args [i] = TypeManager.int32_type;
3045 ModuleBuilder mb = CodeGen.ModuleBuilder;
3046 get = mb.GetArrayMethod (
3048 CallingConventions.HasThis| CallingConventions.Standard,
3050 ig.Emit (OpCodes.Call, get);
3051 variable.EmitAssign (ec, conv);
3052 statement.Emit (ec);
3053 ig.MarkLabel (ec.LoopBegin);
3054 for (dim = rank - 1; dim >= 0; dim--){
3055 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3056 ig.Emit (OpCodes.Ldc_I4_1);
3057 ig.Emit (OpCodes.Add);
3058 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3060 ig.MarkLabel (test [dim]);
3061 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3062 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
3063 ig.Emit (OpCodes.Blt, loop [dim]);
3066 ig.MarkLabel (ec.LoopEnd);
3071 public override bool Emit (EmitContext ec)
3076 expr = expr.Resolve (ec);
3080 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
3081 if (var_type == null)
3085 // We need an instance variable. Not sure this is the best
3086 // way of doing this.
3088 // FIXME: When we implement propertyaccess, will those turn
3089 // out to return values in ExprClass? I think they should.
3091 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3092 expr.eclass == ExprClass.PropertyAccess)){
3093 error1579 (expr.Type);
3097 ILGenerator ig = ec.ig;
3099 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
3100 bool old_inloop = ec.InLoop;
3101 ec.LoopBegin = ig.DefineLabel ();
3102 ec.LoopEnd = ig.DefineLabel ();
3105 if (expr.Type.IsArray)
3106 ret_val = EmitArrayForeach (ec, var_type);
3108 ForeachHelperMethods hm;
3110 hm = ProbeCollectionType (ec, expr.Type);
3112 error1579 (expr.Type);
3116 ret_val = EmitCollectionForeach (ec, var_type, hm);
3119 ec.LoopBegin = old_begin;
3120 ec.LoopEnd = old_end;
3121 ec.InLoop = old_inloop;