2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
15 namespace Mono.CSharp {
17 using System.Collections;
19 public abstract class Statement {
22 /// Return value indicates whether all code paths emitted return.
24 public abstract bool Emit (EmitContext ec);
27 /// Emits a bool expression.
29 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
36 if (e.Type != TypeManager.bool_type)
37 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
42 31, "Can not convert the expression to a boolean");
50 if (u.Oper == Unary.Operator.LogicalNot){
53 u.EmitLogicalNot (ec);
62 ec.ig.Emit (OpCodes.Brfalse, l);
64 ec.ig.Emit (OpCodes.Brtrue, l);
67 ec.ig.Emit (OpCodes.Brtrue, l);
69 ec.ig.Emit (OpCodes.Brfalse, l);
77 public class EmptyStatement : Statement {
78 public override bool Emit (EmitContext ec)
84 public class If : Statement {
85 public readonly Expression Expr;
86 public readonly Statement TrueStatement;
87 public readonly Statement FalseStatement;
89 public If (Expression expr, Statement trueStatement)
92 TrueStatement = trueStatement;
95 public If (Expression expr,
96 Statement trueStatement,
97 Statement falseStatement)
100 TrueStatement = trueStatement;
101 FalseStatement = falseStatement;
104 public override bool Emit (EmitContext ec)
106 ILGenerator ig = ec.ig;
107 Label false_target = ig.DefineLabel ();
109 bool is_true_ret, is_false_ret;
111 if (!EmitBoolExpression (ec, Expr, false_target, false))
114 is_true_ret = TrueStatement.Emit (ec);
115 is_false_ret = is_true_ret;
117 if (FalseStatement != null){
118 bool branch_emitted = false;
120 end = ig.DefineLabel ();
122 ig.Emit (OpCodes.Br, end);
123 branch_emitted = true;
126 ig.MarkLabel (false_target);
127 is_false_ret = FalseStatement.Emit (ec);
132 ig.MarkLabel (false_target);
133 is_false_ret = false;
136 return is_true_ret && is_false_ret;
140 public class Do : Statement {
141 public readonly Expression Expr;
142 public readonly Statement EmbeddedStatement;
144 public Do (Statement statement, Expression boolExpr)
147 EmbeddedStatement = statement;
150 public override bool Emit (EmitContext ec)
152 ILGenerator ig = ec.ig;
153 Label loop = ig.DefineLabel ();
154 Label old_begin = ec.LoopBegin;
155 Label old_end = ec.LoopEnd;
156 bool old_inloop = ec.InLoop;
158 ec.LoopBegin = ig.DefineLabel ();
159 ec.LoopEnd = ig.DefineLabel ();
163 EmbeddedStatement.Emit (ec);
164 ig.MarkLabel (ec.LoopBegin);
165 EmitBoolExpression (ec, Expr, loop, true);
166 ig.MarkLabel (ec.LoopEnd);
168 ec.LoopBegin = old_begin;
169 ec.LoopEnd = old_end;
170 ec.InLoop = old_inloop;
176 public class While : Statement {
177 public readonly Expression Expr;
178 public readonly Statement Statement;
180 public While (Expression boolExpr, Statement statement)
183 Statement = statement;
186 public override bool Emit (EmitContext ec)
188 ILGenerator ig = ec.ig;
189 Label old_begin = ec.LoopBegin;
190 Label old_end = ec.LoopEnd;
191 bool old_inloop = ec.InLoop;
193 ec.LoopBegin = ig.DefineLabel ();
194 ec.LoopEnd = ig.DefineLabel ();
197 ig.MarkLabel (ec.LoopBegin);
198 EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
200 ig.Emit (OpCodes.Br, ec.LoopBegin);
201 ig.MarkLabel (ec.LoopEnd);
203 ec.LoopBegin = old_begin;
204 ec.LoopEnd = old_end;
205 ec.InLoop = old_inloop;
211 public class For : Statement {
212 public readonly Statement InitStatement;
213 public readonly Expression Test;
214 public readonly Statement Increment;
215 public readonly Statement Statement;
217 public For (Statement initStatement,
222 InitStatement = initStatement;
224 Increment = increment;
225 Statement = statement;
228 public override bool Emit (EmitContext ec)
230 ILGenerator ig = ec.ig;
231 Label old_begin = ec.LoopBegin;
232 Label old_end = ec.LoopEnd;
233 bool old_inloop = ec.InLoop;
234 Label loop = ig.DefineLabel ();
236 if (InitStatement != null)
237 if (! (InitStatement is EmptyStatement))
238 InitStatement.Emit (ec);
240 ec.LoopBegin = ig.DefineLabel ();
241 ec.LoopEnd = ig.DefineLabel ();
247 // If test is null, there is no test, and we are just
251 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
254 ig.MarkLabel (ec.LoopBegin);
255 if (!(Increment is EmptyStatement))
257 ig.Emit (OpCodes.Br, loop);
258 ig.MarkLabel (ec.LoopEnd);
260 ec.LoopBegin = old_begin;
261 ec.LoopEnd = old_end;
262 ec.InLoop = old_inloop;
267 public class StatementExpression : Statement {
268 public readonly ExpressionStatement Expr;
270 public StatementExpression (ExpressionStatement expr)
275 public override bool Emit (EmitContext ec)
277 ILGenerator ig = ec.ig;
280 ne = Expr.Resolve (ec);
282 if (ne is ExpressionStatement)
283 ((ExpressionStatement) ne).EmitStatement (ec);
286 ig.Emit (OpCodes.Pop);
293 public override string ToString ()
295 return "StatementExpression (" + Expr + ")";
300 /// Implements the return statement
302 public class Return : Statement {
303 public Expression Expr;
304 public readonly Location loc;
306 public Return (Expression expr, Location l)
312 public override bool Emit (EmitContext ec)
315 Report.Error (157,loc,"Control can not leave the body of the finally block");
319 if (ec.ReturnType == null){
321 Report.Error (127, loc, "Return with a value not allowed here");
326 Report.Error (126, loc, "An object of type `" +
327 TypeManager.CSharpName (ec.ReturnType) + "' is " +
328 "expected for the return statement");
332 Expr = Expr.Resolve (ec);
336 if (Expr.Type != ec.ReturnType)
337 Expr = Expression.ConvertImplicitRequired (
338 ec, Expr, ec.ReturnType, loc);
345 if (ec.InTry || ec.InCatch)
346 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
349 if (ec.InTry || ec.InCatch){
350 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
353 ec.ig.Emit (OpCodes.Ret);
359 public class Goto : Statement {
364 public Goto (Block parent_block, string label, Location l)
366 block = parent_block;
371 public string Target {
377 public override bool Emit (EmitContext ec)
379 LabeledStatement label = block.LookupLabel (target);
383 // Maybe we should catch this before?
387 "No such label `" + target + "' in this scope");
390 Label l = label.LabelTarget (ec);
391 ec.ig.Emit (OpCodes.Br, l);
397 public class LabeledStatement : Statement {
402 public LabeledStatement (string label_name)
404 this.label_name = label_name;
407 public Label LabelTarget (EmitContext ec)
411 label = ec.ig.DefineLabel ();
417 public override bool Emit (EmitContext ec)
420 ec.ig.MarkLabel (label);
428 /// `goto default' statement
430 public class GotoDefault : Statement {
433 public GotoDefault (Location l)
438 public override bool Emit (EmitContext ec)
440 if (ec.Switch == null){
441 Report.Error (153, loc, "goto default is only valid in a switch statement");
445 if (!ec.Switch.GotDefault){
446 Report.Error (159, loc, "No default target on switch statement");
449 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
455 /// `goto case' statement
457 public class GotoCase : Statement {
461 public GotoCase (Expression e, Location l)
467 public override bool Emit (EmitContext ec)
469 if (ec.Switch == null){
470 Report.Error (153, loc, "goto case is only valid in a switch statement");
474 expr = expr.Resolve (ec);
478 if (!(expr is Constant)){
479 Report.Error (159, loc, "Target expression for goto case is not constant");
483 object val = Expression.ConvertIntLiteral (
484 (Constant) expr, ec.Switch.SwitchType, loc);
489 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
494 "No such label 'case " + val + "': for the goto case");
497 ec.ig.Emit (OpCodes.Br, sl.ILLabel);
502 public class Throw : Statement {
503 public readonly Expression Expr;
506 public Throw (Expression expr, Location l)
512 public override bool Emit (EmitContext ec)
516 ec.ig.Emit (OpCodes.Rethrow);
520 "A throw statement with no argument is only " +
521 "allowed in a catch clause");
526 Expression e = Expr.Resolve (ec);
533 ec.ig.Emit (OpCodes.Throw);
539 public class Break : Statement {
542 public Break (Location l)
547 public override bool Emit (EmitContext ec)
549 ILGenerator ig = ec.ig;
551 if (ec.InLoop == false && ec.Switch == null){
552 Report.Error (139, loc, "No enclosing loop or switch to continue to");
556 ig.Emit (OpCodes.Br, ec.LoopEnd);
561 public class Continue : Statement {
564 public Continue (Location l)
569 public override bool Emit (EmitContext ec)
571 Label begin = ec.LoopBegin;
574 Report.Error (139, loc, "No enclosing loop to continue to");
579 // UGH: Non trivial. This Br might cross a try/catch boundary
583 // try { ... } catch { continue; }
587 // try {} catch { while () { continue; }}
589 ec.ig.Emit (OpCodes.Br, begin);
594 public class VariableInfo {
595 public readonly string Type;
596 public LocalBuilder LocalBuilder;
597 public Type VariableType;
598 public readonly Location Location;
602 public bool Assigned;
603 public bool ReadOnly;
605 public VariableInfo (string type, Location l)
616 throw new Exception ("Unassigned idx for variable");
629 /// Block represents a C# block.
633 /// This class is used in a number of places: either to represent
634 /// explicit blocks that the programmer places or implicit blocks.
636 /// Implicit blocks are used as labels or to introduce variable
639 public class Block : Statement {
640 public readonly Block Parent;
641 public readonly bool Implicit;
644 // The statements in this block
646 StatementCollection statements;
649 // An array of Blocks. We keep track of children just
650 // to generate the local variable declarations.
652 // Statements and child statements are handled through the
658 // Labels. (label, block) pairs.
663 // Keeps track of (name, type) pairs
668 // Keeps track of constants
672 // Maps variable names to ILGenerator.LocalBuilders
674 Hashtable local_builders;
682 public Block (Block parent)
685 parent.AddChild (this);
687 this.Parent = parent;
688 this.Implicit = false;
693 public Block (Block parent, bool implicit_block)
696 parent.AddChild (this);
698 this.Parent = parent;
699 this.Implicit = true;
709 void AddChild (Block b)
711 if (children == null)
712 children = new ArrayList ();
718 /// Adds a label to the current block.
722 /// false if the name already exists in this block. true
726 public bool AddLabel (string name, LabeledStatement target)
729 labels = new Hashtable ();
730 if (labels.Contains (name))
733 labels.Add (name, target);
737 public LabeledStatement LookupLabel (string name)
740 if (labels.Contains (name))
741 return ((LabeledStatement) labels [name]);
745 return Parent.LookupLabel (name);
750 public bool AddVariable (string type, string name, Parameters pars, Location l)
752 if (variables == null)
753 variables = new Hashtable ();
755 if (GetVariableType (name) != null)
760 Parameter p = pars.GetParameterByName (name, out idx);
765 VariableInfo vi = new VariableInfo (type, l);
767 variables.Add (name, vi);
772 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
774 if (!AddVariable (type, name, pars, l))
777 if (constants == null)
778 constants = new Hashtable ();
780 constants.Add (name, value);
785 public Hashtable Variables {
791 public VariableInfo GetVariableInfo (string name)
793 if (variables != null) {
795 temp = variables [name];
798 return (VariableInfo) temp;
803 return Parent.GetVariableInfo (name);
808 public string GetVariableType (string name)
810 VariableInfo vi = GetVariableInfo (name);
818 public Expression GetConstantExpression (string name)
820 if (constants != null) {
822 temp = constants [name];
825 return (Expression) temp;
829 return Parent.GetConstantExpression (name);
835 /// True if the variable named @name has been defined
838 public bool IsVariableDefined (string name)
840 if (variables != null) {
841 if (variables.Contains (name))
846 return Parent.IsVariableDefined (name);
852 /// True if the variable named @name is a constant
854 public bool IsConstant (string name)
858 e = GetConstantExpression (name);
864 /// Use to fetch the statement associated with this label
866 public Statement this [string name] {
868 return (Statement) labels [name];
873 /// A list of labels that were not used within this block
875 public string [] GetUnreferenced ()
877 // FIXME: Implement me
881 public StatementCollection Statements {
883 if (statements == null)
884 statements = new StatementCollection ();
890 public void AddStatement (Statement s)
892 if (statements == null)
893 statements = new StatementCollection ();
911 /// Emits the variable declarations and labels.
914 /// tc: is our typecontainer (to resolve type references)
915 /// ig: is the code generator:
916 /// toplevel: the toplevel block. This is used for checking
917 /// that no two labels with the same name are used.
919 public int EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
922 // Process this block variables
924 if (variables != null){
925 local_builders = new Hashtable ();
927 foreach (DictionaryEntry de in variables){
928 string name = (string) de.Key;
929 VariableInfo vi = (VariableInfo) de.Value;
932 t = RootContext.LookupType (tc, vi.Type, false, vi.Location);
937 vi.LocalBuilder = ig.DeclareLocal (t);
943 // Now, handle the children
945 if (children != null){
946 foreach (Block b in children)
947 count = b.EmitMeta (tc, ig, toplevel, count);
953 public void UsageWarning ()
957 if (variables != null){
958 foreach (DictionaryEntry de in variables){
959 VariableInfo vi = (VariableInfo) de.Value;
964 name = (string) de.Key;
968 219, vi.Location, "The variable `" + name +
969 "' is assigned but its value is never used");
972 168, vi.Location, "The variable `" +
974 "' is declared but never used");
979 if (children != null)
980 foreach (Block b in children)
986 public override bool Emit (EmitContext ec)
989 Block prev_block = ec.CurrentBlock;
992 ec.CurrentBlock = this;
994 // throw new Exception ();
995 foreach (Statement s in Statements)
996 is_ret = s.Emit (ec);
999 ec.CurrentBlock = prev_block;
1004 public class SwitchLabel {
1007 public Location loc;
1008 public Label ILLabel;
1011 // if expr == null, then it is the default case.
1013 public SwitchLabel (Expression expr, Location l)
1019 public Expression Label {
1025 public object Converted {
1032 // Resolves the expression, reduces it to a literal if possible
1033 // and then converts it to the requested type.
1035 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1037 ILLabel = ec.ig.DefineLabel ();
1042 Expression e = label.Resolve (ec);
1047 if (!(e is Constant)){
1048 Console.WriteLine ("Value is: " + label);
1049 Report.Error (150, loc, "A constant value is expected");
1053 if (e is StringConstant || e is NullLiteral){
1054 if (required_type == TypeManager.string_type){
1056 ILLabel = ec.ig.DefineLabel ();
1061 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1062 if (converted == null)
1069 public class SwitchSection {
1070 // An array of SwitchLabels.
1071 public readonly ArrayList Labels;
1072 public readonly Block Block;
1074 public SwitchSection (ArrayList labels, Block block)
1081 public class Switch : Statement {
1082 public readonly ArrayList Sections;
1083 public Expression Expr;
1086 /// Maps constants whose type type SwitchType to their SwitchLabels.
1088 public Hashtable Elements;
1091 /// The governing switch type
1093 public Type SwitchType;
1099 Label default_target;
1103 // The types allowed to be implicitly cast from
1104 // on the governing type
1106 static Type [] allowed_types;
1108 public Switch (Expression e, ArrayList sects, Location l)
1115 public bool GotDefault {
1121 public Label DefaultTarget {
1123 return default_target;
1128 // Determines the governing type for a switch. The returned
1129 // expression might be the expression from the switch, or an
1130 // expression that includes any potential conversions to the
1131 // integral types or to string.
1133 Expression SwitchGoverningType (EmitContext ec, Type t)
1135 if (t == TypeManager.int32_type ||
1136 t == TypeManager.uint32_type ||
1137 t == TypeManager.char_type ||
1138 t == TypeManager.byte_type ||
1139 t == TypeManager.sbyte_type ||
1140 t == TypeManager.ushort_type ||
1141 t == TypeManager.short_type ||
1142 t == TypeManager.uint64_type ||
1143 t == TypeManager.int64_type ||
1144 t == TypeManager.string_type ||
1145 t.IsSubclassOf (TypeManager.enum_type))
1148 if (allowed_types == null){
1149 allowed_types = new Type [] {
1150 TypeManager.sbyte_type,
1151 TypeManager.byte_type,
1152 TypeManager.short_type,
1153 TypeManager.ushort_type,
1154 TypeManager.int32_type,
1155 TypeManager.uint32_type,
1156 TypeManager.int64_type,
1157 TypeManager.uint64_type,
1158 TypeManager.char_type,
1159 TypeManager.string_type
1164 // Try to find a *user* defined implicit conversion.
1166 // If there is no implicit conversion, or if there are multiple
1167 // conversions, we have to report an error
1169 Expression converted = null;
1170 foreach (Type tt in allowed_types){
1173 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1177 if (converted != null){
1178 Report.Error (-12, loc, "More than one conversion to an integral " +
1179 " type exists for type `" +
1180 TypeManager.CSharpName (Expr.Type)+"'");
1187 void error152 (string n)
1190 152, "The label `" + n + ":' " +
1191 "is already present on this switch statement");
1195 // Performs the basic sanity checks on the switch statement
1196 // (looks for duplicate keys and non-constant expressions).
1198 // It also returns a hashtable with the keys that we will later
1199 // use to compute the switch tables
1201 bool CheckSwitch (EmitContext ec)
1205 Elements = new Hashtable ();
1207 got_default = false;
1209 if (TypeManager.IsEnumType (SwitchType)){
1210 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1212 compare_type = SwitchType;
1214 foreach (SwitchSection ss in Sections){
1215 foreach (SwitchLabel sl in ss.Labels){
1216 if (!sl.ResolveAndReduce (ec, SwitchType)){
1221 if (sl.Label == null){
1223 error152 ("default");
1230 object key = sl.Converted;
1232 if (key is Constant)
1233 key = ((Constant) key).GetValue ();
1236 key = NullLiteral.Null;
1238 string lname = null;
1239 if (compare_type == TypeManager.uint64_type){
1240 ulong v = (ulong) key;
1242 if (Elements.Contains (v))
1243 lname = v.ToString ();
1245 Elements.Add (v, sl);
1246 } else if (compare_type == TypeManager.int64_type){
1247 long v = (long) key;
1249 if (Elements.Contains (v))
1250 lname = v.ToString ();
1252 Elements.Add (v, sl);
1253 } else if (compare_type == TypeManager.uint32_type){
1254 uint v = (uint) key;
1256 if (Elements.Contains (v))
1257 lname = v.ToString ();
1259 Elements.Add (v, sl);
1260 } else if (compare_type == TypeManager.char_type){
1261 char v = (char) key;
1263 if (Elements.Contains (v))
1264 lname = v.ToString ();
1266 Elements.Add (v, sl);
1267 } else if (compare_type == TypeManager.byte_type){
1268 byte v = (byte) key;
1270 if (Elements.Contains (v))
1271 lname = v.ToString ();
1273 Elements.Add (v, sl);
1274 } else if (compare_type == TypeManager.sbyte_type){
1275 sbyte v = (sbyte) key;
1277 if (Elements.Contains (v))
1278 lname = v.ToString ();
1280 Elements.Add (v, sl);
1281 } else if (compare_type == TypeManager.short_type){
1282 short v = (short) key;
1284 if (Elements.Contains (v))
1285 lname = v.ToString ();
1287 Elements.Add (v, sl);
1288 } else if (compare_type == TypeManager.ushort_type){
1289 ushort v = (ushort) key;
1291 if (Elements.Contains (v))
1292 lname = v.ToString ();
1294 Elements.Add (v, sl);
1295 } else if (compare_type == TypeManager.string_type){
1296 if (key is NullLiteral){
1297 if (Elements.Contains (NullLiteral.Null))
1300 Elements.Add (NullLiteral.Null, null);
1302 string s = (string) key;
1304 if (Elements.Contains (s))
1307 Elements.Add (s, sl);
1309 } else if (compare_type == TypeManager.int32_type) {
1312 if (Elements.Contains (v))
1313 lname = v.ToString ();
1315 Elements.Add (v, sl);
1317 throw new Exception ("Unknown switch type!" +
1318 SwitchType + " " + compare_type);
1322 error152 ("case + " + lname);
1333 void EmitObjectInteger (ILGenerator ig, object k)
1336 IntConstant.EmitInt (ig, (int) k);
1337 else if (k is Constant){
1338 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1339 } else if (k is uint)
1340 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1342 LongConstant.EmitLong (ig, (long) k);
1343 else if (k is ulong)
1344 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1346 IntConstant.EmitInt (ig, (int) ((char) k));
1347 else if (k is sbyte)
1348 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1350 IntConstant.EmitInt (ig, (int) ((byte) k));
1352 throw new Exception ("Unhandled case");
1356 // This simple emit switch works, but does not take advantage of the
1357 // `switch' opcode. The swithc opcode uses a jump table that we are not
1358 // computing at this point
1360 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1362 ILGenerator ig = ec.ig;
1363 Label end_of_switch = ig.DefineLabel ();
1364 Label next_test = ig.DefineLabel ();
1365 Label null_target = ig.DefineLabel ();
1366 bool default_found = false;
1367 bool first_test = true;
1368 bool pending_goto_end = false;
1369 bool all_return = true;
1370 bool is_string = false;
1374 // Special processing for strings: we cant compare
1377 if (SwitchType == TypeManager.string_type){
1378 ig.Emit (OpCodes.Ldloc, val);
1381 if (Elements.Contains (NullLiteral.Null)){
1382 ig.Emit (OpCodes.Brfalse, null_target);
1384 ig.Emit (OpCodes.Brfalse, default_target);
1386 ig.Emit (OpCodes.Ldloc, val);
1387 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1388 ig.Emit (OpCodes.Stloc, val);
1391 foreach (SwitchSection ss in Sections){
1392 Label sec_begin = ig.DefineLabel ();
1394 if (pending_goto_end)
1395 ig.Emit (OpCodes.Br, end_of_switch);
1397 int label_count = ss.Labels.Count;
1399 foreach (SwitchLabel sl in ss.Labels){
1400 ig.MarkLabel (sl.ILLabel);
1403 ig.MarkLabel (next_test);
1404 next_test = ig.DefineLabel ();
1407 // If we are the default target
1409 if (sl.Label == null){
1410 ig.MarkLabel (default_target);
1411 default_found = true;
1413 object lit = sl.Converted;
1415 if (lit is NullLiteral){
1417 if (label_count == 1)
1418 ig.Emit (OpCodes.Br, next_test);
1423 StringConstant str = (StringConstant) lit;
1425 ig.Emit (OpCodes.Ldloc, val);
1426 ig.Emit (OpCodes.Ldstr, str.Value);
1427 if (label_count == 1)
1428 ig.Emit (OpCodes.Bne_Un, next_test);
1430 ig.Emit (OpCodes.Beq, sec_begin);
1432 ig.Emit (OpCodes.Ldloc, val);
1433 EmitObjectInteger (ig, lit);
1434 ig.Emit (OpCodes.Ceq);
1435 if (label_count == 1)
1436 ig.Emit (OpCodes.Brfalse, next_test);
1438 ig.Emit (OpCodes.Brtrue, sec_begin);
1442 if (label_count != 1)
1443 ig.Emit (OpCodes.Br, next_test);
1446 ig.MarkLabel (null_target);
1447 ig.MarkLabel (sec_begin);
1448 if (ss.Block.Emit (ec))
1449 pending_goto_end = false;
1452 pending_goto_end = true;
1457 ig.MarkLabel (default_target);
1458 ig.MarkLabel (next_test);
1459 ig.MarkLabel (end_of_switch);
1464 public override bool Emit (EmitContext ec)
1466 Expr = Expr.Resolve (ec);
1470 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1471 if (new_expr == null){
1472 Report.Error (151, loc, "An integer type or string was expected for switch");
1477 SwitchType = new_expr.Type;
1479 if (!CheckSwitch (ec))
1482 // Store variable for comparission purposes
1483 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1485 ec.ig.Emit (OpCodes.Stloc, value);
1487 ILGenerator ig = ec.ig;
1489 default_target = ig.DefineLabel ();
1492 // Setup the codegen context
1494 Label old_end = ec.LoopEnd;
1495 Switch old_switch = ec.Switch;
1497 ec.LoopEnd = ig.DefineLabel ();
1501 bool all_return = SimpleSwitchEmit (ec, value);
1503 // Restore context state.
1504 ig.MarkLabel (ec.LoopEnd);
1507 // FIXME: I am emitting a nop, because the switch performs
1508 // no analysis on whether something ever reaches the end
1510 // try: b (int a) { switch (a) { default: return 0; } }
1511 ig.Emit (OpCodes.Nop);
1514 // Restore the previous context
1516 ec.LoopEnd = old_end;
1517 ec.Switch = old_switch;
1520 // Because we have a nop at the end
1526 public class Lock : Statement {
1527 public readonly Expression Expr;
1528 public readonly Statement Statement;
1531 public Lock (Expression expr, Statement stmt, Location l)
1538 public override bool Emit (EmitContext ec)
1540 Expression e = Expr.Resolve (ec);
1546 if (type.IsValueType){
1547 Report.Error (185, loc, "lock statement requires the expression to be " +
1548 " a reference type (type is: `" +
1549 TypeManager.CSharpName (type) + "'");
1553 ILGenerator ig = ec.ig;
1554 LocalBuilder temp = ig.DeclareLocal (type);
1557 ig.Emit (OpCodes.Dup);
1558 ig.Emit (OpCodes.Stloc, temp);
1559 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1562 Label end = ig.BeginExceptionBlock ();
1563 bool old_in_try = ec.InTry;
1565 Label finish = ig.DefineLabel ();
1566 Statement.Emit (ec);
1567 ec.InTry = old_in_try;
1568 // ig.Emit (OpCodes.Leave, finish);
1570 ig.MarkLabel (finish);
1573 ig.BeginFinallyBlock ();
1574 ig.Emit (OpCodes.Ldloc, temp);
1575 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1576 ig.EndExceptionBlock ();
1582 public class Unchecked : Statement {
1583 public readonly Block Block;
1585 public Unchecked (Block b)
1590 public override bool Emit (EmitContext ec)
1592 bool previous_state = ec.CheckState;
1595 ec.CheckState = false;
1596 val = Block.Emit (ec);
1597 ec.CheckState = previous_state;
1603 public class Checked : Statement {
1604 public readonly Block Block;
1606 public Checked (Block b)
1611 public override bool Emit (EmitContext ec)
1613 bool previous_state = ec.CheckState;
1616 ec.CheckState = true;
1617 val = Block.Emit (ec);
1618 ec.CheckState = previous_state;
1624 public class Unsafe : Statement {
1625 public readonly Block Block;
1627 public Unsafe (Block b)
1632 public override bool Emit (EmitContext ec)
1634 bool previous_state = ec.InUnsafe;
1638 val = Block.Emit (ec);
1639 ec.InUnsafe = previous_state;
1645 public class Unsafe : Statement {
1646 public readonly Block Block;
1648 public Unsafe (Block b)
1653 public override bool Emit (EmitContext ec)
1655 bool previous_state = ec.InUnsafe;
1659 val = Block.Emit (ec);
1660 ec.InUnsafe = previous_state;
1666 public class Catch {
1667 public readonly string Type;
1668 public readonly string Name;
1669 public readonly Block Block;
1670 public readonly Location Location;
1672 public Catch (string type, string name, Block block, Location l)
1681 public class Try : Statement {
1682 public readonly Block Fini, Block;
1683 public readonly ArrayList Specific;
1684 public readonly Catch General;
1687 // specific, general and fini might all be null.
1689 public Try (Block block, ArrayList specific, Catch general, Block fini)
1691 if (specific == null && general == null){
1692 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1696 this.Specific = specific;
1697 this.General = general;
1701 public override bool Emit (EmitContext ec)
1703 ILGenerator ig = ec.ig;
1705 Label finish = ig.DefineLabel ();;
1708 end = ig.BeginExceptionBlock ();
1709 bool old_in_try = ec.InTry;
1711 returns = Block.Emit (ec);
1712 ec.InTry = old_in_try;
1715 // System.Reflection.Emit provides this automatically:
1716 // ig.Emit (OpCodes.Leave, finish);
1718 bool old_in_catch = ec.InCatch;
1720 DeclSpace ds = ec.TypeContainer;
1722 foreach (Catch c in Specific){
1723 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1726 if (catch_type == null)
1729 ig.BeginCatchBlock (catch_type);
1731 if (c.Name != null){
1732 vi = c.Block.GetVariableInfo (c.Name);
1734 Console.WriteLine ("This should not happen! variable does not exist in this block");
1735 Environment.Exit (0);
1738 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1740 ig.Emit (OpCodes.Pop);
1745 if (General != null){
1746 ig.BeginCatchBlock (TypeManager.object_type);
1747 ig.Emit (OpCodes.Pop);
1748 General.Block.Emit (ec);
1750 ec.InCatch = old_in_catch;
1752 ig.MarkLabel (finish);
1754 ig.BeginFinallyBlock ();
1755 bool old_in_finally = ec.InFinally;
1756 ec.InFinally = true;
1758 ec.InFinally = old_in_finally;
1761 ig.EndExceptionBlock ();
1764 // FIXME: Is this correct?
1765 // Replace with `returns' and check test-18, maybe we can
1766 // perform an optimization here.
1773 // FIXME: We still do not support the expression variant of the using
1776 public class Using : Statement {
1777 object expression_or_block;
1778 Statement Statement;
1781 public Using (object expression_or_block, Statement stmt, Location l)
1783 this.expression_or_block = expression_or_block;
1789 // Emits the code for the case of using using a local variable declaration.
1791 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
1793 ILGenerator ig = ec.ig;
1794 Expression [] converted_vars;
1795 bool need_conv = false;
1796 Type type = RootContext.LookupType (ec.TypeContainer, type_name, false, loc);
1803 // The type must be an IDisposable or an implicit conversion
1806 converted_vars = new Expression [var_list.Count];
1807 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
1808 foreach (DictionaryEntry e in var_list){
1809 Expression var = (Expression) e.Key;
1811 var = var.Resolve (ec);
1815 converted_vars [i] = Expression.ConvertImplicit (
1816 ec, var, TypeManager.idisposable_type, loc);
1818 if (converted_vars [i] == null)
1826 bool old_in_try = ec.InTry;
1828 foreach (DictionaryEntry e in var_list){
1829 LocalVariableReference var = (LocalVariableReference) e.Key;
1830 Expression expr = (Expression) e.Value;
1833 a = new Assign (var, expr, loc);
1836 converted_vars [i] = var;
1840 ((ExpressionStatement) a).EmitStatement (ec);
1842 ig.BeginExceptionBlock ();
1845 Statement.Emit (ec);
1846 ec.InTry = old_in_try;
1848 bool old_in_finally = ec.InFinally;
1849 ec.InFinally = true;
1850 var_list.Reverse ();
1851 foreach (DictionaryEntry e in var_list){
1852 LocalVariableReference var = (LocalVariableReference) e.Key;
1853 Label skip = ig.DefineLabel ();
1856 ig.BeginFinallyBlock ();
1859 ig.Emit (OpCodes.Brfalse, skip);
1860 converted_vars [i].Emit (ec);
1861 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1862 ig.MarkLabel (skip);
1863 ig.EndExceptionBlock ();
1865 ec.InFinally = old_in_finally;
1870 bool EmitExpression (EmitContext ec, Expression expr)
1872 Type expr_type = expr.Type;
1873 Expression conv = null;
1875 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
1876 conv = Expression.ConvertImplicit (
1877 ec, expr, TypeManager.idisposable_type, loc);
1884 // Make a copy of the expression and operate on that.
1886 ILGenerator ig = ec.ig;
1887 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
1892 ig.Emit (OpCodes.Stloc, local_copy);
1894 bool old_in_try = ec.InTry;
1896 ig.BeginExceptionBlock ();
1897 Statement.Emit (ec);
1898 ec.InTry = old_in_try;
1900 Label skip = ig.DefineLabel ();
1901 bool old_in_finally = ec.InFinally;
1902 ig.BeginFinallyBlock ();
1903 ig.Emit (OpCodes.Ldloc, local_copy);
1904 ig.Emit (OpCodes.Brfalse, skip);
1905 ig.Emit (OpCodes.Ldloc, local_copy);
1906 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1907 ig.MarkLabel (skip);
1908 ec.InFinally = old_in_finally;
1909 ig.EndExceptionBlock ();
1914 public override bool Emit (EmitContext ec)
1916 if (expression_or_block is DictionaryEntry){
1917 string t = (string) ((DictionaryEntry) expression_or_block).Key;
1918 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
1920 return EmitLocalVariableDecls (ec, t, var_list);
1921 } if (expression_or_block is Expression){
1922 Expression e = (Expression) expression_or_block;
1928 return EmitExpression (ec, e);
1935 /// Implementation of the foreach C# statement
1937 public class Foreach : Statement {
1939 LocalVariableReference variable;
1941 Statement statement;
1944 public Foreach (string type, LocalVariableReference var, Expression expr,
1945 Statement stmt, Location l)
1948 this.variable = var;
1954 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1959 if (!(m is MethodInfo))
1962 if (m.Name != "GetEnumerator")
1965 MethodInfo mi = (MethodInfo) m;
1967 if (mi.ReturnType != TypeManager.ienumerator_type){
1968 if (!TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType))
1972 Type [] args = TypeManager.GetArgumentTypes (mi);
1976 if (args.Length == 0)
1983 /// This filter is used to find the GetEnumerator method
1984 /// on which IEnumerator operates
1986 static MemberFilter FilterEnumerator;
1990 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1993 void error1579 (Type t)
1995 Report.Error (1579, loc,
1996 "foreach statement cannot operate on variables of type `" +
1997 t.FullName + "' because that class does not provide a " +
1998 " GetEnumerator method or it is inaccessible");
2001 MethodInfo ProbeCollectionType (Type t)
2005 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2006 BindingFlags.Public | BindingFlags.Instance,
2007 FilterEnumerator, null);
2014 if (mi.Length == 0){
2019 return (MethodInfo) mi [0];
2023 // FIXME: possible optimization.
2024 // We might be able to avoid creating `empty' if the type is the sam
2026 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
2028 ILGenerator ig = ec.ig;
2029 LocalBuilder enumerator, disposable;
2030 Expression empty = new EmptyExpression ();
2034 // FIXME: maybe we can apply the same trick we do in the
2035 // array handling to avoid creating empty and conv in some cases.
2037 // Although it is not as important in this case, as the type
2038 // will not likely be object (what the enumerator will return).
2040 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2044 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2045 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2048 // Instantiate the enumerator
2050 if (expr.Type.IsValueType){
2051 if (expr is IMemoryLocation){
2052 IMemoryLocation ml = (IMemoryLocation) expr;
2056 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2057 " does not implement IMemoryLocation");
2058 ig.Emit (OpCodes.Call, get_enum);
2061 ig.Emit (OpCodes.Callvirt, get_enum);
2063 ig.Emit (OpCodes.Stloc, enumerator);
2066 // Protect the code in a try/finalize block, so that
2067 // if the beast implement IDisposable, we get rid of it
2069 Label l = ig.BeginExceptionBlock ();
2070 bool old_in_try = ec.InTry;
2073 Label end_try = ig.DefineLabel ();
2075 ig.MarkLabel (ec.LoopBegin);
2076 ig.Emit (OpCodes.Ldloc, enumerator);
2077 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
2078 ig.Emit (OpCodes.Brfalse, end_try);
2079 ig.Emit (OpCodes.Ldloc, enumerator);
2080 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
2081 variable.EmitAssign (ec, conv);
2082 statement.Emit (ec);
2083 ig.Emit (OpCodes.Br, ec.LoopBegin);
2084 ig.MarkLabel (end_try);
2085 ec.InTry = old_in_try;
2087 // The runtime provides this for us.
2088 // ig.Emit (OpCodes.Leave, end);
2091 // Now the finally block
2093 Label end_finally = ig.DefineLabel ();
2094 bool old_in_finally = ec.InFinally;
2095 ec.InFinally = true;
2096 ig.BeginFinallyBlock ();
2098 ig.Emit (OpCodes.Ldloc, enumerator);
2099 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2100 ig.Emit (OpCodes.Stloc, disposable);
2101 ig.Emit (OpCodes.Ldloc, disposable);
2102 ig.Emit (OpCodes.Brfalse, end_finally);
2103 ig.Emit (OpCodes.Ldloc, disposable);
2104 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2105 ig.MarkLabel (end_finally);
2106 ec.InFinally = old_in_finally;
2108 // The runtime generates this anyways.
2109 // ig.Emit (OpCodes.Endfinally);
2111 ig.EndExceptionBlock ();
2113 ig.MarkLabel (ec.LoopEnd);
2118 // FIXME: possible optimization.
2119 // We might be able to avoid creating `empty' if the type is the sam
2121 bool EmitArrayForeach (EmitContext ec, Type var_type)
2123 Type array_type = expr.Type;
2124 Type element_type = array_type.GetElementType ();
2125 Expression conv = null;
2126 Expression empty = new EmptyExpression (element_type);
2128 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2132 int rank = array_type.GetArrayRank ();
2133 ILGenerator ig = ec.ig;
2135 LocalBuilder copy = ig.DeclareLocal (array_type);
2138 // Make our copy of the array
2141 ig.Emit (OpCodes.Stloc, copy);
2144 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2148 ig.Emit (OpCodes.Ldc_I4_0);
2149 ig.Emit (OpCodes.Stloc, counter);
2150 test = ig.DefineLabel ();
2151 ig.Emit (OpCodes.Br, test);
2153 loop = ig.DefineLabel ();
2154 ig.MarkLabel (loop);
2156 ig.Emit (OpCodes.Ldloc, copy);
2157 ig.Emit (OpCodes.Ldloc, counter);
2158 ArrayAccess.EmitLoadOpcode (ig, var_type);
2160 variable.EmitAssign (ec, conv);
2162 statement.Emit (ec);
2164 ig.MarkLabel (ec.LoopBegin);
2165 ig.Emit (OpCodes.Ldloc, counter);
2166 ig.Emit (OpCodes.Ldc_I4_1);
2167 ig.Emit (OpCodes.Add);
2168 ig.Emit (OpCodes.Stloc, counter);
2170 ig.MarkLabel (test);
2171 ig.Emit (OpCodes.Ldloc, counter);
2172 ig.Emit (OpCodes.Ldloc, copy);
2173 ig.Emit (OpCodes.Ldlen);
2174 ig.Emit (OpCodes.Conv_I4);
2175 ig.Emit (OpCodes.Blt, loop);
2177 LocalBuilder [] dim_len = new LocalBuilder [rank];
2178 LocalBuilder [] dim_count = new LocalBuilder [rank];
2179 Label [] loop = new Label [rank];
2180 Label [] test = new Label [rank];
2183 for (dim = 0; dim < rank; dim++){
2184 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2185 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2186 test [dim] = ig.DefineLabel ();
2187 loop [dim] = ig.DefineLabel ();
2190 for (dim = 0; dim < rank; dim++){
2191 ig.Emit (OpCodes.Ldloc, copy);
2192 IntLiteral.EmitInt (ig, dim);
2193 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2194 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2197 for (dim = 0; dim < rank; dim++){
2198 ig.Emit (OpCodes.Ldc_I4_0);
2199 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2200 ig.Emit (OpCodes.Br, test [dim]);
2201 ig.MarkLabel (loop [dim]);
2204 ig.Emit (OpCodes.Ldloc, copy);
2205 for (dim = 0; dim < rank; dim++)
2206 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2209 // FIXME: Maybe we can cache the computation of `get'?
2211 Type [] args = new Type [rank];
2214 for (int i = 0; i < rank; i++)
2215 args [i] = TypeManager.int32_type;
2217 ModuleBuilder mb = RootContext.ModuleBuilder;
2218 get = mb.GetArrayMethod (
2220 CallingConventions.HasThis| CallingConventions.Standard,
2222 ig.Emit (OpCodes.Call, get);
2223 variable.EmitAssign (ec, conv);
2224 statement.Emit (ec);
2225 ig.MarkLabel (ec.LoopBegin);
2226 for (dim = rank - 1; dim >= 0; dim--){
2227 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2228 ig.Emit (OpCodes.Ldc_I4_1);
2229 ig.Emit (OpCodes.Add);
2230 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2232 ig.MarkLabel (test [dim]);
2233 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2234 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2235 ig.Emit (OpCodes.Blt, loop [dim]);
2238 ig.MarkLabel (ec.LoopEnd);
2243 public override bool Emit (EmitContext ec)
2248 expr = expr.Resolve (ec);
2252 var_type = RootContext.LookupType (ec.TypeContainer, type, false, loc);
2253 if (var_type == null)
2257 // We need an instance variable. Not sure this is the best
2258 // way of doing this.
2260 // FIXME: When we implement propertyaccess, will those turn
2261 // out to return values in ExprClass? I think they should.
2263 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2264 expr.eclass == ExprClass.PropertyAccess)){
2265 error1579 (expr.Type);
2269 ILGenerator ig = ec.ig;
2271 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2272 bool old_inloop = ec.InLoop;
2273 ec.LoopBegin = ig.DefineLabel ();
2274 ec.LoopEnd = ig.DefineLabel ();
2277 if (expr.Type.IsArray)
2278 ret_val = EmitArrayForeach (ec, var_type);
2280 MethodInfo get_enum;
2282 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
2285 ret_val = EmitCollectionForeach (ec, var_type, get_enum);
2288 ec.LoopBegin = old_begin;
2289 ec.LoopEnd = old_end;
2290 ec.InLoop = old_inloop;