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 {
113 public Statement TrueStatement;
114 public 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 expr = ResolveBoolean (ec, expr, loc);
141 if (TrueStatement.Resolve (ec)){
142 if (FalseStatement != null){
143 if (FalseStatement.Resolve (ec))
153 public override bool Emit (EmitContext ec)
155 ILGenerator ig = ec.ig;
156 Label false_target = ig.DefineLabel ();
158 bool is_true_ret, is_false_ret;
161 // Dead code elimination
163 if (expr is BoolConstant){
164 bool take = ((BoolConstant) expr).Value;
167 if (FalseStatement != null){
168 Warning_DeadCodeFound (FalseStatement.loc);
170 return TrueStatement.Emit (ec);
172 Warning_DeadCodeFound (TrueStatement.loc);
173 if (FalseStatement != null)
174 return FalseStatement.Emit (ec);
178 EmitBoolExpression (ec, expr, false_target, false);
180 is_true_ret = TrueStatement.Emit (ec);
181 is_false_ret = is_true_ret;
183 if (FalseStatement != null){
184 bool branch_emitted = false;
186 end = ig.DefineLabel ();
188 ig.Emit (OpCodes.Br, end);
189 branch_emitted = true;
192 ig.MarkLabel (false_target);
193 is_false_ret = FalseStatement.Emit (ec);
198 ig.MarkLabel (false_target);
199 is_false_ret = false;
202 return is_true_ret && is_false_ret;
206 public class Do : Statement {
207 public Expression expr;
208 public readonly Statement EmbeddedStatement;
210 public Do (Statement statement, Expression boolExpr, Location l)
213 EmbeddedStatement = statement;
217 public override bool Resolve (EmitContext ec)
219 expr = ResolveBoolean (ec, expr, loc);
223 return EmbeddedStatement.Resolve (ec);
226 public override bool Emit (EmitContext ec)
228 ILGenerator ig = ec.ig;
229 Label loop = ig.DefineLabel ();
230 Label old_begin = ec.LoopBegin;
231 Label old_end = ec.LoopEnd;
232 bool old_inloop = ec.InLoop;
233 bool old_breaks = ec.Breaks;
235 ec.LoopBegin = ig.DefineLabel ();
236 ec.LoopEnd = ig.DefineLabel ();
241 EmbeddedStatement.Emit (ec);
242 bool breaks = ec.Breaks;
243 ig.MarkLabel (ec.LoopBegin);
246 // Dead code elimination
248 if (expr is BoolConstant){
249 bool res = ((BoolConstant) expr).Value;
252 ec.ig.Emit (OpCodes.Br, loop);
254 EmitBoolExpression (ec, expr, loop, true);
256 ig.MarkLabel (ec.LoopEnd);
258 ec.LoopBegin = old_begin;
259 ec.LoopEnd = old_end;
260 ec.InLoop = old_inloop;
261 ec.Breaks = old_breaks;
264 // Inform whether we are infinite or not
266 if (expr is BoolConstant){
267 BoolConstant bc = (BoolConstant) expr;
269 if (bc.Value == true)
270 return breaks == false;
277 public class While : Statement {
278 public Expression expr;
279 public readonly Statement Statement;
281 public While (Expression boolExpr, Statement statement, Location l)
283 this.expr = boolExpr;
284 Statement = statement;
288 public override bool Resolve (EmitContext ec)
290 expr = ResolveBoolean (ec, expr, loc);
294 return Statement.Resolve (ec);
297 public override bool Emit (EmitContext ec)
299 ILGenerator ig = ec.ig;
300 Label old_begin = ec.LoopBegin;
301 Label old_end = ec.LoopEnd;
302 bool old_inloop = ec.InLoop;
303 bool old_breaks = ec.Breaks;
304 Label while_loop = ig.DefineLabel ();
307 ec.LoopBegin = ig.DefineLabel ();
308 ec.LoopEnd = ig.DefineLabel ();
311 ig.Emit (OpCodes.Br, ec.LoopBegin);
312 ig.MarkLabel (while_loop);
315 // Inform whether we are infinite or not
317 if (expr is BoolConstant){
318 BoolConstant bc = (BoolConstant) expr;
320 ig.MarkLabel (ec.LoopBegin);
321 if (bc.Value == false){
322 Warning_DeadCodeFound (Statement.loc);
330 ig.Emit (OpCodes.Br, ec.LoopBegin);
333 // Inform that we are infinite (ie, `we return'), only
334 // if we do not `break' inside the code.
336 ret = breaks == false;
338 ig.MarkLabel (ec.LoopEnd);
342 ig.MarkLabel (ec.LoopBegin);
344 EmitBoolExpression (ec, expr, while_loop, true);
345 ig.MarkLabel (ec.LoopEnd);
350 ec.LoopBegin = old_begin;
351 ec.LoopEnd = old_end;
352 ec.InLoop = old_inloop;
353 ec.Breaks = old_breaks;
359 public class For : Statement {
361 readonly Statement InitStatement;
362 readonly Statement Increment;
363 readonly Statement Statement;
365 public For (Statement initStatement,
371 InitStatement = initStatement;
373 Increment = increment;
374 Statement = statement;
378 public override bool Resolve (EmitContext ec)
383 Test = ResolveBoolean (ec, Test, loc);
388 if (InitStatement != null){
389 if (!InitStatement.Resolve (ec))
393 if (Increment != null){
394 if (!Increment.Resolve (ec))
398 return Statement.Resolve (ec) && ok;
401 public override bool Emit (EmitContext ec)
403 ILGenerator ig = ec.ig;
404 Label old_begin = ec.LoopBegin;
405 Label old_end = ec.LoopEnd;
406 bool old_inloop = ec.InLoop;
407 bool old_breaks = ec.Breaks;
408 Label loop = ig.DefineLabel ();
410 if (InitStatement != null)
411 if (! (InitStatement is EmptyStatement))
412 InitStatement.Emit (ec);
414 ec.LoopBegin = ig.DefineLabel ();
415 ec.LoopEnd = ig.DefineLabel ();
421 // If test is null, there is no test, and we are just
425 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
429 bool breaks = ec.Breaks;
431 ig.MarkLabel (ec.LoopBegin);
432 if (!(Increment is EmptyStatement))
434 ig.Emit (OpCodes.Br, loop);
435 ig.MarkLabel (ec.LoopEnd);
437 ec.LoopBegin = old_begin;
438 ec.LoopEnd = old_end;
439 ec.InLoop = old_inloop;
440 ec.Breaks = old_breaks;
443 // Inform whether we are infinite or not
446 if (Test is BoolConstant){
447 BoolConstant bc = (BoolConstant) Test;
450 return breaks == false;
458 public class StatementExpression : Statement {
461 public StatementExpression (ExpressionStatement expr, Location l)
467 public override bool Resolve (EmitContext ec)
469 expr = (Expression) expr.Resolve (ec);
473 public override bool Emit (EmitContext ec)
475 ILGenerator ig = ec.ig;
477 if (expr is ExpressionStatement)
478 ((ExpressionStatement) expr).EmitStatement (ec);
481 ig.Emit (OpCodes.Pop);
487 public override string ToString ()
489 return "StatementExpression (" + expr + ")";
494 /// Implements the return statement
496 public class Return : Statement {
497 public Expression Expr;
499 public Return (Expression expr, Location l)
505 public override bool Resolve (EmitContext ec)
508 Expr = Expr.Resolve (ec);
515 public override bool Emit (EmitContext ec)
518 Report.Error (157,loc,"Control can not leave the body of the finally block");
522 if (ec.ReturnType == null){
524 Report.Error (127, loc, "Return with a value not allowed here");
529 Report.Error (126, loc, "An object of type `" +
530 TypeManager.CSharpName (ec.ReturnType) + "' is " +
531 "expected for the return statement");
535 if (Expr.Type != ec.ReturnType)
536 Expr = Expression.ConvertImplicitRequired (
537 ec, Expr, ec.ReturnType, loc);
544 if (ec.InTry || ec.InCatch)
545 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
548 if (ec.InTry || ec.InCatch)
549 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
551 ec.ig.Emit (OpCodes.Ret);
557 public class Goto : Statement {
561 public override bool Resolve (EmitContext ec)
566 public Goto (Block parent_block, string label, Location l)
568 block = parent_block;
573 public string Target {
579 public override bool Emit (EmitContext ec)
581 LabeledStatement label = block.LookupLabel (target);
585 // Maybe we should catch this before?
589 "No such label `" + target + "' in this scope");
592 Label l = label.LabelTarget (ec);
593 ec.ig.Emit (OpCodes.Br, l);
599 public class LabeledStatement : Statement {
604 public LabeledStatement (string label_name)
606 this.label_name = label_name;
609 public Label LabelTarget (EmitContext ec)
613 label = ec.ig.DefineLabel ();
619 public override bool Emit (EmitContext ec)
622 ec.ig.MarkLabel (label);
630 /// `goto default' statement
632 public class GotoDefault : Statement {
634 public GotoDefault (Location l)
639 public override bool Emit (EmitContext ec)
641 if (ec.Switch == null){
642 Report.Error (153, loc, "goto default is only valid in a switch statement");
646 if (!ec.Switch.GotDefault){
647 Report.Error (159, loc, "No default target on switch statement");
650 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
656 /// `goto case' statement
658 public class GotoCase : Statement {
661 public GotoCase (Expression e, Location l)
667 public override bool Emit (EmitContext ec)
669 if (ec.Switch == null){
670 Report.Error (153, loc, "goto case is only valid in a switch statement");
674 expr = expr.Resolve (ec);
678 if (!(expr is Constant)){
679 Report.Error (159, loc, "Target expression for goto case is not constant");
683 object val = Expression.ConvertIntLiteral (
684 (Constant) expr, ec.Switch.SwitchType, loc);
689 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
694 "No such label 'case " + val + "': for the goto case");
697 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
702 public class Throw : Statement {
705 public Throw (Expression expr, Location l)
711 public override bool Resolve (EmitContext ec)
714 expr = expr.Resolve (ec);
721 public override bool Emit (EmitContext ec)
725 ec.ig.Emit (OpCodes.Rethrow);
729 "A throw statement with no argument is only " +
730 "allowed in a catch clause");
737 ec.ig.Emit (OpCodes.Throw);
743 public class Break : Statement {
745 public Break (Location l)
750 public override bool Emit (EmitContext ec)
752 ILGenerator ig = ec.ig;
754 if (ec.InLoop == false && ec.Switch == null){
755 Report.Error (139, loc, "No enclosing loop or switch to continue to");
760 ig.Emit (OpCodes.Br, ec.LoopEnd);
766 public class Continue : Statement {
768 public Continue (Location l)
773 public override bool Emit (EmitContext ec)
775 Label begin = ec.LoopBegin;
778 Report.Error (139, loc, "No enclosing loop to continue to");
783 // UGH: Non trivial. This Br might cross a try/catch boundary
787 // try { ... } catch { continue; }
791 // try {} catch { while () { continue; }}
793 ec.ig.Emit (OpCodes.Br, begin);
798 public class VariableInfo {
799 public readonly string Type;
800 public LocalBuilder LocalBuilder;
801 public Type VariableType;
802 public readonly Location Location;
805 public bool Assigned;
806 public bool ReadOnly;
808 public VariableInfo (string type, Location l)
815 public void MakePinned ()
817 TypeManager.MakePinned (LocalBuilder);
822 /// Block represents a C# block.
826 /// This class is used in a number of places: either to represent
827 /// explicit blocks that the programmer places or implicit blocks.
829 /// Implicit blocks are used as labels or to introduce variable
832 public class Block : Statement {
833 public readonly Block Parent;
834 public readonly bool Implicit;
835 public readonly Location StartLocation;
836 public Location EndLocation;
839 // The statements in this block
841 ArrayList statements;
844 // An array of Blocks. We keep track of children just
845 // to generate the local variable declarations.
847 // Statements and child statements are handled through the
853 // Labels. (label, block) pairs.
858 // Keeps track of (name, type) pairs
863 // Keeps track of constants
867 // Maps variable names to ILGenerator.LocalBuilders
869 Hashtable local_builders;
877 public Block (Block parent)
878 : this (parent, false, Location.Null, Location.Null)
881 public Block (Block parent, bool implicit_block)
882 : this (parent, implicit_block, Location.Null, Location.Null)
885 public Block (Block parent, Location start, Location end)
886 : this (parent, false, start, end)
889 public Block (Block parent, bool implicit_block, Location start, Location end)
892 parent.AddChild (this);
894 this.Parent = parent;
895 this.Implicit = implicit_block;
896 this.StartLocation = start;
897 this.EndLocation = end;
900 statements = new ArrayList ();
909 void AddChild (Block b)
911 if (children == null)
912 children = new ArrayList ();
917 public void SetEndLocation (Location loc)
923 /// Adds a label to the current block.
927 /// false if the name already exists in this block. true
931 public bool AddLabel (string name, LabeledStatement target)
934 labels = new Hashtable ();
935 if (labels.Contains (name))
938 labels.Add (name, target);
942 public LabeledStatement LookupLabel (string name)
945 if (labels.Contains (name))
946 return ((LabeledStatement) labels [name]);
950 return Parent.LookupLabel (name);
955 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
957 if (variables == null)
958 variables = new Hashtable ();
960 if (GetVariableType (name) != null)
965 Parameter p = pars.GetParameterByName (name, out idx);
970 VariableInfo vi = new VariableInfo (type, l);
972 variables.Add (name, vi);
974 // Console.WriteLine ("Adding {0} to {1}", name, ID);
978 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
980 if (AddVariable (type, name, pars, l) == null)
983 if (constants == null)
984 constants = new Hashtable ();
986 constants.Add (name, value);
990 public Hashtable Variables {
996 public VariableInfo GetVariableInfo (string name)
998 if (variables != null) {
1000 temp = variables [name];
1003 return (VariableInfo) temp;
1008 return Parent.GetVariableInfo (name);
1013 public string GetVariableType (string name)
1015 VariableInfo vi = GetVariableInfo (name);
1023 public Expression GetConstantExpression (string name)
1025 if (constants != null) {
1027 temp = constants [name];
1030 return (Expression) temp;
1034 return Parent.GetConstantExpression (name);
1040 /// True if the variable named @name has been defined
1043 public bool IsVariableDefined (string name)
1045 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1046 if (variables != null) {
1047 if (variables.Contains (name))
1052 return Parent.IsVariableDefined (name);
1058 /// True if the variable named @name is a constant
1060 public bool IsConstant (string name)
1062 Expression e = null;
1064 e = GetConstantExpression (name);
1070 /// Use to fetch the statement associated with this label
1072 public Statement this [string name] {
1074 return (Statement) labels [name];
1079 /// A list of labels that were not used within this block
1081 public string [] GetUnreferenced ()
1083 // FIXME: Implement me
1087 public void AddStatement (Statement s)
1105 /// Emits the variable declarations and labels.
1108 /// tc: is our typecontainer (to resolve type references)
1109 /// ig: is the code generator:
1110 /// toplevel: the toplevel block. This is used for checking
1111 /// that no two labels with the same name are used.
1113 public void EmitMeta (EmitContext ec, Block toplevel)
1115 DeclSpace ds = ec.DeclSpace;
1116 ILGenerator ig = ec.ig;
1119 // Process this block variables
1121 if (variables != null){
1122 local_builders = new Hashtable ();
1124 foreach (DictionaryEntry de in variables){
1125 string name = (string) de.Key;
1126 VariableInfo vi = (VariableInfo) de.Value;
1129 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
1133 vi.VariableType = t;
1134 vi.LocalBuilder = ig.DeclareLocal (t);
1136 if (CodeGen.SymbolWriter != null)
1137 vi.LocalBuilder.SetLocalSymInfo (name);
1139 if (constants == null)
1142 Expression cv = (Expression) constants [name];
1146 Expression e = cv.Resolve (ec);
1150 if (!(e is Constant)){
1151 Report.Error (133, vi.Location,
1152 "The expression being assigned to `" +
1153 name + "' must be constant (" + e + ")");
1157 constants.Remove (name);
1158 constants.Add (name, e);
1163 // Now, handle the children
1165 if (children != null){
1166 foreach (Block b in children)
1167 b.EmitMeta (ec, toplevel);
1171 public void UsageWarning ()
1175 if (variables != null){
1176 foreach (DictionaryEntry de in variables){
1177 VariableInfo vi = (VariableInfo) de.Value;
1182 name = (string) de.Key;
1186 219, vi.Location, "The variable `" + name +
1187 "' is assigned but its value is never used");
1190 168, vi.Location, "The variable `" +
1192 "' is declared but never used");
1197 if (children != null)
1198 foreach (Block b in children)
1202 public override bool Resolve (EmitContext ec)
1204 Block prev_block = ec.CurrentBlock;
1206 ec.CurrentBlock = this;
1207 foreach (Statement s in statements){
1208 if (s.Resolve (ec) == false){
1209 ec.CurrentBlock = prev_block;
1214 ec.CurrentBlock = prev_block;
1218 public override bool Emit (EmitContext ec)
1220 bool is_ret = false;
1221 Block prev_block = ec.CurrentBlock;
1223 ec.CurrentBlock = this;
1225 if (CodeGen.SymbolWriter != null) {
1226 ec.Mark (StartLocation);
1228 foreach (Statement s in statements) {
1231 is_ret = s.Emit (ec);
1234 ec.Mark (EndLocation);
1236 foreach (Statement s in statements)
1237 is_ret = s.Emit (ec);
1240 ec.CurrentBlock = prev_block;
1245 public class SwitchLabel {
1248 public Location loc;
1249 public Label ILLabel;
1250 public Label ILLabelCode;
1253 // if expr == null, then it is the default case.
1255 public SwitchLabel (Expression expr, Location l)
1261 public Expression Label {
1267 public object Converted {
1274 // Resolves the expression, reduces it to a literal if possible
1275 // and then converts it to the requested type.
1277 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1279 ILLabel = ec.ig.DefineLabel ();
1280 ILLabelCode = ec.ig.DefineLabel ();
1285 Expression e = label.Resolve (ec);
1290 if (!(e is Constant)){
1291 Console.WriteLine ("Value is: " + label);
1292 Report.Error (150, loc, "A constant value is expected");
1296 if (e is StringConstant || e is NullLiteral){
1297 if (required_type == TypeManager.string_type){
1299 ILLabel = ec.ig.DefineLabel ();
1304 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1305 if (converted == null)
1312 public class SwitchSection {
1313 // An array of SwitchLabels.
1314 public readonly ArrayList Labels;
1315 public readonly Block Block;
1317 public SwitchSection (ArrayList labels, Block block)
1324 public class Switch : Statement {
1325 public readonly ArrayList Sections;
1326 public Expression Expr;
1329 /// Maps constants whose type type SwitchType to their SwitchLabels.
1331 public Hashtable Elements;
1334 /// The governing switch type
1336 public Type SwitchType;
1342 Label default_target;
1345 // The types allowed to be implicitly cast from
1346 // on the governing type
1348 static Type [] allowed_types;
1350 public Switch (Expression e, ArrayList sects, Location l)
1357 public bool GotDefault {
1363 public Label DefaultTarget {
1365 return default_target;
1370 // Determines the governing type for a switch. The returned
1371 // expression might be the expression from the switch, or an
1372 // expression that includes any potential conversions to the
1373 // integral types or to string.
1375 Expression SwitchGoverningType (EmitContext ec, Type t)
1377 if (t == TypeManager.int32_type ||
1378 t == TypeManager.uint32_type ||
1379 t == TypeManager.char_type ||
1380 t == TypeManager.byte_type ||
1381 t == TypeManager.sbyte_type ||
1382 t == TypeManager.ushort_type ||
1383 t == TypeManager.short_type ||
1384 t == TypeManager.uint64_type ||
1385 t == TypeManager.int64_type ||
1386 t == TypeManager.string_type ||
1387 t == TypeManager.bool_type ||
1388 t.IsSubclassOf (TypeManager.enum_type))
1391 if (allowed_types == null){
1392 allowed_types = new Type [] {
1393 TypeManager.sbyte_type,
1394 TypeManager.byte_type,
1395 TypeManager.short_type,
1396 TypeManager.ushort_type,
1397 TypeManager.int32_type,
1398 TypeManager.uint32_type,
1399 TypeManager.int64_type,
1400 TypeManager.uint64_type,
1401 TypeManager.char_type,
1402 TypeManager.bool_type,
1403 TypeManager.string_type
1408 // Try to find a *user* defined implicit conversion.
1410 // If there is no implicit conversion, or if there are multiple
1411 // conversions, we have to report an error
1413 Expression converted = null;
1414 foreach (Type tt in allowed_types){
1417 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1421 if (converted != null){
1422 Report.Error (-12, loc, "More than one conversion to an integral " +
1423 " type exists for type `" +
1424 TypeManager.CSharpName (Expr.Type)+"'");
1432 void error152 (string n)
1435 152, "The label `" + n + ":' " +
1436 "is already present on this switch statement");
1440 // Performs the basic sanity checks on the switch statement
1441 // (looks for duplicate keys and non-constant expressions).
1443 // It also returns a hashtable with the keys that we will later
1444 // use to compute the switch tables
1446 bool CheckSwitch (EmitContext ec)
1450 Elements = new Hashtable ();
1452 got_default = false;
1454 if (TypeManager.IsEnumType (SwitchType)){
1455 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1457 compare_type = SwitchType;
1459 foreach (SwitchSection ss in Sections){
1460 foreach (SwitchLabel sl in ss.Labels){
1461 if (!sl.ResolveAndReduce (ec, SwitchType)){
1466 if (sl.Label == null){
1468 error152 ("default");
1475 object key = sl.Converted;
1477 if (key is Constant)
1478 key = ((Constant) key).GetValue ();
1481 key = NullLiteral.Null;
1483 string lname = null;
1484 if (compare_type == TypeManager.uint64_type){
1485 ulong v = (ulong) key;
1487 if (Elements.Contains (v))
1488 lname = v.ToString ();
1490 Elements.Add (v, sl);
1491 } else if (compare_type == TypeManager.int64_type){
1492 long v = (long) key;
1494 if (Elements.Contains (v))
1495 lname = v.ToString ();
1497 Elements.Add (v, sl);
1498 } else if (compare_type == TypeManager.uint32_type){
1499 uint v = (uint) key;
1501 if (Elements.Contains (v))
1502 lname = v.ToString ();
1504 Elements.Add (v, sl);
1505 } else if (compare_type == TypeManager.char_type){
1506 char v = (char) key;
1508 if (Elements.Contains (v))
1509 lname = v.ToString ();
1511 Elements.Add (v, sl);
1512 } else if (compare_type == TypeManager.byte_type){
1513 byte v = (byte) key;
1515 if (Elements.Contains (v))
1516 lname = v.ToString ();
1518 Elements.Add (v, sl);
1519 } else if (compare_type == TypeManager.sbyte_type){
1520 sbyte v = (sbyte) key;
1522 if (Elements.Contains (v))
1523 lname = v.ToString ();
1525 Elements.Add (v, sl);
1526 } else if (compare_type == TypeManager.short_type){
1527 short v = (short) key;
1529 if (Elements.Contains (v))
1530 lname = v.ToString ();
1532 Elements.Add (v, sl);
1533 } else if (compare_type == TypeManager.ushort_type){
1534 ushort v = (ushort) key;
1536 if (Elements.Contains (v))
1537 lname = v.ToString ();
1539 Elements.Add (v, sl);
1540 } else if (compare_type == TypeManager.string_type){
1541 if (key is NullLiteral){
1542 if (Elements.Contains (NullLiteral.Null))
1545 Elements.Add (NullLiteral.Null, null);
1547 string s = (string) key;
1549 if (Elements.Contains (s))
1552 Elements.Add (s, sl);
1554 } else if (compare_type == TypeManager.int32_type) {
1557 if (Elements.Contains (v))
1558 lname = v.ToString ();
1560 Elements.Add (v, sl);
1561 } else if (compare_type == TypeManager.bool_type) {
1562 bool v = (bool) key;
1564 if (Elements.Contains (v))
1565 lname = v.ToString ();
1567 Elements.Add (v, sl);
1571 throw new Exception ("Unknown switch type!" +
1572 SwitchType + " " + compare_type);
1576 error152 ("case + " + lname);
1587 void EmitObjectInteger (ILGenerator ig, object k)
1590 IntConstant.EmitInt (ig, (int) k);
1591 else if (k is Constant) {
1592 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1595 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1598 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1600 IntConstant.EmitInt (ig, (int) (long) k);
1601 ig.Emit (OpCodes.Conv_I8);
1604 LongConstant.EmitLong (ig, (long) k);
1606 else if (k is ulong)
1608 if ((ulong) k < (1L<<32))
1610 IntConstant.EmitInt (ig, (int) (long) k);
1611 ig.Emit (OpCodes.Conv_U8);
1615 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1619 IntConstant.EmitInt (ig, (int) ((char) k));
1620 else if (k is sbyte)
1621 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1623 IntConstant.EmitInt (ig, (int) ((byte) k));
1624 else if (k is short)
1625 IntConstant.EmitInt (ig, (int) ((short) k));
1626 else if (k is ushort)
1627 IntConstant.EmitInt (ig, (int) ((ushort) k));
1629 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1631 throw new Exception ("Unhandled case");
1634 // structure used to hold blocks of keys while calculating table switch
1635 class KeyBlock : IComparable
1637 public KeyBlock (long _nFirst)
1639 nFirst = nLast = _nFirst;
1643 public ArrayList rgKeys = null;
1646 get { return (int) (nLast - nFirst + 1); }
1648 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1650 return kbLast.nLast - kbFirst.nFirst + 1;
1652 public int CompareTo (object obj)
1654 KeyBlock kb = (KeyBlock) obj;
1655 int nLength = Length;
1656 int nLengthOther = kb.Length;
1657 if (nLengthOther == nLength)
1658 return (int) (kb.nFirst - nFirst);
1659 return nLength - nLengthOther;
1664 /// This method emits code for a lookup-based switch statement (non-string)
1665 /// Basically it groups the cases into blocks that are at least half full,
1666 /// and then spits out individual lookup opcodes for each block.
1667 /// It emits the longest blocks first, and short blocks are just
1668 /// handled with direct compares.
1670 /// <param name="ec"></param>
1671 /// <param name="val"></param>
1672 /// <returns></returns>
1673 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1675 int cElements = Elements.Count;
1676 object [] rgKeys = new object [cElements];
1677 Elements.Keys.CopyTo (rgKeys, 0);
1678 Array.Sort (rgKeys);
1680 // initialize the block list with one element per key
1681 ArrayList rgKeyBlocks = new ArrayList ();
1682 foreach (object key in rgKeys)
1683 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1686 // iteratively merge the blocks while they are at least half full
1687 // there's probably a really cool way to do this with a tree...
1688 while (rgKeyBlocks.Count > 1)
1690 ArrayList rgKeyBlocksNew = new ArrayList ();
1691 kbCurr = (KeyBlock) rgKeyBlocks [0];
1692 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1694 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1695 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1698 kbCurr.nLast = kb.nLast;
1702 // start a new block
1703 rgKeyBlocksNew.Add (kbCurr);
1707 rgKeyBlocksNew.Add (kbCurr);
1708 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1710 rgKeyBlocks = rgKeyBlocksNew;
1713 // initialize the key lists
1714 foreach (KeyBlock kb in rgKeyBlocks)
1715 kb.rgKeys = new ArrayList ();
1717 // fill the key lists
1719 if (rgKeyBlocks.Count > 0) {
1720 kbCurr = (KeyBlock) rgKeyBlocks [0];
1721 foreach (object key in rgKeys)
1723 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1725 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1726 kbCurr.rgKeys.Add (key);
1730 // sort the blocks so we can tackle the largest ones first
1731 rgKeyBlocks.Sort ();
1733 // okay now we can start...
1734 ILGenerator ig = ec.ig;
1735 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1736 Label lblDefault = ig.DefineLabel ();
1738 Type typeKeys = null;
1739 if (rgKeys.Length > 0)
1740 typeKeys = rgKeys [0].GetType (); // used for conversions
1742 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1744 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1745 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1748 foreach (object key in kb.rgKeys)
1750 ig.Emit (OpCodes.Ldloc, val);
1751 EmitObjectInteger (ig, key);
1752 SwitchLabel sl = (SwitchLabel) Elements [key];
1753 ig.Emit (OpCodes.Beq, sl.ILLabel);
1758 // TODO: if all the keys in the block are the same and there are
1759 // no gaps/defaults then just use a range-check.
1760 if (SwitchType == TypeManager.int64_type ||
1761 SwitchType == TypeManager.uint64_type)
1763 // TODO: optimize constant/I4 cases
1765 // check block range (could be > 2^31)
1766 ig.Emit (OpCodes.Ldloc, val);
1767 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1768 ig.Emit (OpCodes.Blt, lblDefault);
1769 ig.Emit (OpCodes.Ldloc, val);
1770 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1771 ig.Emit (OpCodes.Bgt, lblDefault);
1774 ig.Emit (OpCodes.Ldloc, val);
1777 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1778 ig.Emit (OpCodes.Sub);
1780 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1785 ig.Emit (OpCodes.Ldloc, val);
1786 int nFirst = (int) kb.nFirst;
1789 IntConstant.EmitInt (ig, nFirst);
1790 ig.Emit (OpCodes.Sub);
1792 else if (nFirst < 0)
1794 IntConstant.EmitInt (ig, -nFirst);
1795 ig.Emit (OpCodes.Add);
1799 // first, build the list of labels for the switch
1801 int cJumps = kb.Length;
1802 Label [] rgLabels = new Label [cJumps];
1803 for (int iJump = 0; iJump < cJumps; iJump++)
1805 object key = kb.rgKeys [iKey];
1806 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1808 SwitchLabel sl = (SwitchLabel) Elements [key];
1809 rgLabels [iJump] = sl.ILLabel;
1813 rgLabels [iJump] = lblDefault;
1815 // emit the switch opcode
1816 ig.Emit (OpCodes.Switch, rgLabels);
1819 // mark the default for this block
1821 ig.MarkLabel (lblDefault);
1824 // TODO: find the default case and emit it here,
1825 // to prevent having to do the following jump.
1826 // make sure to mark other labels in the default section
1828 // the last default just goes to the end
1829 ig.Emit (OpCodes.Br, lblDefault);
1831 // now emit the code for the sections
1832 bool fFoundDefault = false;
1833 bool fAllReturn = true;
1834 foreach (SwitchSection ss in Sections)
1836 foreach (SwitchLabel sl in ss.Labels)
1838 ig.MarkLabel (sl.ILLabel);
1839 ig.MarkLabel (sl.ILLabelCode);
1840 if (sl.Label == null)
1842 ig.MarkLabel (lblDefault);
1843 fFoundDefault = true;
1846 fAllReturn &= ss.Block.Emit (ec);
1847 //ig.Emit (OpCodes.Br, lblEnd);
1851 ig.MarkLabel (lblDefault);
1852 ig.MarkLabel (lblEnd);
1857 // This simple emit switch works, but does not take advantage of the
1859 // TODO: remove non-string logic from here
1860 // TODO: binary search strings?
1862 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1864 ILGenerator ig = ec.ig;
1865 Label end_of_switch = ig.DefineLabel ();
1866 Label next_test = ig.DefineLabel ();
1867 Label null_target = ig.DefineLabel ();
1868 bool default_found = false;
1869 bool first_test = true;
1870 bool pending_goto_end = false;
1871 bool all_return = true;
1872 bool is_string = false;
1876 // Special processing for strings: we cant compare
1879 if (SwitchType == TypeManager.string_type){
1880 ig.Emit (OpCodes.Ldloc, val);
1883 if (Elements.Contains (NullLiteral.Null)){
1884 ig.Emit (OpCodes.Brfalse, null_target);
1886 ig.Emit (OpCodes.Brfalse, default_target);
1888 ig.Emit (OpCodes.Ldloc, val);
1889 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1890 ig.Emit (OpCodes.Stloc, val);
1893 SwitchSection last_section;
1894 last_section = (SwitchSection) Sections [Sections.Count-1];
1896 foreach (SwitchSection ss in Sections){
1897 Label sec_begin = ig.DefineLabel ();
1899 if (pending_goto_end)
1900 ig.Emit (OpCodes.Br, end_of_switch);
1902 int label_count = ss.Labels.Count;
1904 foreach (SwitchLabel sl in ss.Labels){
1905 ig.MarkLabel (sl.ILLabel);
1908 ig.MarkLabel (next_test);
1909 next_test = ig.DefineLabel ();
1912 // If we are the default target
1914 if (sl.Label == null){
1915 ig.MarkLabel (default_target);
1916 default_found = true;
1918 object lit = sl.Converted;
1920 if (lit is NullLiteral){
1922 if (label_count == 1)
1923 ig.Emit (OpCodes.Br, next_test);
1928 StringConstant str = (StringConstant) lit;
1930 ig.Emit (OpCodes.Ldloc, val);
1931 ig.Emit (OpCodes.Ldstr, str.Value);
1932 if (label_count == 1)
1933 ig.Emit (OpCodes.Bne_Un, next_test);
1935 ig.Emit (OpCodes.Beq, sec_begin);
1937 ig.Emit (OpCodes.Ldloc, val);
1938 EmitObjectInteger (ig, lit);
1939 ig.Emit (OpCodes.Ceq);
1940 if (label_count == 1)
1941 ig.Emit (OpCodes.Brfalse, next_test);
1943 ig.Emit (OpCodes.Brtrue, sec_begin);
1947 if (label_count != 1 && ss != last_section)
1948 ig.Emit (OpCodes.Br, next_test);
1951 ig.MarkLabel (null_target);
1952 ig.MarkLabel (sec_begin);
1953 foreach (SwitchLabel sl in ss.Labels)
\r
1954 ig.MarkLabel (sl.ILLabelCode);
1955 if (ss.Block.Emit (ec))
1956 pending_goto_end = false;
1959 pending_goto_end = true;
1963 if (!default_found){
1964 ig.MarkLabel (default_target);
1967 ig.MarkLabel (next_test);
1968 ig.MarkLabel (end_of_switch);
1973 public override bool Resolve (EmitContext ec)
1975 foreach (SwitchSection ss in Sections){
1976 if (ss.Block.Resolve (ec) != true)
1983 public override bool Emit (EmitContext ec)
1985 Expr = Expr.Resolve (ec);
1989 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1990 if (new_expr == null){
1991 Report.Error (151, loc, "An integer type or string was expected for switch");
1996 SwitchType = new_expr.Type;
1998 if (!CheckSwitch (ec))
2001 // Store variable for comparission purposes
2002 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2004 ec.ig.Emit (OpCodes.Stloc, value);
2006 ILGenerator ig = ec.ig;
2008 default_target = ig.DefineLabel ();
2011 // Setup the codegen context
2013 Label old_end = ec.LoopEnd;
2014 Switch old_switch = ec.Switch;
2016 ec.LoopEnd = ig.DefineLabel ();
2021 if (SwitchType == TypeManager.string_type)
2022 all_return = SimpleSwitchEmit (ec, value);
2024 all_return = TableSwitchEmit (ec, value);
2026 // Restore context state.
2027 ig.MarkLabel (ec.LoopEnd);
2030 // Restore the previous context
2032 ec.LoopEnd = old_end;
2033 ec.Switch = old_switch;
2039 public class Lock : Statement {
2041 Statement Statement;
2043 public Lock (Expression expr, Statement stmt, Location l)
2050 public override bool Resolve (EmitContext ec)
2052 expr = expr.Resolve (ec);
2053 return Statement.Resolve (ec) && expr != null;
2056 public override bool Emit (EmitContext ec)
2058 Type type = expr.Type;
2061 if (type.IsValueType){
2062 Report.Error (185, loc, "lock statement requires the expression to be " +
2063 " a reference type (type is: `" +
2064 TypeManager.CSharpName (type) + "'");
2068 ILGenerator ig = ec.ig;
2069 LocalBuilder temp = ig.DeclareLocal (type);
2072 ig.Emit (OpCodes.Dup);
2073 ig.Emit (OpCodes.Stloc, temp);
2074 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2077 Label end = ig.BeginExceptionBlock ();
2078 bool old_in_try = ec.InTry;
2080 Label finish = ig.DefineLabel ();
2081 val = Statement.Emit (ec);
2082 ec.InTry = old_in_try;
2083 // ig.Emit (OpCodes.Leave, finish);
2085 ig.MarkLabel (finish);
2088 ig.BeginFinallyBlock ();
2089 ig.Emit (OpCodes.Ldloc, temp);
2090 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2091 ig.EndExceptionBlock ();
2097 public class Unchecked : Statement {
2098 public readonly Block Block;
2100 public Unchecked (Block b)
2105 public override bool Resolve (EmitContext ec)
2107 return Block.Resolve (ec);
2110 public override bool Emit (EmitContext ec)
2112 bool previous_state = ec.CheckState;
2113 bool previous_state_const = ec.ConstantCheckState;
2116 ec.CheckState = false;
2117 ec.ConstantCheckState = false;
2118 val = Block.Emit (ec);
2119 ec.CheckState = previous_state;
2120 ec.ConstantCheckState = previous_state_const;
2126 public class Checked : Statement {
2127 public readonly Block Block;
2129 public Checked (Block b)
2134 public override bool Resolve (EmitContext ec)
2136 return Block.Resolve (ec);
2139 public override bool Emit (EmitContext ec)
2141 bool previous_state = ec.CheckState;
2142 bool previous_state_const = ec.ConstantCheckState;
2145 ec.CheckState = true;
2146 ec.ConstantCheckState = true;
2147 val = Block.Emit (ec);
2148 ec.CheckState = previous_state;
2149 ec.ConstantCheckState = previous_state_const;
2155 public class Unsafe : Statement {
2156 public readonly Block Block;
2158 public Unsafe (Block b)
2163 public override bool Resolve (EmitContext ec)
2165 return Block.Resolve (ec);
2168 public override bool Emit (EmitContext ec)
2170 bool previous_state = ec.InUnsafe;
2174 val = Block.Emit (ec);
2175 ec.InUnsafe = previous_state;
2184 public class Fixed : Statement {
2186 ArrayList declarators;
2187 Statement statement;
2189 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
2192 declarators = decls;
2197 public override bool Resolve (EmitContext ec)
2199 return statement.Resolve (ec);
2202 public override bool Emit (EmitContext ec)
2204 ILGenerator ig = ec.ig;
2207 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2211 bool is_ret = false;
2213 foreach (Pair p in declarators){
2214 VariableInfo vi = (VariableInfo) p.First;
2215 Expression e = (Expression) p.Second;
2218 // The rules for the possible declarators are pretty wise,
2219 // but the production on the grammar is more concise.
2221 // So we have to enforce these rules here.
2223 // We do not resolve before doing the case 1 test,
2224 // because the grammar is explicit in that the token &
2225 // is present, so we need to test for this particular case.
2229 // Case 1: & object.
2231 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2232 Expression child = ((Unary) e).Expr;
2235 if (child is ParameterReference || child is LocalVariableReference){
2238 "No need to use fixed statement for parameters or " +
2239 "local variable declarations (address is already " +
2248 child = ((Unary) e).Expr;
2250 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2254 // Store pointer in pinned location
2257 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2259 is_ret = statement.Emit (ec);
2261 // Clear the pinned variable.
2262 ig.Emit (OpCodes.Ldc_I4_0);
2263 ig.Emit (OpCodes.Conv_U);
2264 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2276 if (e.Type.IsArray){
2277 Type array_type = e.Type.GetElementType ();
2281 // Provided that array_type is unmanaged,
2283 if (!TypeManager.VerifyUnManaged (array_type, loc))
2287 // and T* is implicitly convertible to the
2288 // pointer type given in the fixed statement.
2290 ArrayPtr array_ptr = new ArrayPtr (e);
2292 Expression converted = Expression.ConvertImplicitRequired (
2293 ec, array_ptr, vi.VariableType, loc);
2294 if (converted == null)
2298 // Store pointer in pinned location
2300 converted.Emit (ec);
2302 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2304 is_ret = statement.Emit (ec);
2306 // Clear the pinned variable.
2307 ig.Emit (OpCodes.Ldc_I4_0);
2308 ig.Emit (OpCodes.Conv_U);
2309 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2317 if (e.Type == TypeManager.string_type){
2318 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2319 TypeManager.MakePinned (pinned_string);
2322 ig.Emit (OpCodes.Stloc, pinned_string);
2324 Expression sptr = new StringPtr (pinned_string);
2325 Expression converted = Expression.ConvertImplicitRequired (
2326 ec, sptr, vi.VariableType, loc);
2328 if (converted == null)
2331 converted.Emit (ec);
2332 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2334 is_ret = statement.Emit (ec);
2336 // Clear the pinned variable
2337 ig.Emit (OpCodes.Ldnull);
2338 ig.Emit (OpCodes.Stloc, pinned_string);
2346 public class Catch {
2347 public readonly string Type;
2348 public readonly string Name;
2349 public readonly Block Block;
2350 public readonly Location Location;
2352 public Catch (string type, string name, Block block, Location l)
2361 public class Try : Statement {
2362 public readonly Block Fini, Block;
2363 public readonly ArrayList Specific;
2364 public readonly Catch General;
2367 // specific, general and fini might all be null.
2369 public Try (Block block, ArrayList specific, Catch general, Block fini)
2371 if (specific == null && general == null){
2372 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2376 this.Specific = specific;
2377 this.General = general;
2381 public override bool Resolve (EmitContext ec)
2385 if (General != null)
2386 if (!General.Block.Resolve (ec))
2389 foreach (Catch c in Specific){
2390 if (!c.Block.Resolve (ec))
2394 if (!Block.Resolve (ec))
2398 if (!Fini.Resolve (ec))
2404 public override bool Emit (EmitContext ec)
2406 ILGenerator ig = ec.ig;
2408 Label finish = ig.DefineLabel ();;
2411 end = ig.BeginExceptionBlock ();
2412 bool old_in_try = ec.InTry;
2414 returns = Block.Emit (ec);
2415 ec.InTry = old_in_try;
2418 // System.Reflection.Emit provides this automatically:
2419 // ig.Emit (OpCodes.Leave, finish);
2421 bool old_in_catch = ec.InCatch;
2423 DeclSpace ds = ec.DeclSpace;
2425 foreach (Catch c in Specific){
2426 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2429 if (catch_type == null)
2432 ig.BeginCatchBlock (catch_type);
2434 if (c.Name != null){
2435 vi = c.Block.GetVariableInfo (c.Name);
2437 Console.WriteLine ("This should not happen! variable does not exist in this block");
2438 Environment.Exit (0);
2441 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2443 ig.Emit (OpCodes.Pop);
2445 if (!c.Block.Emit (ec))
2449 if (General != null){
2450 ig.BeginCatchBlock (TypeManager.object_type);
2451 ig.Emit (OpCodes.Pop);
2452 if (!General.Block.Emit (ec))
2455 ec.InCatch = old_in_catch;
2457 ig.MarkLabel (finish);
2459 ig.BeginFinallyBlock ();
2460 bool old_in_finally = ec.InFinally;
2461 ec.InFinally = true;
2463 ec.InFinally = old_in_finally;
2466 ig.EndExceptionBlock ();
2469 // FIXME: Is this correct?
2470 // Replace with `returns' and check test-18, maybe we can
2471 // perform an optimization here.
2478 // FIXME: We still do not support the expression variant of the using
2481 public class Using : Statement {
2482 object expression_or_block;
2483 Statement Statement;
2485 public Using (object expression_or_block, Statement stmt, Location l)
2487 this.expression_or_block = expression_or_block;
2493 // Emits the code for the case of using using a local variable declaration.
2495 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2497 ILGenerator ig = ec.ig;
2498 Expression [] converted_vars;
2499 bool need_conv = false;
2500 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2507 // The type must be an IDisposable or an implicit conversion
2510 converted_vars = new Expression [var_list.Count];
2511 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2512 foreach (DictionaryEntry e in var_list){
2513 Expression var = (Expression) e.Key;
2515 var = var.Resolve (ec);
2519 converted_vars [i] = Expression.ConvertImplicit (
2520 ec, var, TypeManager.idisposable_type, loc);
2522 if (converted_vars [i] == null)
2530 bool old_in_try = ec.InTry;
2533 foreach (DictionaryEntry e in var_list){
2534 LocalVariableReference var = (LocalVariableReference) e.Key;
2535 Expression expr = (Expression) e.Value;
2538 a = new Assign (var, expr, loc);
2541 converted_vars [i] = var;
2547 ((ExpressionStatement) a).EmitStatement (ec);
2549 ig.BeginExceptionBlock ();
2554 Statement.Emit (ec);
2555 ec.InTry = old_in_try;
2557 bool old_in_finally = ec.InFinally;
2558 ec.InFinally = true;
2559 var_list.Reverse ();
2560 foreach (DictionaryEntry e in var_list){
2561 LocalVariableReference var = (LocalVariableReference) e.Key;
2562 Label skip = ig.DefineLabel ();
2565 ig.BeginFinallyBlock ();
2568 ig.Emit (OpCodes.Brfalse, skip);
2569 converted_vars [i].Emit (ec);
2570 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2571 ig.MarkLabel (skip);
2572 ig.EndExceptionBlock ();
2574 ec.InFinally = old_in_finally;
2579 bool EmitExpression (EmitContext ec, Expression expr)
2581 Type expr_type = expr.Type;
2582 Expression conv = null;
2584 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2585 conv = Expression.ConvertImplicit (
2586 ec, expr, TypeManager.idisposable_type, loc);
2593 // Make a copy of the expression and operate on that.
2595 ILGenerator ig = ec.ig;
2596 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2601 ig.Emit (OpCodes.Stloc, local_copy);
2603 bool old_in_try = ec.InTry;
2605 ig.BeginExceptionBlock ();
2606 Statement.Emit (ec);
2607 ec.InTry = old_in_try;
2609 Label skip = ig.DefineLabel ();
2610 bool old_in_finally = ec.InFinally;
2611 ig.BeginFinallyBlock ();
2612 ig.Emit (OpCodes.Ldloc, local_copy);
2613 ig.Emit (OpCodes.Brfalse, skip);
2614 ig.Emit (OpCodes.Ldloc, local_copy);
2615 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2616 ig.MarkLabel (skip);
2617 ec.InFinally = old_in_finally;
2618 ig.EndExceptionBlock ();
2623 public override bool Resolve (EmitContext ec)
2625 return Statement.Resolve (ec);
2628 public override bool Emit (EmitContext ec)
2630 if (expression_or_block is DictionaryEntry){
2631 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2632 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2634 return EmitLocalVariableDecls (ec, t, var_list);
2635 } if (expression_or_block is Expression){
2636 Expression e = (Expression) expression_or_block;
2642 return EmitExpression (ec, e);
2649 /// Implementation of the foreach C# statement
2651 public class Foreach : Statement {
2653 LocalVariableReference variable;
2655 Statement statement;
2657 public Foreach (string type, LocalVariableReference var, Expression expr,
2658 Statement stmt, Location l)
2661 this.variable = var;
2667 public override bool Resolve (EmitContext ec)
2669 expr = expr.Resolve (ec);
2670 return statement.Resolve (ec) && expr != null;
2674 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2676 static MethodInfo FetchMethodMoveNext (Type t)
2678 MemberInfo [] move_next_list;
2680 move_next_list = TypeContainer.FindMembers (
2681 t, MemberTypes.Method,
2682 BindingFlags.Public | BindingFlags.Instance,
2683 Type.FilterName, "MoveNext");
2684 if (move_next_list == null || move_next_list.Length == 0)
2687 foreach (MemberInfo m in move_next_list){
2688 MethodInfo mi = (MethodInfo) m;
2691 args = TypeManager.GetArgumentTypes (mi);
2692 if (args != null && args.Length == 0){
2693 if (mi.ReturnType == TypeManager.bool_type)
2701 // Retrieves a `public T get_Current ()' method from the Type `t'
2703 static MethodInfo FetchMethodGetCurrent (Type t)
2705 MemberInfo [] move_next_list;
2707 move_next_list = TypeContainer.FindMembers (
2708 t, MemberTypes.Method,
2709 BindingFlags.Public | BindingFlags.Instance,
2710 Type.FilterName, "get_Current");
2711 if (move_next_list == null || move_next_list.Length == 0)
2714 foreach (MemberInfo m in move_next_list){
2715 MethodInfo mi = (MethodInfo) m;
2718 args = TypeManager.GetArgumentTypes (mi);
2719 if (args != null && args.Length == 0)
2726 // This struct records the helper methods used by the Foreach construct
2728 class ForeachHelperMethods {
2729 public EmitContext ec;
2730 public MethodInfo get_enumerator;
2731 public MethodInfo move_next;
2732 public MethodInfo get_current;
2734 public ForeachHelperMethods (EmitContext ec)
2740 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2745 if (!(m is MethodInfo))
2748 if (m.Name != "GetEnumerator")
2751 MethodInfo mi = (MethodInfo) m;
2752 Type [] args = TypeManager.GetArgumentTypes (mi);
2754 if (args.Length != 0)
2757 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2758 EmitContext ec = hm.ec;
2761 // Check whether GetEnumerator is accessible to us
2763 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2765 Type declaring = mi.DeclaringType;
2766 if (prot == MethodAttributes.Private){
2767 if (declaring != ec.ContainerType)
2769 } else if (prot == MethodAttributes.FamANDAssem){
2770 // If from a different assembly, false
2771 if (!(mi is MethodBuilder))
2774 // Are we being invoked from the same class, or from a derived method?
2776 if (ec.ContainerType != declaring){
2777 if (!ec.ContainerType.IsSubclassOf (declaring))
2780 } else if (prot == MethodAttributes.FamORAssem){
2781 if (!(mi is MethodBuilder ||
2782 ec.ContainerType == declaring ||
2783 ec.ContainerType.IsSubclassOf (declaring)))
2785 } if (prot == MethodAttributes.Family){
2786 if (!(ec.ContainerType == declaring ||
2787 ec.ContainerType.IsSubclassOf (declaring)))
2792 // Ok, we can access it, now make sure that we can do something
2793 // with this `GetEnumerator'
2795 if (mi.ReturnType == TypeManager.ienumerator_type ||
2796 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2797 hm.move_next = TypeManager.bool_movenext_void;
2798 hm.get_current = TypeManager.object_getcurrent_void;
2803 // Ok, so they dont return an IEnumerable, we will have to
2804 // find if they support the GetEnumerator pattern.
2806 Type return_type = mi.ReturnType;
2808 hm.move_next = FetchMethodMoveNext (return_type);
2809 if (hm.move_next == null)
2811 hm.get_current = FetchMethodGetCurrent (return_type);
2812 if (hm.get_current == null)
2819 /// This filter is used to find the GetEnumerator method
2820 /// on which IEnumerator operates
2822 static MemberFilter FilterEnumerator;
2826 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2829 void error1579 (Type t)
2831 Report.Error (1579, loc,
2832 "foreach statement cannot operate on variables of type `" +
2833 t.FullName + "' because that class does not provide a " +
2834 " GetEnumerator method or it is inaccessible");
2837 static bool TryType (Type t, ForeachHelperMethods hm)
2841 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2842 BindingFlags.Public | BindingFlags.NonPublic |
2843 BindingFlags.Instance,
2844 FilterEnumerator, hm);
2846 if (mi == null || mi.Length == 0)
2849 hm.get_enumerator = (MethodInfo) mi [0];
2854 // Looks for a usable GetEnumerator in the Type, and if found returns
2855 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2857 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2859 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2861 if (TryType (t, hm))
2865 // Now try to find the method in the interfaces
2868 Type [] ifaces = t.GetInterfaces ();
2870 foreach (Type i in ifaces){
2871 if (TryType (i, hm))
2876 // Since TypeBuilder.GetInterfaces only returns the interface
2877 // types for this type, we have to keep looping, but once
2878 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2879 // done, because it returns all the types
2881 if ((t is TypeBuilder))
2891 // FIXME: possible optimization.
2892 // We might be able to avoid creating `empty' if the type is the sam
2894 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2896 ILGenerator ig = ec.ig;
2897 LocalBuilder enumerator, disposable;
2898 Expression empty = new EmptyExpression ();
2902 // FIXME: maybe we can apply the same trick we do in the
2903 // array handling to avoid creating empty and conv in some cases.
2905 // Although it is not as important in this case, as the type
2906 // will not likely be object (what the enumerator will return).
2908 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2912 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2913 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2916 // Instantiate the enumerator
2918 if (expr.Type.IsValueType){
2919 if (expr is IMemoryLocation){
2920 IMemoryLocation ml = (IMemoryLocation) expr;
2922 ml.AddressOf (ec, AddressOp.Load);
2924 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2925 " does not implement IMemoryLocation");
2926 ig.Emit (OpCodes.Call, hm.get_enumerator);
2929 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2931 ig.Emit (OpCodes.Stloc, enumerator);
2934 // Protect the code in a try/finalize block, so that
2935 // if the beast implement IDisposable, we get rid of it
2937 Label l = ig.BeginExceptionBlock ();
2938 bool old_in_try = ec.InTry;
2941 Label end_try = ig.DefineLabel ();
2943 ig.MarkLabel (ec.LoopBegin);
2944 ig.Emit (OpCodes.Ldloc, enumerator);
2945 ig.Emit (OpCodes.Callvirt, hm.move_next);
2946 ig.Emit (OpCodes.Brfalse, end_try);
2947 ig.Emit (OpCodes.Ldloc, enumerator);
2948 ig.Emit (OpCodes.Callvirt, hm.get_current);
2949 variable.EmitAssign (ec, conv);
2950 statement.Emit (ec);
2951 ig.Emit (OpCodes.Br, ec.LoopBegin);
2952 ig.MarkLabel (end_try);
2953 ec.InTry = old_in_try;
2955 // The runtime provides this for us.
2956 // ig.Emit (OpCodes.Leave, end);
2959 // Now the finally block
2961 Label end_finally = ig.DefineLabel ();
2962 bool old_in_finally = ec.InFinally;
2963 ec.InFinally = true;
2964 ig.BeginFinallyBlock ();
2966 ig.Emit (OpCodes.Ldloc, enumerator);
2967 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2968 ig.Emit (OpCodes.Stloc, disposable);
2969 ig.Emit (OpCodes.Ldloc, disposable);
2970 ig.Emit (OpCodes.Brfalse, end_finally);
2971 ig.Emit (OpCodes.Ldloc, disposable);
2972 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2973 ig.MarkLabel (end_finally);
2974 ec.InFinally = old_in_finally;
2976 // The runtime generates this anyways.
2977 // ig.Emit (OpCodes.Endfinally);
2979 ig.EndExceptionBlock ();
2981 ig.MarkLabel (ec.LoopEnd);
2986 // FIXME: possible optimization.
2987 // We might be able to avoid creating `empty' if the type is the sam
2989 bool EmitArrayForeach (EmitContext ec, Type var_type)
2991 Type array_type = expr.Type;
2992 Type element_type = array_type.GetElementType ();
2993 Expression conv = null;
2994 Expression empty = new EmptyExpression (element_type);
2996 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
3000 int rank = array_type.GetArrayRank ();
3001 ILGenerator ig = ec.ig;
3003 LocalBuilder copy = ig.DeclareLocal (array_type);
3006 // Make our copy of the array
3009 ig.Emit (OpCodes.Stloc, copy);
3012 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
3016 ig.Emit (OpCodes.Ldc_I4_0);
3017 ig.Emit (OpCodes.Stloc, counter);
3018 test = ig.DefineLabel ();
3019 ig.Emit (OpCodes.Br, test);
3021 loop = ig.DefineLabel ();
3022 ig.MarkLabel (loop);
3024 ig.Emit (OpCodes.Ldloc, copy);
3025 ig.Emit (OpCodes.Ldloc, counter);
3026 ArrayAccess.EmitLoadOpcode (ig, var_type);
3028 variable.EmitAssign (ec, conv);
3030 statement.Emit (ec);
3032 ig.MarkLabel (ec.LoopBegin);
3033 ig.Emit (OpCodes.Ldloc, counter);
3034 ig.Emit (OpCodes.Ldc_I4_1);
3035 ig.Emit (OpCodes.Add);
3036 ig.Emit (OpCodes.Stloc, counter);
3038 ig.MarkLabel (test);
3039 ig.Emit (OpCodes.Ldloc, counter);
3040 ig.Emit (OpCodes.Ldloc, copy);
3041 ig.Emit (OpCodes.Ldlen);
3042 ig.Emit (OpCodes.Conv_I4);
3043 ig.Emit (OpCodes.Blt, loop);
3045 LocalBuilder [] dim_len = new LocalBuilder [rank];
3046 LocalBuilder [] dim_count = new LocalBuilder [rank];
3047 Label [] loop = new Label [rank];
3048 Label [] test = new Label [rank];
3051 for (dim = 0; dim < rank; dim++){
3052 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
3053 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
3054 test [dim] = ig.DefineLabel ();
3055 loop [dim] = ig.DefineLabel ();
3058 for (dim = 0; dim < rank; dim++){
3059 ig.Emit (OpCodes.Ldloc, copy);
3060 IntLiteral.EmitInt (ig, dim);
3061 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
3062 ig.Emit (OpCodes.Stloc, dim_len [dim]);
3065 for (dim = 0; dim < rank; dim++){
3066 ig.Emit (OpCodes.Ldc_I4_0);
3067 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3068 ig.Emit (OpCodes.Br, test [dim]);
3069 ig.MarkLabel (loop [dim]);
3072 ig.Emit (OpCodes.Ldloc, copy);
3073 for (dim = 0; dim < rank; dim++)
3074 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3077 // FIXME: Maybe we can cache the computation of `get'?
3079 Type [] args = new Type [rank];
3082 for (int i = 0; i < rank; i++)
3083 args [i] = TypeManager.int32_type;
3085 ModuleBuilder mb = CodeGen.ModuleBuilder;
3086 get = mb.GetArrayMethod (
3088 CallingConventions.HasThis| CallingConventions.Standard,
3090 ig.Emit (OpCodes.Call, get);
3091 variable.EmitAssign (ec, conv);
3092 statement.Emit (ec);
3093 ig.MarkLabel (ec.LoopBegin);
3094 for (dim = rank - 1; dim >= 0; dim--){
3095 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3096 ig.Emit (OpCodes.Ldc_I4_1);
3097 ig.Emit (OpCodes.Add);
3098 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3100 ig.MarkLabel (test [dim]);
3101 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3102 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
3103 ig.Emit (OpCodes.Blt, loop [dim]);
3106 ig.MarkLabel (ec.LoopEnd);
3111 public override bool Emit (EmitContext ec)
3116 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
3117 if (var_type == null)
3121 // We need an instance variable. Not sure this is the best
3122 // way of doing this.
3124 // FIXME: When we implement propertyaccess, will those turn
3125 // out to return values in ExprClass? I think they should.
3127 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3128 expr.eclass == ExprClass.PropertyAccess)){
3129 error1579 (expr.Type);
3133 ILGenerator ig = ec.ig;
3135 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
3136 bool old_inloop = ec.InLoop;
3137 ec.LoopBegin = ig.DefineLabel ();
3138 ec.LoopEnd = ig.DefineLabel ();
3141 if (expr.Type.IsArray)
3142 ret_val = EmitArrayForeach (ec, var_type);
3144 ForeachHelperMethods hm;
3146 hm = ProbeCollectionType (ec, expr.Type);
3148 error1579 (expr.Type);
3152 ret_val = EmitCollectionForeach (ec, var_type, hm);
3155 ec.LoopBegin = old_begin;
3156 ec.LoopEnd = old_end;
3157 ec.InLoop = old_inloop;