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;
268 public class StatementExpression : Statement {
269 public readonly ExpressionStatement Expr;
271 public StatementExpression (ExpressionStatement expr)
276 public override bool Emit (EmitContext ec)
278 ILGenerator ig = ec.ig;
281 ne = Expr.Resolve (ec);
283 if (ne is ExpressionStatement)
284 ((ExpressionStatement) ne).EmitStatement (ec);
287 ig.Emit (OpCodes.Pop);
294 public override string ToString ()
296 return "StatementExpression (" + Expr + ")";
301 /// Implements the return statement
303 public class Return : Statement {
304 public Expression Expr;
305 public readonly Location loc;
307 public Return (Expression expr, Location l)
313 public override bool Emit (EmitContext ec)
316 Report.Error (157,loc,"Control can not leave the body of the finally block");
320 if (ec.ReturnType == null){
322 Report.Error (127, loc, "Return with a value not allowed here");
327 Report.Error (126, loc, "An object of type `" +
328 TypeManager.CSharpName (ec.ReturnType) + "' is " +
329 "expected for the return statement");
333 Expr = Expr.Resolve (ec);
337 if (Expr.Type != ec.ReturnType)
338 Expr = Expression.ConvertImplicitRequired (
339 ec, Expr, ec.ReturnType, loc);
346 if (ec.InTry || ec.InCatch)
347 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
350 if (ec.InTry || ec.InCatch){
351 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
354 ec.ig.Emit (OpCodes.Ret);
360 public class Goto : Statement {
365 public Goto (Block parent_block, string label, Location l)
367 block = parent_block;
372 public string Target {
378 public override bool Emit (EmitContext ec)
380 LabeledStatement label = block.LookupLabel (target);
384 // Maybe we should catch this before?
388 "No such label `" + target + "' in this scope");
391 Label l = label.LabelTarget (ec);
392 ec.ig.Emit (OpCodes.Br, l);
398 public class LabeledStatement : Statement {
403 public LabeledStatement (string label_name)
405 this.label_name = label_name;
408 public Label LabelTarget (EmitContext ec)
412 label = ec.ig.DefineLabel ();
418 public override bool Emit (EmitContext ec)
421 ec.ig.MarkLabel (label);
429 /// `goto default' statement
431 public class GotoDefault : Statement {
434 public GotoDefault (Location l)
439 public override bool Emit (EmitContext ec)
441 if (ec.Switch == null){
442 Report.Error (153, loc, "goto default is only valid in a switch statement");
446 if (!ec.Switch.GotDefault){
447 Report.Error (159, loc, "No default target on switch statement");
450 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
456 /// `goto case' statement
458 public class GotoCase : Statement {
462 public GotoCase (Expression e, Location l)
468 public override bool Emit (EmitContext ec)
470 if (ec.Switch == null){
471 Report.Error (153, loc, "goto case is only valid in a switch statement");
475 expr = expr.Resolve (ec);
479 if (!(expr is Constant)){
480 Report.Error (159, loc, "Target expression for goto case is not constant");
484 object val = Expression.ConvertIntLiteral (
485 (Constant) expr, ec.Switch.SwitchType, loc);
490 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
495 "No such label 'case " + val + "': for the goto case");
498 ec.ig.Emit (OpCodes.Br, sl.ILLabel);
503 public class Throw : Statement {
504 public readonly Expression Expr;
507 public Throw (Expression expr, Location l)
513 public override bool Emit (EmitContext ec)
517 ec.ig.Emit (OpCodes.Rethrow);
521 "A throw statement with no argument is only " +
522 "allowed in a catch clause");
527 Expression e = Expr.Resolve (ec);
534 ec.ig.Emit (OpCodes.Throw);
540 public class Break : Statement {
543 public Break (Location l)
548 public override bool Emit (EmitContext ec)
550 ILGenerator ig = ec.ig;
552 if (ec.InLoop == false && ec.Switch == null){
553 Report.Error (139, loc, "No enclosing loop or switch to continue to");
557 ig.Emit (OpCodes.Br, ec.LoopEnd);
562 public class Continue : Statement {
565 public Continue (Location l)
570 public override bool Emit (EmitContext ec)
572 Label begin = ec.LoopBegin;
575 Report.Error (139, loc, "No enclosing loop to continue to");
580 // UGH: Non trivial. This Br might cross a try/catch boundary
584 // try { ... } catch { continue; }
588 // try {} catch { while () { continue; }}
590 ec.ig.Emit (OpCodes.Br, begin);
595 public class VariableInfo {
596 public readonly string Type;
597 public LocalBuilder LocalBuilder;
598 public Type VariableType;
599 public readonly Location Location;
603 public bool Assigned;
604 public bool ReadOnly;
606 public VariableInfo (string type, Location l)
617 throw new Exception ("Unassigned idx for variable");
630 /// Block represents a C# block.
634 /// This class is used in a number of places: either to represent
635 /// explicit blocks that the programmer places or implicit blocks.
637 /// Implicit blocks are used as labels or to introduce variable
640 public class Block : Statement {
641 public readonly Block Parent;
642 public readonly bool Implicit;
645 // The statements in this block
647 StatementCollection statements;
650 // An array of Blocks. We keep track of children just
651 // to generate the local variable declarations.
653 // Statements and child statements are handled through the
659 // Labels. (label, block) pairs.
664 // Keeps track of (name, type) pairs
669 // Keeps track of constants
673 // Maps variable names to ILGenerator.LocalBuilders
675 Hashtable local_builders;
683 public Block (Block parent)
686 parent.AddChild (this);
688 this.Parent = parent;
689 this.Implicit = false;
694 public Block (Block parent, bool implicit_block)
697 parent.AddChild (this);
699 this.Parent = parent;
700 this.Implicit = true;
710 void AddChild (Block b)
712 if (children == null)
713 children = new ArrayList ();
719 /// Adds a label to the current block.
723 /// false if the name already exists in this block. true
727 public bool AddLabel (string name, LabeledStatement target)
730 labels = new Hashtable ();
731 if (labels.Contains (name))
734 labels.Add (name, target);
738 public LabeledStatement LookupLabel (string name)
741 if (labels.Contains (name))
742 return ((LabeledStatement) labels [name]);
746 return Parent.LookupLabel (name);
751 public bool AddVariable (string type, string name, Parameters pars, Location l)
753 if (variables == null)
754 variables = new Hashtable ();
756 if (GetVariableType (name) != null)
761 Parameter p = pars.GetParameterByName (name, out idx);
766 VariableInfo vi = new VariableInfo (type, l);
768 variables.Add (name, vi);
773 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
775 if (!AddVariable (type, name, pars, l))
778 if (constants == null)
779 constants = new Hashtable ();
781 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 (EmitContext ec, Block toplevel, int count)
921 TypeContainer tc = ec.TypeContainer;
922 ILGenerator ig = ec.ig;
925 // Process this block variables
927 if (variables != null){
928 local_builders = new Hashtable ();
930 foreach (DictionaryEntry de in variables){
931 string name = (string) de.Key;
932 VariableInfo vi = (VariableInfo) de.Value;
935 t = RootContext.LookupType (tc, vi.Type, false, vi.Location);
940 vi.LocalBuilder = ig.DeclareLocal (t);
943 if (constants == null)
946 Expression cv = (Expression) constants [name];
950 Expression e = cv.Resolve (ec);
954 if (!(e is Constant)){
955 Report.Error (133, vi.Location,
956 "The expression being assigned to `" +
957 name + "' must be constant");
961 constants.Remove (name);
962 constants.Add (name, e);
967 // Now, handle the children
969 if (children != null){
970 foreach (Block b in children)
971 count = b.EmitMeta (ec, toplevel, count);
977 public void UsageWarning ()
981 if (variables != null){
982 foreach (DictionaryEntry de in variables){
983 VariableInfo vi = (VariableInfo) de.Value;
988 name = (string) de.Key;
992 219, vi.Location, "The variable `" + name +
993 "' is assigned but its value is never used");
996 168, vi.Location, "The variable `" +
998 "' is declared but never used");
1003 if (children != null)
1004 foreach (Block b in children)
1008 // static int count;
1010 public override bool Emit (EmitContext ec)
1012 bool is_ret = false;
1013 Block prev_block = ec.CurrentBlock;
1016 ec.CurrentBlock = this;
1018 // throw new Exception ();
1019 foreach (Statement s in Statements)
1020 is_ret = s.Emit (ec);
1023 ec.CurrentBlock = prev_block;
1028 public class SwitchLabel {
1031 public Location loc;
1032 public Label ILLabel;
1035 // if expr == null, then it is the default case.
1037 public SwitchLabel (Expression expr, Location l)
1043 public Expression Label {
1049 public object Converted {
1056 // Resolves the expression, reduces it to a literal if possible
1057 // and then converts it to the requested type.
1059 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1061 ILLabel = ec.ig.DefineLabel ();
1066 Expression e = label.Resolve (ec);
1071 if (!(e is Constant)){
1072 Console.WriteLine ("Value is: " + label);
1073 Report.Error (150, loc, "A constant value is expected");
1077 if (e is StringConstant || e is NullLiteral){
1078 if (required_type == TypeManager.string_type){
1080 ILLabel = ec.ig.DefineLabel ();
1085 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1086 if (converted == null)
1093 public class SwitchSection {
1094 // An array of SwitchLabels.
1095 public readonly ArrayList Labels;
1096 public readonly Block Block;
1098 public SwitchSection (ArrayList labels, Block block)
1105 public class Switch : Statement {
1106 public readonly ArrayList Sections;
1107 public Expression Expr;
1110 /// Maps constants whose type type SwitchType to their SwitchLabels.
1112 public Hashtable Elements;
1115 /// The governing switch type
1117 public Type SwitchType;
1123 Label default_target;
1127 // The types allowed to be implicitly cast from
1128 // on the governing type
1130 static Type [] allowed_types;
1132 public Switch (Expression e, ArrayList sects, Location l)
1139 public bool GotDefault {
1145 public Label DefaultTarget {
1147 return default_target;
1152 // Determines the governing type for a switch. The returned
1153 // expression might be the expression from the switch, or an
1154 // expression that includes any potential conversions to the
1155 // integral types or to string.
1157 Expression SwitchGoverningType (EmitContext ec, Type t)
1159 if (t == TypeManager.int32_type ||
1160 t == TypeManager.uint32_type ||
1161 t == TypeManager.char_type ||
1162 t == TypeManager.byte_type ||
1163 t == TypeManager.sbyte_type ||
1164 t == TypeManager.ushort_type ||
1165 t == TypeManager.short_type ||
1166 t == TypeManager.uint64_type ||
1167 t == TypeManager.int64_type ||
1168 t == TypeManager.string_type ||
1169 t.IsSubclassOf (TypeManager.enum_type))
1172 if (allowed_types == null){
1173 allowed_types = new Type [] {
1174 TypeManager.sbyte_type,
1175 TypeManager.byte_type,
1176 TypeManager.short_type,
1177 TypeManager.ushort_type,
1178 TypeManager.int32_type,
1179 TypeManager.uint32_type,
1180 TypeManager.int64_type,
1181 TypeManager.uint64_type,
1182 TypeManager.char_type,
1183 TypeManager.string_type
1188 // Try to find a *user* defined implicit conversion.
1190 // If there is no implicit conversion, or if there are multiple
1191 // conversions, we have to report an error
1193 Expression converted = null;
1194 foreach (Type tt in allowed_types){
1197 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1201 if (converted != null){
1202 Report.Error (-12, loc, "More than one conversion to an integral " +
1203 " type exists for type `" +
1204 TypeManager.CSharpName (Expr.Type)+"'");
1212 void error152 (string n)
1215 152, "The label `" + n + ":' " +
1216 "is already present on this switch statement");
1220 // Performs the basic sanity checks on the switch statement
1221 // (looks for duplicate keys and non-constant expressions).
1223 // It also returns a hashtable with the keys that we will later
1224 // use to compute the switch tables
1226 bool CheckSwitch (EmitContext ec)
1230 Elements = new Hashtable ();
1232 got_default = false;
1234 if (TypeManager.IsEnumType (SwitchType)){
1235 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1237 compare_type = SwitchType;
1239 foreach (SwitchSection ss in Sections){
1240 foreach (SwitchLabel sl in ss.Labels){
1241 if (!sl.ResolveAndReduce (ec, SwitchType)){
1246 if (sl.Label == null){
1248 error152 ("default");
1255 object key = sl.Converted;
1257 if (key is Constant)
1258 key = ((Constant) key).GetValue ();
1261 key = NullLiteral.Null;
1263 string lname = null;
1264 if (compare_type == TypeManager.uint64_type){
1265 ulong v = (ulong) key;
1267 if (Elements.Contains (v))
1268 lname = v.ToString ();
1270 Elements.Add (v, sl);
1271 } else if (compare_type == TypeManager.int64_type){
1272 long v = (long) key;
1274 if (Elements.Contains (v))
1275 lname = v.ToString ();
1277 Elements.Add (v, sl);
1278 } else if (compare_type == TypeManager.uint32_type){
1279 uint v = (uint) key;
1281 if (Elements.Contains (v))
1282 lname = v.ToString ();
1284 Elements.Add (v, sl);
1285 } else if (compare_type == TypeManager.char_type){
1286 char v = (char) key;
1288 if (Elements.Contains (v))
1289 lname = v.ToString ();
1291 Elements.Add (v, sl);
1292 } else if (compare_type == TypeManager.byte_type){
1293 byte v = (byte) key;
1295 if (Elements.Contains (v))
1296 lname = v.ToString ();
1298 Elements.Add (v, sl);
1299 } else if (compare_type == TypeManager.sbyte_type){
1300 sbyte v = (sbyte) key;
1302 if (Elements.Contains (v))
1303 lname = v.ToString ();
1305 Elements.Add (v, sl);
1306 } else if (compare_type == TypeManager.short_type){
1307 short v = (short) key;
1309 if (Elements.Contains (v))
1310 lname = v.ToString ();
1312 Elements.Add (v, sl);
1313 } else if (compare_type == TypeManager.ushort_type){
1314 ushort v = (ushort) key;
1316 if (Elements.Contains (v))
1317 lname = v.ToString ();
1319 Elements.Add (v, sl);
1320 } else if (compare_type == TypeManager.string_type){
1321 if (key is NullLiteral){
1322 if (Elements.Contains (NullLiteral.Null))
1325 Elements.Add (NullLiteral.Null, null);
1327 string s = (string) key;
1329 if (Elements.Contains (s))
1332 Elements.Add (s, sl);
1334 } else if (compare_type == TypeManager.int32_type) {
1337 if (Elements.Contains (v))
1338 lname = v.ToString ();
1340 Elements.Add (v, sl);
1342 throw new Exception ("Unknown switch type!" +
1343 SwitchType + " " + compare_type);
1347 error152 ("case + " + lname);
1358 void EmitObjectInteger (ILGenerator ig, object k)
1361 IntConstant.EmitInt (ig, (int) k);
1362 else if (k is Constant){
1363 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1364 } else if (k is uint)
1365 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1367 LongConstant.EmitLong (ig, (long) k);
1368 else if (k is ulong)
1369 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1371 IntConstant.EmitInt (ig, (int) ((char) k));
1372 else if (k is sbyte)
1373 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1375 IntConstant.EmitInt (ig, (int) ((byte) k));
1377 throw new Exception ("Unhandled case");
1381 // This simple emit switch works, but does not take advantage of the
1382 // `switch' opcode. The swithc opcode uses a jump table that we are not
1383 // computing at this point
1385 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1387 ILGenerator ig = ec.ig;
1388 Label end_of_switch = ig.DefineLabel ();
1389 Label next_test = ig.DefineLabel ();
1390 Label null_target = ig.DefineLabel ();
1391 bool default_found = false;
1392 bool first_test = true;
1393 bool pending_goto_end = false;
1394 bool all_return = true;
1395 bool is_string = false;
1399 // Special processing for strings: we cant compare
1402 if (SwitchType == TypeManager.string_type){
1403 ig.Emit (OpCodes.Ldloc, val);
1406 if (Elements.Contains (NullLiteral.Null)){
1407 ig.Emit (OpCodes.Brfalse, null_target);
1409 ig.Emit (OpCodes.Brfalse, default_target);
1411 ig.Emit (OpCodes.Ldloc, val);
1412 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1413 ig.Emit (OpCodes.Stloc, val);
1416 foreach (SwitchSection ss in Sections){
1417 Label sec_begin = ig.DefineLabel ();
1419 if (pending_goto_end)
1420 ig.Emit (OpCodes.Br, end_of_switch);
1422 int label_count = ss.Labels.Count;
1424 foreach (SwitchLabel sl in ss.Labels){
1425 ig.MarkLabel (sl.ILLabel);
1428 ig.MarkLabel (next_test);
1429 next_test = ig.DefineLabel ();
1432 // If we are the default target
1434 if (sl.Label == null){
1435 ig.MarkLabel (default_target);
1436 default_found = true;
1438 object lit = sl.Converted;
1440 if (lit is NullLiteral){
1442 if (label_count == 1)
1443 ig.Emit (OpCodes.Br, next_test);
1448 StringConstant str = (StringConstant) lit;
1450 ig.Emit (OpCodes.Ldloc, val);
1451 ig.Emit (OpCodes.Ldstr, str.Value);
1452 if (label_count == 1)
1453 ig.Emit (OpCodes.Bne_Un, next_test);
1455 ig.Emit (OpCodes.Beq, sec_begin);
1457 ig.Emit (OpCodes.Ldloc, val);
1458 EmitObjectInteger (ig, lit);
1459 ig.Emit (OpCodes.Ceq);
1460 if (label_count == 1)
1461 ig.Emit (OpCodes.Brfalse, next_test);
1463 ig.Emit (OpCodes.Brtrue, sec_begin);
1467 if (label_count != 1)
1468 ig.Emit (OpCodes.Br, next_test);
1471 ig.MarkLabel (null_target);
1472 ig.MarkLabel (sec_begin);
1473 if (ss.Block.Emit (ec))
1474 pending_goto_end = false;
1477 pending_goto_end = true;
1482 ig.MarkLabel (default_target);
1483 ig.MarkLabel (next_test);
1484 ig.MarkLabel (end_of_switch);
1489 public override bool Emit (EmitContext ec)
1491 Expr = Expr.Resolve (ec);
1495 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1496 if (new_expr == null){
1497 Report.Error (151, loc, "An integer type or string was expected for switch");
1502 SwitchType = new_expr.Type;
1504 if (!CheckSwitch (ec))
1507 // Store variable for comparission purposes
1508 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1510 ec.ig.Emit (OpCodes.Stloc, value);
1512 ILGenerator ig = ec.ig;
1514 default_target = ig.DefineLabel ();
1517 // Setup the codegen context
1519 Label old_end = ec.LoopEnd;
1520 Switch old_switch = ec.Switch;
1522 ec.LoopEnd = ig.DefineLabel ();
1526 bool all_return = SimpleSwitchEmit (ec, value);
1528 // Restore context state.
1529 ig.MarkLabel (ec.LoopEnd);
1532 // FIXME: I am emitting a nop, because the switch performs
1533 // no analysis on whether something ever reaches the end
1535 // try: b (int a) { switch (a) { default: return 0; } }
1536 ig.Emit (OpCodes.Nop);
1539 // Restore the previous context
1541 ec.LoopEnd = old_end;
1542 ec.Switch = old_switch;
1545 // Because we have a nop at the end
1551 public class Lock : Statement {
1552 public readonly Expression Expr;
1553 public readonly Statement Statement;
1556 public Lock (Expression expr, Statement stmt, Location l)
1563 public override bool Emit (EmitContext ec)
1565 Expression e = Expr.Resolve (ec);
1571 if (type.IsValueType){
1572 Report.Error (185, loc, "lock statement requires the expression to be " +
1573 " a reference type (type is: `" +
1574 TypeManager.CSharpName (type) + "'");
1578 ILGenerator ig = ec.ig;
1579 LocalBuilder temp = ig.DeclareLocal (type);
1582 ig.Emit (OpCodes.Dup);
1583 ig.Emit (OpCodes.Stloc, temp);
1584 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1587 Label end = ig.BeginExceptionBlock ();
1588 bool old_in_try = ec.InTry;
1590 Label finish = ig.DefineLabel ();
1591 Statement.Emit (ec);
1592 ec.InTry = old_in_try;
1593 // ig.Emit (OpCodes.Leave, finish);
1595 ig.MarkLabel (finish);
1598 ig.BeginFinallyBlock ();
1599 ig.Emit (OpCodes.Ldloc, temp);
1600 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1601 ig.EndExceptionBlock ();
1607 public class Unchecked : Statement {
1608 public readonly Block Block;
1610 public Unchecked (Block b)
1615 public override bool Emit (EmitContext ec)
1617 bool previous_state = ec.CheckState;
1620 ec.CheckState = false;
1621 val = Block.Emit (ec);
1622 ec.CheckState = previous_state;
1628 public class Checked : Statement {
1629 public readonly Block Block;
1631 public Checked (Block b)
1636 public override bool Emit (EmitContext ec)
1638 bool previous_state = ec.CheckState;
1641 ec.CheckState = true;
1642 val = Block.Emit (ec);
1643 ec.CheckState = previous_state;
1649 public class Unsafe : Statement {
1650 public readonly Block Block;
1652 public Unsafe (Block b)
1657 public override bool Emit (EmitContext ec)
1659 bool previous_state = ec.InUnsafe;
1663 val = Block.Emit (ec);
1664 ec.InUnsafe = previous_state;
1670 public class Catch {
1671 public readonly string Type;
1672 public readonly string Name;
1673 public readonly Block Block;
1674 public readonly Location Location;
1676 public Catch (string type, string name, Block block, Location l)
1685 public class Try : Statement {
1686 public readonly Block Fini, Block;
1687 public readonly ArrayList Specific;
1688 public readonly Catch General;
1691 // specific, general and fini might all be null.
1693 public Try (Block block, ArrayList specific, Catch general, Block fini)
1695 if (specific == null && general == null){
1696 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1700 this.Specific = specific;
1701 this.General = general;
1705 public override bool Emit (EmitContext ec)
1707 ILGenerator ig = ec.ig;
1709 Label finish = ig.DefineLabel ();;
1712 end = ig.BeginExceptionBlock ();
1713 bool old_in_try = ec.InTry;
1715 returns = Block.Emit (ec);
1716 ec.InTry = old_in_try;
1719 // System.Reflection.Emit provides this automatically:
1720 // ig.Emit (OpCodes.Leave, finish);
1722 bool old_in_catch = ec.InCatch;
1724 DeclSpace ds = ec.TypeContainer;
1726 foreach (Catch c in Specific){
1727 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1730 if (catch_type == null)
1733 ig.BeginCatchBlock (catch_type);
1735 if (c.Name != null){
1736 vi = c.Block.GetVariableInfo (c.Name);
1738 Console.WriteLine ("This should not happen! variable does not exist in this block");
1739 Environment.Exit (0);
1742 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1744 ig.Emit (OpCodes.Pop);
1749 if (General != null){
1750 ig.BeginCatchBlock (TypeManager.object_type);
1751 ig.Emit (OpCodes.Pop);
1752 General.Block.Emit (ec);
1754 ec.InCatch = old_in_catch;
1756 ig.MarkLabel (finish);
1758 ig.BeginFinallyBlock ();
1759 bool old_in_finally = ec.InFinally;
1760 ec.InFinally = true;
1762 ec.InFinally = old_in_finally;
1765 ig.EndExceptionBlock ();
1768 // FIXME: Is this correct?
1769 // Replace with `returns' and check test-18, maybe we can
1770 // perform an optimization here.
1777 // FIXME: We still do not support the expression variant of the using
1780 public class Using : Statement {
1781 object expression_or_block;
1782 Statement Statement;
1785 public Using (object expression_or_block, Statement stmt, Location l)
1787 this.expression_or_block = expression_or_block;
1793 // Emits the code for the case of using using a local variable declaration.
1795 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
1797 ILGenerator ig = ec.ig;
1798 Expression [] converted_vars;
1799 bool need_conv = false;
1800 Type type = RootContext.LookupType (ec.TypeContainer, type_name, false, loc);
1807 // The type must be an IDisposable or an implicit conversion
1810 converted_vars = new Expression [var_list.Count];
1811 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
1812 foreach (DictionaryEntry e in var_list){
1813 Expression var = (Expression) e.Key;
1815 var = var.Resolve (ec);
1819 converted_vars [i] = Expression.ConvertImplicit (
1820 ec, var, TypeManager.idisposable_type, loc);
1822 if (converted_vars [i] == null)
1830 bool old_in_try = ec.InTry;
1832 foreach (DictionaryEntry e in var_list){
1833 LocalVariableReference var = (LocalVariableReference) e.Key;
1834 Expression expr = (Expression) e.Value;
1837 a = new Assign (var, expr, loc);
1840 converted_vars [i] = var;
1844 ((ExpressionStatement) a).EmitStatement (ec);
1846 ig.BeginExceptionBlock ();
1849 Statement.Emit (ec);
1850 ec.InTry = old_in_try;
1852 bool old_in_finally = ec.InFinally;
1853 ec.InFinally = true;
1854 var_list.Reverse ();
1855 foreach (DictionaryEntry e in var_list){
1856 LocalVariableReference var = (LocalVariableReference) e.Key;
1857 Label skip = ig.DefineLabel ();
1860 ig.BeginFinallyBlock ();
1863 ig.Emit (OpCodes.Brfalse, skip);
1864 converted_vars [i].Emit (ec);
1865 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1866 ig.MarkLabel (skip);
1867 ig.EndExceptionBlock ();
1869 ec.InFinally = old_in_finally;
1874 bool EmitExpression (EmitContext ec, Expression expr)
1876 Type expr_type = expr.Type;
1877 Expression conv = null;
1879 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
1880 conv = Expression.ConvertImplicit (
1881 ec, expr, TypeManager.idisposable_type, loc);
1888 // Make a copy of the expression and operate on that.
1890 ILGenerator ig = ec.ig;
1891 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
1896 ig.Emit (OpCodes.Stloc, local_copy);
1898 bool old_in_try = ec.InTry;
1900 ig.BeginExceptionBlock ();
1901 Statement.Emit (ec);
1902 ec.InTry = old_in_try;
1904 Label skip = ig.DefineLabel ();
1905 bool old_in_finally = ec.InFinally;
1906 ig.BeginFinallyBlock ();
1907 ig.Emit (OpCodes.Ldloc, local_copy);
1908 ig.Emit (OpCodes.Brfalse, skip);
1909 ig.Emit (OpCodes.Ldloc, local_copy);
1910 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1911 ig.MarkLabel (skip);
1912 ec.InFinally = old_in_finally;
1913 ig.EndExceptionBlock ();
1918 public override bool Emit (EmitContext ec)
1920 if (expression_or_block is DictionaryEntry){
1921 string t = (string) ((DictionaryEntry) expression_or_block).Key;
1922 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
1924 return EmitLocalVariableDecls (ec, t, var_list);
1925 } if (expression_or_block is Expression){
1926 Expression e = (Expression) expression_or_block;
1932 return EmitExpression (ec, e);
1939 /// Implementation of the foreach C# statement
1941 public class Foreach : Statement {
1943 LocalVariableReference variable;
1945 Statement statement;
1948 public Foreach (string type, LocalVariableReference var, Expression expr,
1949 Statement stmt, Location l)
1952 this.variable = var;
1958 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1963 if (!(m is MethodInfo))
1966 if (m.Name != "GetEnumerator")
1969 MethodInfo mi = (MethodInfo) m;
1971 if (mi.ReturnType != TypeManager.ienumerator_type){
1972 if (!TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType))
1976 Type [] args = TypeManager.GetArgumentTypes (mi);
1980 if (args.Length == 0)
1987 /// This filter is used to find the GetEnumerator method
1988 /// on which IEnumerator operates
1990 static MemberFilter FilterEnumerator;
1994 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1997 void error1579 (Type t)
1999 Report.Error (1579, loc,
2000 "foreach statement cannot operate on variables of type `" +
2001 t.FullName + "' because that class does not provide a " +
2002 " GetEnumerator method or it is inaccessible");
2005 MethodInfo ProbeCollectionType (Type t)
2009 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2010 BindingFlags.Public | BindingFlags.Instance,
2011 FilterEnumerator, null);
2018 if (mi.Length == 0){
2023 return (MethodInfo) mi [0];
2027 // FIXME: possible optimization.
2028 // We might be able to avoid creating `empty' if the type is the sam
2030 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
2032 ILGenerator ig = ec.ig;
2033 LocalBuilder enumerator, disposable;
2034 Expression empty = new EmptyExpression ();
2038 // FIXME: maybe we can apply the same trick we do in the
2039 // array handling to avoid creating empty and conv in some cases.
2041 // Although it is not as important in this case, as the type
2042 // will not likely be object (what the enumerator will return).
2044 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2048 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2049 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2052 // Instantiate the enumerator
2054 if (expr.Type.IsValueType){
2055 if (expr is IMemoryLocation){
2056 IMemoryLocation ml = (IMemoryLocation) expr;
2060 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2061 " does not implement IMemoryLocation");
2062 ig.Emit (OpCodes.Call, get_enum);
2065 ig.Emit (OpCodes.Callvirt, get_enum);
2067 ig.Emit (OpCodes.Stloc, enumerator);
2070 // Protect the code in a try/finalize block, so that
2071 // if the beast implement IDisposable, we get rid of it
2073 Label l = ig.BeginExceptionBlock ();
2074 bool old_in_try = ec.InTry;
2077 Label end_try = ig.DefineLabel ();
2079 ig.MarkLabel (ec.LoopBegin);
2080 ig.Emit (OpCodes.Ldloc, enumerator);
2081 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
2082 ig.Emit (OpCodes.Brfalse, end_try);
2083 ig.Emit (OpCodes.Ldloc, enumerator);
2084 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
2085 variable.EmitAssign (ec, conv);
2086 statement.Emit (ec);
2087 ig.Emit (OpCodes.Br, ec.LoopBegin);
2088 ig.MarkLabel (end_try);
2089 ec.InTry = old_in_try;
2091 // The runtime provides this for us.
2092 // ig.Emit (OpCodes.Leave, end);
2095 // Now the finally block
2097 Label end_finally = ig.DefineLabel ();
2098 bool old_in_finally = ec.InFinally;
2099 ec.InFinally = true;
2100 ig.BeginFinallyBlock ();
2102 ig.Emit (OpCodes.Ldloc, enumerator);
2103 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2104 ig.Emit (OpCodes.Stloc, disposable);
2105 ig.Emit (OpCodes.Ldloc, disposable);
2106 ig.Emit (OpCodes.Brfalse, end_finally);
2107 ig.Emit (OpCodes.Ldloc, disposable);
2108 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2109 ig.MarkLabel (end_finally);
2110 ec.InFinally = old_in_finally;
2112 // The runtime generates this anyways.
2113 // ig.Emit (OpCodes.Endfinally);
2115 ig.EndExceptionBlock ();
2117 ig.MarkLabel (ec.LoopEnd);
2122 // FIXME: possible optimization.
2123 // We might be able to avoid creating `empty' if the type is the sam
2125 bool EmitArrayForeach (EmitContext ec, Type var_type)
2127 Type array_type = expr.Type;
2128 Type element_type = array_type.GetElementType ();
2129 Expression conv = null;
2130 Expression empty = new EmptyExpression (element_type);
2132 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2136 int rank = array_type.GetArrayRank ();
2137 ILGenerator ig = ec.ig;
2139 LocalBuilder copy = ig.DeclareLocal (array_type);
2142 // Make our copy of the array
2145 ig.Emit (OpCodes.Stloc, copy);
2148 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2152 ig.Emit (OpCodes.Ldc_I4_0);
2153 ig.Emit (OpCodes.Stloc, counter);
2154 test = ig.DefineLabel ();
2155 ig.Emit (OpCodes.Br, test);
2157 loop = ig.DefineLabel ();
2158 ig.MarkLabel (loop);
2160 ig.Emit (OpCodes.Ldloc, copy);
2161 ig.Emit (OpCodes.Ldloc, counter);
2162 ArrayAccess.EmitLoadOpcode (ig, var_type);
2164 variable.EmitAssign (ec, conv);
2166 statement.Emit (ec);
2168 ig.MarkLabel (ec.LoopBegin);
2169 ig.Emit (OpCodes.Ldloc, counter);
2170 ig.Emit (OpCodes.Ldc_I4_1);
2171 ig.Emit (OpCodes.Add);
2172 ig.Emit (OpCodes.Stloc, counter);
2174 ig.MarkLabel (test);
2175 ig.Emit (OpCodes.Ldloc, counter);
2176 ig.Emit (OpCodes.Ldloc, copy);
2177 ig.Emit (OpCodes.Ldlen);
2178 ig.Emit (OpCodes.Conv_I4);
2179 ig.Emit (OpCodes.Blt, loop);
2181 LocalBuilder [] dim_len = new LocalBuilder [rank];
2182 LocalBuilder [] dim_count = new LocalBuilder [rank];
2183 Label [] loop = new Label [rank];
2184 Label [] test = new Label [rank];
2187 for (dim = 0; dim < rank; dim++){
2188 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2189 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2190 test [dim] = ig.DefineLabel ();
2191 loop [dim] = ig.DefineLabel ();
2194 for (dim = 0; dim < rank; dim++){
2195 ig.Emit (OpCodes.Ldloc, copy);
2196 IntLiteral.EmitInt (ig, dim);
2197 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2198 ig.Emit (OpCodes.Stloc, dim_len [dim]);
2201 for (dim = 0; dim < rank; dim++){
2202 ig.Emit (OpCodes.Ldc_I4_0);
2203 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2204 ig.Emit (OpCodes.Br, test [dim]);
2205 ig.MarkLabel (loop [dim]);
2208 ig.Emit (OpCodes.Ldloc, copy);
2209 for (dim = 0; dim < rank; dim++)
2210 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2213 // FIXME: Maybe we can cache the computation of `get'?
2215 Type [] args = new Type [rank];
2218 for (int i = 0; i < rank; i++)
2219 args [i] = TypeManager.int32_type;
2221 ModuleBuilder mb = RootContext.ModuleBuilder;
2222 get = mb.GetArrayMethod (
2224 CallingConventions.HasThis| CallingConventions.Standard,
2226 ig.Emit (OpCodes.Call, get);
2227 variable.EmitAssign (ec, conv);
2228 statement.Emit (ec);
2229 ig.MarkLabel (ec.LoopBegin);
2230 for (dim = rank - 1; dim >= 0; dim--){
2231 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2232 ig.Emit (OpCodes.Ldc_I4_1);
2233 ig.Emit (OpCodes.Add);
2234 ig.Emit (OpCodes.Stloc, dim_count [dim]);
2236 ig.MarkLabel (test [dim]);
2237 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2238 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2239 ig.Emit (OpCodes.Blt, loop [dim]);
2242 ig.MarkLabel (ec.LoopEnd);
2247 public override bool Emit (EmitContext ec)
2252 expr = expr.Resolve (ec);
2256 var_type = RootContext.LookupType (ec.TypeContainer, type, false, loc);
2257 if (var_type == null)
2261 // We need an instance variable. Not sure this is the best
2262 // way of doing this.
2264 // FIXME: When we implement propertyaccess, will those turn
2265 // out to return values in ExprClass? I think they should.
2267 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2268 expr.eclass == ExprClass.PropertyAccess)){
2269 error1579 (expr.Type);
2273 ILGenerator ig = ec.ig;
2275 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2276 bool old_inloop = ec.InLoop;
2277 ec.LoopBegin = ig.DefineLabel ();
2278 ec.LoopEnd = ig.DefineLabel ();
2281 if (expr.Type.IsArray)
2282 ret_val = EmitArrayForeach (ec, var_type);
2284 MethodInfo get_enum;
2286 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
2289 ret_val = EmitCollectionForeach (ec, var_type, get_enum);
2292 ec.LoopBegin = old_begin;
2293 ec.LoopEnd = old_end;
2294 ec.InLoop = old_inloop;