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 /// Encapsulates the emission of a boolean test and jumping to a
62 /// This will emit the bool expression in `bool_expr' and if
63 /// `target_is_for_true' is true, then the code will generate a
64 /// brtrue to the target. Otherwise a brfalse.
66 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
67 Label target, bool target_is_for_true)
69 ILGenerator ig = ec.ig;
72 if (bool_expr is Unary){
73 Unary u = (Unary) bool_expr;
75 if (u.Oper == Unary.Operator.LogicalNot){
78 u.EmitLogicalNot (ec);
80 } else if (bool_expr is Binary){
81 Binary b = (Binary) bool_expr;
83 if (b.EmitBranchable (ec, target, target_is_for_true))
90 if (target_is_for_true){
92 ig.Emit (OpCodes.Brfalse, target);
94 ig.Emit (OpCodes.Brtrue, target);
97 ig.Emit (OpCodes.Brtrue, target);
99 ig.Emit (OpCodes.Brfalse, target);
103 public static void Warning_DeadCodeFound (Location loc)
105 Report.Warning (162, loc, "Unreachable code detected");
109 public class EmptyStatement : Statement {
110 public override bool Resolve (EmitContext ec)
115 public override bool Emit (EmitContext ec)
121 public class If : Statement {
123 public Statement TrueStatement;
124 public Statement FalseStatement;
126 public If (Expression expr, Statement trueStatement, Location l)
129 TrueStatement = trueStatement;
133 public If (Expression expr,
134 Statement trueStatement,
135 Statement falseStatement,
139 TrueStatement = trueStatement;
140 FalseStatement = falseStatement;
144 public override bool Resolve (EmitContext ec)
146 expr = ResolveBoolean (ec, expr, loc);
151 if (TrueStatement.Resolve (ec)){
152 if (FalseStatement != null){
153 if (FalseStatement.Resolve (ec))
163 public override bool Emit (EmitContext ec)
165 ILGenerator ig = ec.ig;
166 Label false_target = ig.DefineLabel ();
168 bool is_true_ret, is_false_ret;
171 // Dead code elimination
173 if (expr is BoolConstant){
174 bool take = ((BoolConstant) expr).Value;
177 if (FalseStatement != null){
178 Warning_DeadCodeFound (FalseStatement.loc);
180 return TrueStatement.Emit (ec);
182 Warning_DeadCodeFound (TrueStatement.loc);
183 if (FalseStatement != null)
184 return FalseStatement.Emit (ec);
188 EmitBoolExpression (ec, expr, false_target, false);
190 is_true_ret = TrueStatement.Emit (ec);
191 is_false_ret = is_true_ret;
193 if (FalseStatement != null){
194 bool branch_emitted = false;
196 end = ig.DefineLabel ();
198 ig.Emit (OpCodes.Br, end);
199 branch_emitted = true;
202 ig.MarkLabel (false_target);
203 is_false_ret = FalseStatement.Emit (ec);
208 ig.MarkLabel (false_target);
209 is_false_ret = false;
212 return is_true_ret && is_false_ret;
216 public class Do : Statement {
217 public Expression expr;
218 public readonly Statement EmbeddedStatement;
220 public Do (Statement statement, Expression boolExpr, Location l)
223 EmbeddedStatement = statement;
227 public override bool Resolve (EmitContext ec)
229 expr = ResolveBoolean (ec, expr, loc);
233 return EmbeddedStatement.Resolve (ec);
236 public override bool Emit (EmitContext ec)
238 ILGenerator ig = ec.ig;
239 Label loop = ig.DefineLabel ();
240 Label old_begin = ec.LoopBegin;
241 Label old_end = ec.LoopEnd;
242 bool old_inloop = ec.InLoop;
243 bool old_breaks = ec.Breaks;
245 ec.LoopBegin = ig.DefineLabel ();
246 ec.LoopEnd = ig.DefineLabel ();
251 EmbeddedStatement.Emit (ec);
252 bool breaks = ec.Breaks;
253 ig.MarkLabel (ec.LoopBegin);
256 // Dead code elimination
258 if (expr is BoolConstant){
259 bool res = ((BoolConstant) expr).Value;
262 ec.ig.Emit (OpCodes.Br, loop);
264 EmitBoolExpression (ec, expr, loop, true);
266 ig.MarkLabel (ec.LoopEnd);
268 ec.LoopBegin = old_begin;
269 ec.LoopEnd = old_end;
270 ec.InLoop = old_inloop;
271 ec.Breaks = old_breaks;
274 // Inform whether we are infinite or not
276 if (expr is BoolConstant){
277 BoolConstant bc = (BoolConstant) expr;
279 if (bc.Value == true)
280 return breaks == false;
287 public class While : Statement {
288 public Expression expr;
289 public readonly Statement Statement;
291 public While (Expression boolExpr, Statement statement, Location l)
293 this.expr = boolExpr;
294 Statement = statement;
298 public override bool Resolve (EmitContext ec)
300 expr = ResolveBoolean (ec, expr, loc);
304 return Statement.Resolve (ec);
307 public override bool Emit (EmitContext ec)
309 ILGenerator ig = ec.ig;
310 Label old_begin = ec.LoopBegin;
311 Label old_end = ec.LoopEnd;
312 bool old_inloop = ec.InLoop;
313 bool old_breaks = ec.Breaks;
314 Label while_loop = ig.DefineLabel ();
317 ec.LoopBegin = ig.DefineLabel ();
318 ec.LoopEnd = ig.DefineLabel ();
321 ig.Emit (OpCodes.Br, ec.LoopBegin);
322 ig.MarkLabel (while_loop);
325 // Inform whether we are infinite or not
327 if (expr is BoolConstant){
328 BoolConstant bc = (BoolConstant) expr;
330 ig.MarkLabel (ec.LoopBegin);
331 if (bc.Value == false){
332 Warning_DeadCodeFound (Statement.loc);
340 ig.Emit (OpCodes.Br, ec.LoopBegin);
343 // Inform that we are infinite (ie, `we return'), only
344 // if we do not `break' inside the code.
346 ret = breaks == false;
348 ig.MarkLabel (ec.LoopEnd);
352 ig.MarkLabel (ec.LoopBegin);
354 EmitBoolExpression (ec, expr, while_loop, true);
355 ig.MarkLabel (ec.LoopEnd);
360 ec.LoopBegin = old_begin;
361 ec.LoopEnd = old_end;
362 ec.InLoop = old_inloop;
363 ec.Breaks = old_breaks;
369 public class For : Statement {
371 readonly Statement InitStatement;
372 readonly Statement Increment;
373 readonly Statement Statement;
375 public For (Statement initStatement,
381 InitStatement = initStatement;
383 Increment = increment;
384 Statement = statement;
388 public override bool Resolve (EmitContext ec)
393 Test = ResolveBoolean (ec, Test, loc);
398 if (InitStatement != null){
399 if (!InitStatement.Resolve (ec))
403 if (Increment != null){
404 if (!Increment.Resolve (ec))
408 return Statement.Resolve (ec) && ok;
411 public override bool Emit (EmitContext ec)
413 ILGenerator ig = ec.ig;
414 Label old_begin = ec.LoopBegin;
415 Label old_end = ec.LoopEnd;
416 bool old_inloop = ec.InLoop;
417 bool old_breaks = ec.Breaks;
418 Label loop = ig.DefineLabel ();
419 Label test = ig.DefineLabel ();
421 if (InitStatement != null)
422 if (! (InitStatement is EmptyStatement))
423 InitStatement.Emit (ec);
425 ec.LoopBegin = ig.DefineLabel ();
426 ec.LoopEnd = ig.DefineLabel ();
429 ig.Emit (OpCodes.Br, test);
433 bool breaks = ec.Breaks;
435 ig.MarkLabel (ec.LoopBegin);
436 if (!(Increment is EmptyStatement))
441 // If test is null, there is no test, and we are just
445 EmitBoolExpression (ec, Test, loop, true);
447 ig.Emit (OpCodes.Br, loop);
448 ig.MarkLabel (ec.LoopEnd);
450 ec.LoopBegin = old_begin;
451 ec.LoopEnd = old_end;
452 ec.InLoop = old_inloop;
453 ec.Breaks = old_breaks;
456 // Inform whether we are infinite or not
459 if (Test is BoolConstant){
460 BoolConstant bc = (BoolConstant) Test;
463 return breaks == false;
471 public class StatementExpression : Statement {
474 public StatementExpression (ExpressionStatement expr, Location l)
480 public override bool Resolve (EmitContext ec)
482 expr = (Expression) expr.Resolve (ec);
486 public override bool Emit (EmitContext ec)
488 ILGenerator ig = ec.ig;
490 if (expr is ExpressionStatement)
491 ((ExpressionStatement) expr).EmitStatement (ec);
494 ig.Emit (OpCodes.Pop);
500 public override string ToString ()
502 return "StatementExpression (" + expr + ")";
507 /// Implements the return statement
509 public class Return : Statement {
510 public Expression Expr;
512 public Return (Expression expr, Location l)
518 public override bool Resolve (EmitContext ec)
521 Expr = Expr.Resolve (ec);
528 public override bool Emit (EmitContext ec)
531 Report.Error (157,loc,"Control can not leave the body of the finally block");
535 if (ec.ReturnType == null){
537 Report.Error (127, loc, "Return with a value not allowed here");
542 Report.Error (126, loc, "An object of type `" +
543 TypeManager.CSharpName (ec.ReturnType) + "' is " +
544 "expected for the return statement");
548 if (Expr.Type != ec.ReturnType)
549 Expr = Expression.ConvertImplicitRequired (
550 ec, Expr, ec.ReturnType, loc);
557 if (ec.InTry || ec.InCatch)
558 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
561 if (ec.InTry || ec.InCatch)
562 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
564 ec.ig.Emit (OpCodes.Ret);
570 public class Goto : Statement {
574 public override bool Resolve (EmitContext ec)
579 public Goto (Block parent_block, string label, Location l)
581 block = parent_block;
586 public string Target {
592 public override bool Emit (EmitContext ec)
594 LabeledStatement label = block.LookupLabel (target);
598 // Maybe we should catch this before?
602 "No such label `" + target + "' in this scope");
605 Label l = label.LabelTarget (ec);
606 ec.ig.Emit (OpCodes.Br, l);
612 public class LabeledStatement : Statement {
617 public LabeledStatement (string label_name)
619 this.label_name = label_name;
622 public Label LabelTarget (EmitContext ec)
626 label = ec.ig.DefineLabel ();
632 public override bool Emit (EmitContext ec)
635 ec.ig.MarkLabel (label);
643 /// `goto default' statement
645 public class GotoDefault : Statement {
647 public GotoDefault (Location l)
652 public override bool Emit (EmitContext ec)
654 if (ec.Switch == null){
655 Report.Error (153, loc, "goto default is only valid in a switch statement");
659 if (!ec.Switch.GotDefault){
660 Report.Error (159, loc, "No default target on switch statement");
663 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
669 /// `goto case' statement
671 public class GotoCase : Statement {
674 public GotoCase (Expression e, Location l)
680 public override bool Emit (EmitContext ec)
682 if (ec.Switch == null){
683 Report.Error (153, loc, "goto case is only valid in a switch statement");
687 expr = expr.Resolve (ec);
691 if (!(expr is Constant)){
692 Report.Error (159, loc, "Target expression for goto case is not constant");
696 object val = Expression.ConvertIntLiteral (
697 (Constant) expr, ec.Switch.SwitchType, loc);
702 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
707 "No such label 'case " + val + "': for the goto case");
710 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
715 public class Throw : Statement {
718 public Throw (Expression expr, Location l)
724 public override bool Resolve (EmitContext ec)
727 expr = expr.Resolve (ec);
734 public override bool Emit (EmitContext ec)
738 ec.ig.Emit (OpCodes.Rethrow);
742 "A throw statement with no argument is only " +
743 "allowed in a catch clause");
750 ec.ig.Emit (OpCodes.Throw);
756 public class Break : Statement {
758 public Break (Location l)
763 public override bool Emit (EmitContext ec)
765 ILGenerator ig = ec.ig;
767 if (ec.InLoop == false && ec.Switch == null){
768 Report.Error (139, loc, "No enclosing loop or switch to continue to");
773 if (ec.InTry || ec.InCatch)
774 ig.Emit (OpCodes.Leave, ec.LoopEnd);
776 ig.Emit (OpCodes.Br, ec.LoopEnd);
782 public class Continue : Statement {
784 public Continue (Location l)
789 public override bool Emit (EmitContext ec)
791 Label begin = ec.LoopBegin;
794 Report.Error (139, loc, "No enclosing loop to continue to");
799 // UGH: Non trivial. This Br might cross a try/catch boundary
803 // try { ... } catch { continue; }
807 // try {} catch { while () { continue; }}
809 ec.ig.Emit (OpCodes.Br, begin);
814 public class VariableInfo {
815 public readonly string Type;
816 public LocalBuilder LocalBuilder;
817 public Type VariableType;
818 public readonly Location Location;
821 public bool Assigned;
822 public bool ReadOnly;
824 public VariableInfo (string type, Location l)
831 public void MakePinned ()
833 TypeManager.MakePinned (LocalBuilder);
838 /// Block represents a C# block.
842 /// This class is used in a number of places: either to represent
843 /// explicit blocks that the programmer places or implicit blocks.
845 /// Implicit blocks are used as labels or to introduce variable
848 public class Block : Statement {
849 public readonly Block Parent;
850 public readonly bool Implicit;
851 public readonly Location StartLocation;
852 public Location EndLocation;
855 // The statements in this block
857 ArrayList statements;
860 // An array of Blocks. We keep track of children just
861 // to generate the local variable declarations.
863 // Statements and child statements are handled through the
869 // Labels. (label, block) pairs.
874 // Keeps track of (name, type) pairs
879 // Keeps track of constants
883 // Maps variable names to ILGenerator.LocalBuilders
885 Hashtable local_builders;
893 public Block (Block parent)
894 : this (parent, false, Location.Null, Location.Null)
897 public Block (Block parent, bool implicit_block)
898 : this (parent, implicit_block, Location.Null, Location.Null)
901 public Block (Block parent, Location start, Location end)
902 : this (parent, false, start, end)
905 public Block (Block parent, bool implicit_block, Location start, Location end)
908 parent.AddChild (this);
910 this.Parent = parent;
911 this.Implicit = implicit_block;
912 this.StartLocation = start;
913 this.EndLocation = end;
916 statements = new ArrayList ();
925 void AddChild (Block b)
927 if (children == null)
928 children = new ArrayList ();
933 public void SetEndLocation (Location loc)
939 /// Adds a label to the current block.
943 /// false if the name already exists in this block. true
947 public bool AddLabel (string name, LabeledStatement target)
950 labels = new Hashtable ();
951 if (labels.Contains (name))
954 labels.Add (name, target);
958 public LabeledStatement LookupLabel (string name)
961 if (labels.Contains (name))
962 return ((LabeledStatement) labels [name]);
966 return Parent.LookupLabel (name);
971 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
973 if (variables == null)
974 variables = new Hashtable ();
976 if (GetVariableType (name) != null)
981 Parameter p = pars.GetParameterByName (name, out idx);
986 VariableInfo vi = new VariableInfo (type, l);
988 variables.Add (name, vi);
990 // Console.WriteLine ("Adding {0} to {1}", name, ID);
994 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
996 if (AddVariable (type, name, pars, l) == null)
999 if (constants == null)
1000 constants = new Hashtable ();
1002 constants.Add (name, value);
1006 public Hashtable Variables {
1012 public VariableInfo GetVariableInfo (string name)
1014 if (variables != null) {
1016 temp = variables [name];
1019 return (VariableInfo) temp;
1024 return Parent.GetVariableInfo (name);
1029 public string GetVariableType (string name)
1031 VariableInfo vi = GetVariableInfo (name);
1039 public Expression GetConstantExpression (string name)
1041 if (constants != null) {
1043 temp = constants [name];
1046 return (Expression) temp;
1050 return Parent.GetConstantExpression (name);
1056 /// True if the variable named @name has been defined
1059 public bool IsVariableDefined (string name)
1061 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1062 if (variables != null) {
1063 if (variables.Contains (name))
1068 return Parent.IsVariableDefined (name);
1074 /// True if the variable named @name is a constant
1076 public bool IsConstant (string name)
1078 Expression e = null;
1080 e = GetConstantExpression (name);
1086 /// Use to fetch the statement associated with this label
1088 public Statement this [string name] {
1090 return (Statement) labels [name];
1095 /// A list of labels that were not used within this block
1097 public string [] GetUnreferenced ()
1099 // FIXME: Implement me
1103 public void AddStatement (Statement s)
1121 /// Emits the variable declarations and labels.
1124 /// tc: is our typecontainer (to resolve type references)
1125 /// ig: is the code generator:
1126 /// toplevel: the toplevel block. This is used for checking
1127 /// that no two labels with the same name are used.
1129 public void EmitMeta (EmitContext ec, Block toplevel)
1131 DeclSpace ds = ec.DeclSpace;
1132 ILGenerator ig = ec.ig;
1135 // Process this block variables
1137 if (variables != null){
1138 local_builders = new Hashtable ();
1140 foreach (DictionaryEntry de in variables){
1141 string name = (string) de.Key;
1142 VariableInfo vi = (VariableInfo) de.Value;
1145 t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
1149 vi.VariableType = t;
1150 vi.LocalBuilder = ig.DeclareLocal (t);
1152 if (CodeGen.SymbolWriter != null)
1153 vi.LocalBuilder.SetLocalSymInfo (name);
1155 if (constants == null)
1158 Expression cv = (Expression) constants [name];
1162 Expression e = cv.Resolve (ec);
1166 if (!(e is Constant)){
1167 Report.Error (133, vi.Location,
1168 "The expression being assigned to `" +
1169 name + "' must be constant (" + e + ")");
1173 constants.Remove (name);
1174 constants.Add (name, e);
1179 // Now, handle the children
1181 if (children != null){
1182 foreach (Block b in children)
1183 b.EmitMeta (ec, toplevel);
1187 public void UsageWarning ()
1191 if (variables != null){
1192 foreach (DictionaryEntry de in variables){
1193 VariableInfo vi = (VariableInfo) de.Value;
1198 name = (string) de.Key;
1202 219, vi.Location, "The variable `" + name +
1203 "' is assigned but its value is never used");
1206 168, vi.Location, "The variable `" +
1208 "' is declared but never used");
1213 if (children != null)
1214 foreach (Block b in children)
1218 public override bool Resolve (EmitContext ec)
1220 Block prev_block = ec.CurrentBlock;
1222 ec.CurrentBlock = this;
1223 foreach (Statement s in statements){
1224 if (s.Resolve (ec) == false){
1225 ec.CurrentBlock = prev_block;
1230 ec.CurrentBlock = prev_block;
1234 public override bool Emit (EmitContext ec)
1236 bool is_ret = false;
1237 Block prev_block = ec.CurrentBlock;
1239 ec.CurrentBlock = this;
1241 if (CodeGen.SymbolWriter != null) {
1242 ec.Mark (StartLocation);
1244 foreach (Statement s in statements) {
1247 is_ret = s.Emit (ec);
1250 ec.Mark (EndLocation);
1252 foreach (Statement s in statements)
1253 is_ret = s.Emit (ec);
1256 ec.CurrentBlock = prev_block;
1261 public class SwitchLabel {
1264 public Location loc;
1265 public Label ILLabel;
1266 public Label ILLabelCode;
1269 // if expr == null, then it is the default case.
1271 public SwitchLabel (Expression expr, Location l)
1277 public Expression Label {
1283 public object Converted {
1290 // Resolves the expression, reduces it to a literal if possible
1291 // and then converts it to the requested type.
1293 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1295 ILLabel = ec.ig.DefineLabel ();
1296 ILLabelCode = ec.ig.DefineLabel ();
1301 Expression e = label.Resolve (ec);
1306 if (!(e is Constant)){
1307 Console.WriteLine ("Value is: " + label);
1308 Report.Error (150, loc, "A constant value is expected");
1312 if (e is StringConstant || e is NullLiteral){
1313 if (required_type == TypeManager.string_type){
1315 ILLabel = ec.ig.DefineLabel ();
1320 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1321 if (converted == null)
1328 public class SwitchSection {
1329 // An array of SwitchLabels.
1330 public readonly ArrayList Labels;
1331 public readonly Block Block;
1333 public SwitchSection (ArrayList labels, Block block)
1340 public class Switch : Statement {
1341 public readonly ArrayList Sections;
1342 public Expression Expr;
1345 /// Maps constants whose type type SwitchType to their SwitchLabels.
1347 public Hashtable Elements;
1350 /// The governing switch type
1352 public Type SwitchType;
1358 Label default_target;
1361 // The types allowed to be implicitly cast from
1362 // on the governing type
1364 static Type [] allowed_types;
1366 public Switch (Expression e, ArrayList sects, Location l)
1373 public bool GotDefault {
1379 public Label DefaultTarget {
1381 return default_target;
1386 // Determines the governing type for a switch. The returned
1387 // expression might be the expression from the switch, or an
1388 // expression that includes any potential conversions to the
1389 // integral types or to string.
1391 Expression SwitchGoverningType (EmitContext ec, Type t)
1393 if (t == TypeManager.int32_type ||
1394 t == TypeManager.uint32_type ||
1395 t == TypeManager.char_type ||
1396 t == TypeManager.byte_type ||
1397 t == TypeManager.sbyte_type ||
1398 t == TypeManager.ushort_type ||
1399 t == TypeManager.short_type ||
1400 t == TypeManager.uint64_type ||
1401 t == TypeManager.int64_type ||
1402 t == TypeManager.string_type ||
1403 t == TypeManager.bool_type ||
1404 t.IsSubclassOf (TypeManager.enum_type))
1407 if (allowed_types == null){
1408 allowed_types = new Type [] {
1409 TypeManager.sbyte_type,
1410 TypeManager.byte_type,
1411 TypeManager.short_type,
1412 TypeManager.ushort_type,
1413 TypeManager.int32_type,
1414 TypeManager.uint32_type,
1415 TypeManager.int64_type,
1416 TypeManager.uint64_type,
1417 TypeManager.char_type,
1418 TypeManager.bool_type,
1419 TypeManager.string_type
1424 // Try to find a *user* defined implicit conversion.
1426 // If there is no implicit conversion, or if there are multiple
1427 // conversions, we have to report an error
1429 Expression converted = null;
1430 foreach (Type tt in allowed_types){
1433 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1437 if (converted != null){
1438 Report.Error (-12, loc, "More than one conversion to an integral " +
1439 " type exists for type `" +
1440 TypeManager.CSharpName (Expr.Type)+"'");
1448 void error152 (string n)
1451 152, "The label `" + n + ":' " +
1452 "is already present on this switch statement");
1456 // Performs the basic sanity checks on the switch statement
1457 // (looks for duplicate keys and non-constant expressions).
1459 // It also returns a hashtable with the keys that we will later
1460 // use to compute the switch tables
1462 bool CheckSwitch (EmitContext ec)
1466 Elements = new Hashtable ();
1468 got_default = false;
1470 if (TypeManager.IsEnumType (SwitchType)){
1471 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1473 compare_type = SwitchType;
1475 foreach (SwitchSection ss in Sections){
1476 foreach (SwitchLabel sl in ss.Labels){
1477 if (!sl.ResolveAndReduce (ec, SwitchType)){
1482 if (sl.Label == null){
1484 error152 ("default");
1491 object key = sl.Converted;
1493 if (key is Constant)
1494 key = ((Constant) key).GetValue ();
1497 key = NullLiteral.Null;
1499 string lname = null;
1500 if (compare_type == TypeManager.uint64_type){
1501 ulong v = (ulong) key;
1503 if (Elements.Contains (v))
1504 lname = v.ToString ();
1506 Elements.Add (v, sl);
1507 } else if (compare_type == TypeManager.int64_type){
1508 long v = (long) key;
1510 if (Elements.Contains (v))
1511 lname = v.ToString ();
1513 Elements.Add (v, sl);
1514 } else if (compare_type == TypeManager.uint32_type){
1515 uint v = (uint) key;
1517 if (Elements.Contains (v))
1518 lname = v.ToString ();
1520 Elements.Add (v, sl);
1521 } else if (compare_type == TypeManager.char_type){
1522 char v = (char) key;
1524 if (Elements.Contains (v))
1525 lname = v.ToString ();
1527 Elements.Add (v, sl);
1528 } else if (compare_type == TypeManager.byte_type){
1529 byte v = (byte) key;
1531 if (Elements.Contains (v))
1532 lname = v.ToString ();
1534 Elements.Add (v, sl);
1535 } else if (compare_type == TypeManager.sbyte_type){
1536 sbyte v = (sbyte) key;
1538 if (Elements.Contains (v))
1539 lname = v.ToString ();
1541 Elements.Add (v, sl);
1542 } else if (compare_type == TypeManager.short_type){
1543 short v = (short) key;
1545 if (Elements.Contains (v))
1546 lname = v.ToString ();
1548 Elements.Add (v, sl);
1549 } else if (compare_type == TypeManager.ushort_type){
1550 ushort v = (ushort) key;
1552 if (Elements.Contains (v))
1553 lname = v.ToString ();
1555 Elements.Add (v, sl);
1556 } else if (compare_type == TypeManager.string_type){
1557 if (key is NullLiteral){
1558 if (Elements.Contains (NullLiteral.Null))
1561 Elements.Add (NullLiteral.Null, null);
1563 string s = (string) key;
1565 if (Elements.Contains (s))
1568 Elements.Add (s, sl);
1570 } else if (compare_type == TypeManager.int32_type) {
1573 if (Elements.Contains (v))
1574 lname = v.ToString ();
1576 Elements.Add (v, sl);
1577 } else if (compare_type == TypeManager.bool_type) {
1578 bool v = (bool) key;
1580 if (Elements.Contains (v))
1581 lname = v.ToString ();
1583 Elements.Add (v, sl);
1587 throw new Exception ("Unknown switch type!" +
1588 SwitchType + " " + compare_type);
1592 error152 ("case + " + lname);
1603 void EmitObjectInteger (ILGenerator ig, object k)
1606 IntConstant.EmitInt (ig, (int) k);
1607 else if (k is Constant) {
1608 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1611 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1614 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1616 IntConstant.EmitInt (ig, (int) (long) k);
1617 ig.Emit (OpCodes.Conv_I8);
1620 LongConstant.EmitLong (ig, (long) k);
1622 else if (k is ulong)
1624 if ((ulong) k < (1L<<32))
1626 IntConstant.EmitInt (ig, (int) (long) k);
1627 ig.Emit (OpCodes.Conv_U8);
1631 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1635 IntConstant.EmitInt (ig, (int) ((char) k));
1636 else if (k is sbyte)
1637 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1639 IntConstant.EmitInt (ig, (int) ((byte) k));
1640 else if (k is short)
1641 IntConstant.EmitInt (ig, (int) ((short) k));
1642 else if (k is ushort)
1643 IntConstant.EmitInt (ig, (int) ((ushort) k));
1645 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1647 throw new Exception ("Unhandled case");
1650 // structure used to hold blocks of keys while calculating table switch
1651 class KeyBlock : IComparable
1653 public KeyBlock (long _nFirst)
1655 nFirst = nLast = _nFirst;
1659 public ArrayList rgKeys = null;
1662 get { return (int) (nLast - nFirst + 1); }
1664 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1666 return kbLast.nLast - kbFirst.nFirst + 1;
1668 public int CompareTo (object obj)
1670 KeyBlock kb = (KeyBlock) obj;
1671 int nLength = Length;
1672 int nLengthOther = kb.Length;
1673 if (nLengthOther == nLength)
1674 return (int) (kb.nFirst - nFirst);
1675 return nLength - nLengthOther;
1680 /// This method emits code for a lookup-based switch statement (non-string)
1681 /// Basically it groups the cases into blocks that are at least half full,
1682 /// and then spits out individual lookup opcodes for each block.
1683 /// It emits the longest blocks first, and short blocks are just
1684 /// handled with direct compares.
1686 /// <param name="ec"></param>
1687 /// <param name="val"></param>
1688 /// <returns></returns>
1689 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1691 int cElements = Elements.Count;
1692 object [] rgKeys = new object [cElements];
1693 Elements.Keys.CopyTo (rgKeys, 0);
1694 Array.Sort (rgKeys);
1696 // initialize the block list with one element per key
1697 ArrayList rgKeyBlocks = new ArrayList ();
1698 foreach (object key in rgKeys)
1699 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1702 // iteratively merge the blocks while they are at least half full
1703 // there's probably a really cool way to do this with a tree...
1704 while (rgKeyBlocks.Count > 1)
1706 ArrayList rgKeyBlocksNew = new ArrayList ();
1707 kbCurr = (KeyBlock) rgKeyBlocks [0];
1708 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1710 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1711 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1714 kbCurr.nLast = kb.nLast;
1718 // start a new block
1719 rgKeyBlocksNew.Add (kbCurr);
1723 rgKeyBlocksNew.Add (kbCurr);
1724 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1726 rgKeyBlocks = rgKeyBlocksNew;
1729 // initialize the key lists
1730 foreach (KeyBlock kb in rgKeyBlocks)
1731 kb.rgKeys = new ArrayList ();
1733 // fill the key lists
1735 if (rgKeyBlocks.Count > 0) {
1736 kbCurr = (KeyBlock) rgKeyBlocks [0];
1737 foreach (object key in rgKeys)
1739 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1741 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1742 kbCurr.rgKeys.Add (key);
1746 // sort the blocks so we can tackle the largest ones first
1747 rgKeyBlocks.Sort ();
1749 // okay now we can start...
1750 ILGenerator ig = ec.ig;
1751 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1752 Label lblDefault = ig.DefineLabel ();
1754 Type typeKeys = null;
1755 if (rgKeys.Length > 0)
1756 typeKeys = rgKeys [0].GetType (); // used for conversions
1758 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1760 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1761 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1764 foreach (object key in kb.rgKeys)
1766 ig.Emit (OpCodes.Ldloc, val);
1767 EmitObjectInteger (ig, key);
1768 SwitchLabel sl = (SwitchLabel) Elements [key];
1769 ig.Emit (OpCodes.Beq, sl.ILLabel);
1774 // TODO: if all the keys in the block are the same and there are
1775 // no gaps/defaults then just use a range-check.
1776 if (SwitchType == TypeManager.int64_type ||
1777 SwitchType == TypeManager.uint64_type)
1779 // TODO: optimize constant/I4 cases
1781 // check block range (could be > 2^31)
1782 ig.Emit (OpCodes.Ldloc, val);
1783 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1784 ig.Emit (OpCodes.Blt, lblDefault);
1785 ig.Emit (OpCodes.Ldloc, val);
1786 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1787 ig.Emit (OpCodes.Bgt, lblDefault);
1790 ig.Emit (OpCodes.Ldloc, val);
1793 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1794 ig.Emit (OpCodes.Sub);
1796 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1801 ig.Emit (OpCodes.Ldloc, val);
1802 int nFirst = (int) kb.nFirst;
1805 IntConstant.EmitInt (ig, nFirst);
1806 ig.Emit (OpCodes.Sub);
1808 else if (nFirst < 0)
1810 IntConstant.EmitInt (ig, -nFirst);
1811 ig.Emit (OpCodes.Add);
1815 // first, build the list of labels for the switch
1817 int cJumps = kb.Length;
1818 Label [] rgLabels = new Label [cJumps];
1819 for (int iJump = 0; iJump < cJumps; iJump++)
1821 object key = kb.rgKeys [iKey];
1822 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1824 SwitchLabel sl = (SwitchLabel) Elements [key];
1825 rgLabels [iJump] = sl.ILLabel;
1829 rgLabels [iJump] = lblDefault;
1831 // emit the switch opcode
1832 ig.Emit (OpCodes.Switch, rgLabels);
1835 // mark the default for this block
1837 ig.MarkLabel (lblDefault);
1840 // TODO: find the default case and emit it here,
1841 // to prevent having to do the following jump.
1842 // make sure to mark other labels in the default section
1844 // the last default just goes to the end
1845 ig.Emit (OpCodes.Br, lblDefault);
1847 // now emit the code for the sections
1848 bool fFoundDefault = false;
1849 bool fAllReturn = true;
1850 foreach (SwitchSection ss in Sections)
1852 foreach (SwitchLabel sl in ss.Labels)
1854 ig.MarkLabel (sl.ILLabel);
1855 ig.MarkLabel (sl.ILLabelCode);
1856 if (sl.Label == null)
1858 ig.MarkLabel (lblDefault);
1859 fFoundDefault = true;
1862 fAllReturn &= ss.Block.Emit (ec);
1863 //ig.Emit (OpCodes.Br, lblEnd);
1867 ig.MarkLabel (lblDefault);
1868 ig.MarkLabel (lblEnd);
1873 // This simple emit switch works, but does not take advantage of the
1875 // TODO: remove non-string logic from here
1876 // TODO: binary search strings?
1878 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1880 ILGenerator ig = ec.ig;
1881 Label end_of_switch = ig.DefineLabel ();
1882 Label next_test = ig.DefineLabel ();
1883 Label null_target = ig.DefineLabel ();
1884 bool default_found = false;
1885 bool first_test = true;
1886 bool pending_goto_end = false;
1887 bool all_return = true;
1888 bool is_string = false;
1892 // Special processing for strings: we cant compare
1895 if (SwitchType == TypeManager.string_type){
1896 ig.Emit (OpCodes.Ldloc, val);
1899 if (Elements.Contains (NullLiteral.Null)){
1900 ig.Emit (OpCodes.Brfalse, null_target);
1902 ig.Emit (OpCodes.Brfalse, default_target);
1904 ig.Emit (OpCodes.Ldloc, val);
1905 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1906 ig.Emit (OpCodes.Stloc, val);
1909 SwitchSection last_section;
1910 last_section = (SwitchSection) Sections [Sections.Count-1];
1912 foreach (SwitchSection ss in Sections){
1913 Label sec_begin = ig.DefineLabel ();
1915 if (pending_goto_end)
1916 ig.Emit (OpCodes.Br, end_of_switch);
1918 int label_count = ss.Labels.Count;
1920 foreach (SwitchLabel sl in ss.Labels){
1921 ig.MarkLabel (sl.ILLabel);
1924 ig.MarkLabel (next_test);
1925 next_test = ig.DefineLabel ();
1928 // If we are the default target
1930 if (sl.Label == null){
1931 ig.MarkLabel (default_target);
1932 default_found = true;
1934 object lit = sl.Converted;
1936 if (lit is NullLiteral){
1938 if (label_count == 1)
1939 ig.Emit (OpCodes.Br, next_test);
1944 StringConstant str = (StringConstant) lit;
1946 ig.Emit (OpCodes.Ldloc, val);
1947 ig.Emit (OpCodes.Ldstr, str.Value);
1948 if (label_count == 1)
1949 ig.Emit (OpCodes.Bne_Un, next_test);
1951 ig.Emit (OpCodes.Beq, sec_begin);
1953 ig.Emit (OpCodes.Ldloc, val);
1954 EmitObjectInteger (ig, lit);
1955 ig.Emit (OpCodes.Ceq);
1956 if (label_count == 1)
1957 ig.Emit (OpCodes.Brfalse, next_test);
1959 ig.Emit (OpCodes.Brtrue, sec_begin);
1963 if (label_count != 1 && ss != last_section)
1964 ig.Emit (OpCodes.Br, next_test);
1967 ig.MarkLabel (null_target);
1968 ig.MarkLabel (sec_begin);
1969 foreach (SwitchLabel sl in ss.Labels)
1970 ig.MarkLabel (sl.ILLabelCode);
1971 if (ss.Block.Emit (ec))
1972 pending_goto_end = false;
1975 pending_goto_end = true;
1979 if (!default_found){
1980 ig.MarkLabel (default_target);
1983 ig.MarkLabel (next_test);
1984 ig.MarkLabel (end_of_switch);
1989 public override bool Resolve (EmitContext ec)
1991 foreach (SwitchSection ss in Sections){
1992 if (ss.Block.Resolve (ec) != true)
1999 public override bool Emit (EmitContext ec)
2001 Expr = Expr.Resolve (ec);
2005 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
2006 if (new_expr == null){
2007 Report.Error (151, loc, "An integer type or string was expected for switch");
2012 SwitchType = new_expr.Type;
2014 if (!CheckSwitch (ec))
2017 // Store variable for comparission purposes
2018 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2020 ec.ig.Emit (OpCodes.Stloc, value);
2022 ILGenerator ig = ec.ig;
2024 default_target = ig.DefineLabel ();
2027 // Setup the codegen context
2029 Label old_end = ec.LoopEnd;
2030 Switch old_switch = ec.Switch;
2032 ec.LoopEnd = ig.DefineLabel ();
2037 if (SwitchType == TypeManager.string_type)
2038 all_return = SimpleSwitchEmit (ec, value);
2040 all_return = TableSwitchEmit (ec, value);
2042 // Restore context state.
2043 ig.MarkLabel (ec.LoopEnd);
2046 // Restore the previous context
2048 ec.LoopEnd = old_end;
2049 ec.Switch = old_switch;
2055 public class Lock : Statement {
2057 Statement Statement;
2059 public Lock (Expression expr, Statement stmt, Location l)
2066 public override bool Resolve (EmitContext ec)
2068 expr = expr.Resolve (ec);
2069 return Statement.Resolve (ec) && expr != null;
2072 public override bool Emit (EmitContext ec)
2074 Type type = expr.Type;
2077 if (type.IsValueType){
2078 Report.Error (185, loc, "lock statement requires the expression to be " +
2079 " a reference type (type is: `" +
2080 TypeManager.CSharpName (type) + "'");
2084 ILGenerator ig = ec.ig;
2085 LocalBuilder temp = ig.DeclareLocal (type);
2088 ig.Emit (OpCodes.Dup);
2089 ig.Emit (OpCodes.Stloc, temp);
2090 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2093 Label end = ig.BeginExceptionBlock ();
2094 bool old_in_try = ec.InTry;
2096 Label finish = ig.DefineLabel ();
2097 val = Statement.Emit (ec);
2098 ec.InTry = old_in_try;
2099 // ig.Emit (OpCodes.Leave, finish);
2101 ig.MarkLabel (finish);
2104 ig.BeginFinallyBlock ();
2105 ig.Emit (OpCodes.Ldloc, temp);
2106 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2107 ig.EndExceptionBlock ();
2113 public class Unchecked : Statement {
2114 public readonly Block Block;
2116 public Unchecked (Block b)
2121 public override bool Resolve (EmitContext ec)
2123 return Block.Resolve (ec);
2126 public override bool Emit (EmitContext ec)
2128 bool previous_state = ec.CheckState;
2129 bool previous_state_const = ec.ConstantCheckState;
2132 ec.CheckState = false;
2133 ec.ConstantCheckState = false;
2134 val = Block.Emit (ec);
2135 ec.CheckState = previous_state;
2136 ec.ConstantCheckState = previous_state_const;
2142 public class Checked : Statement {
2143 public readonly Block Block;
2145 public Checked (Block b)
2150 public override bool Resolve (EmitContext ec)
2152 bool previous_state = ec.CheckState;
2153 bool previous_state_const = ec.ConstantCheckState;
2155 ec.CheckState = true;
2156 ec.ConstantCheckState = true;
2157 bool ret = Block.Resolve (ec);
2158 ec.CheckState = previous_state;
2159 ec.ConstantCheckState = previous_state_const;
2164 public override bool Emit (EmitContext ec)
2166 bool previous_state = ec.CheckState;
2167 bool previous_state_const = ec.ConstantCheckState;
2170 ec.CheckState = true;
2171 ec.ConstantCheckState = true;
2172 val = Block.Emit (ec);
2173 ec.CheckState = previous_state;
2174 ec.ConstantCheckState = previous_state_const;
2180 public class Unsafe : Statement {
2181 public readonly Block Block;
2183 public Unsafe (Block b)
2188 public override bool Resolve (EmitContext ec)
2190 bool previous_state = ec.InUnsafe;
2194 val = Block.Resolve (ec);
2195 ec.InUnsafe = previous_state;
2200 public override bool Emit (EmitContext ec)
2202 bool previous_state = ec.InUnsafe;
2206 val = Block.Emit (ec);
2207 ec.InUnsafe = previous_state;
2216 public class Fixed : Statement {
2218 ArrayList declarators;
2219 Statement statement;
2221 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
2224 declarators = decls;
2229 public override bool Resolve (EmitContext ec)
2231 return statement.Resolve (ec);
2234 public override bool Emit (EmitContext ec)
2236 ILGenerator ig = ec.ig;
2239 t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2243 bool is_ret = false;
2245 foreach (Pair p in declarators){
2246 VariableInfo vi = (VariableInfo) p.First;
2247 Expression e = (Expression) p.Second;
2250 // The rules for the possible declarators are pretty wise,
2251 // but the production on the grammar is more concise.
2253 // So we have to enforce these rules here.
2255 // We do not resolve before doing the case 1 test,
2256 // because the grammar is explicit in that the token &
2257 // is present, so we need to test for this particular case.
2261 // Case 1: & object.
2263 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2264 Expression child = ((Unary) e).Expr;
2267 if (child is ParameterReference || child is LocalVariableReference){
2270 "No need to use fixed statement for parameters or " +
2271 "local variable declarations (address is already " +
2280 child = ((Unary) e).Expr;
2282 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2286 // Store pointer in pinned location
2289 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2291 is_ret = statement.Emit (ec);
2293 // Clear the pinned variable.
2294 ig.Emit (OpCodes.Ldc_I4_0);
2295 ig.Emit (OpCodes.Conv_U);
2296 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2308 if (e.Type.IsArray){
2309 Type array_type = e.Type.GetElementType ();
2313 // Provided that array_type is unmanaged,
2315 if (!TypeManager.VerifyUnManaged (array_type, loc))
2319 // and T* is implicitly convertible to the
2320 // pointer type given in the fixed statement.
2322 ArrayPtr array_ptr = new ArrayPtr (e);
2324 Expression converted = Expression.ConvertImplicitRequired (
2325 ec, array_ptr, vi.VariableType, loc);
2326 if (converted == null)
2330 // Store pointer in pinned location
2332 converted.Emit (ec);
2334 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2336 is_ret = statement.Emit (ec);
2338 // Clear the pinned variable.
2339 ig.Emit (OpCodes.Ldc_I4_0);
2340 ig.Emit (OpCodes.Conv_U);
2341 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2349 if (e.Type == TypeManager.string_type){
2350 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2351 TypeManager.MakePinned (pinned_string);
2354 ig.Emit (OpCodes.Stloc, pinned_string);
2356 Expression sptr = new StringPtr (pinned_string);
2357 Expression converted = Expression.ConvertImplicitRequired (
2358 ec, sptr, vi.VariableType, loc);
2360 if (converted == null)
2363 converted.Emit (ec);
2364 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2366 is_ret = statement.Emit (ec);
2368 // Clear the pinned variable
2369 ig.Emit (OpCodes.Ldnull);
2370 ig.Emit (OpCodes.Stloc, pinned_string);
2378 public class Catch {
2379 public readonly string Type;
2380 public readonly string Name;
2381 public readonly Block Block;
2382 public readonly Location Location;
2384 public Catch (string type, string name, Block block, Location l)
2393 public class Try : Statement {
2394 public readonly Block Fini, Block;
2395 public readonly ArrayList Specific;
2396 public readonly Catch General;
2399 // specific, general and fini might all be null.
2401 public Try (Block block, ArrayList specific, Catch general, Block fini)
2403 if (specific == null && general == null){
2404 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2408 this.Specific = specific;
2409 this.General = general;
2413 public override bool Resolve (EmitContext ec)
2417 if (General != null)
2418 if (!General.Block.Resolve (ec))
2421 foreach (Catch c in Specific){
2422 if (!c.Block.Resolve (ec))
2426 if (!Block.Resolve (ec))
2430 if (!Fini.Resolve (ec))
2436 public override bool Emit (EmitContext ec)
2438 ILGenerator ig = ec.ig;
2440 Label finish = ig.DefineLabel ();;
2443 end = ig.BeginExceptionBlock ();
2444 bool old_in_try = ec.InTry;
2446 returns = Block.Emit (ec);
2447 ec.InTry = old_in_try;
2450 // System.Reflection.Emit provides this automatically:
2451 // ig.Emit (OpCodes.Leave, finish);
2453 bool old_in_catch = ec.InCatch;
2455 DeclSpace ds = ec.DeclSpace;
2457 foreach (Catch c in Specific){
2458 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2461 if (catch_type == null)
2464 ig.BeginCatchBlock (catch_type);
2466 if (c.Name != null){
2467 vi = c.Block.GetVariableInfo (c.Name);
2469 Console.WriteLine ("This should not happen! variable does not exist in this block");
2470 Environment.Exit (0);
2473 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2475 ig.Emit (OpCodes.Pop);
2477 if (!c.Block.Emit (ec))
2481 if (General != null){
2482 ig.BeginCatchBlock (TypeManager.object_type);
2483 ig.Emit (OpCodes.Pop);
2484 if (!General.Block.Emit (ec))
2487 ec.InCatch = old_in_catch;
2489 ig.MarkLabel (finish);
2491 ig.BeginFinallyBlock ();
2492 bool old_in_finally = ec.InFinally;
2493 ec.InFinally = true;
2495 ec.InFinally = old_in_finally;
2498 ig.EndExceptionBlock ();
2501 // FIXME: Is this correct?
2502 // Replace with `returns' and check test-18, maybe we can
2503 // perform an optimization here.
2510 // FIXME: We still do not support the expression variant of the using
2513 public class Using : Statement {
2514 object expression_or_block;
2515 Statement Statement;
2517 public Using (object expression_or_block, Statement stmt, Location l)
2519 this.expression_or_block = expression_or_block;
2525 // Emits the code for the case of using using a local variable declaration.
2527 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2529 ILGenerator ig = ec.ig;
2530 Expression [] converted_vars;
2531 bool need_conv = false;
2532 Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2539 // The type must be an IDisposable or an implicit conversion
2542 converted_vars = new Expression [var_list.Count];
2543 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2544 foreach (DictionaryEntry e in var_list){
2545 Expression var = (Expression) e.Key;
2547 var = var.Resolve (ec);
2551 converted_vars [i] = Expression.ConvertImplicit (
2552 ec, var, TypeManager.idisposable_type, loc);
2554 if (converted_vars [i] == null)
2562 bool old_in_try = ec.InTry;
2565 foreach (DictionaryEntry e in var_list){
2566 LocalVariableReference var = (LocalVariableReference) e.Key;
2567 Expression expr = (Expression) e.Value;
2570 a = new Assign (var, expr, loc);
2573 converted_vars [i] = var;
2579 ((ExpressionStatement) a).EmitStatement (ec);
2581 ig.BeginExceptionBlock ();
2586 Statement.Emit (ec);
2587 ec.InTry = old_in_try;
2589 bool old_in_finally = ec.InFinally;
2590 ec.InFinally = true;
2591 var_list.Reverse ();
2592 foreach (DictionaryEntry e in var_list){
2593 LocalVariableReference var = (LocalVariableReference) e.Key;
2594 Label skip = ig.DefineLabel ();
2597 ig.BeginFinallyBlock ();
2600 ig.Emit (OpCodes.Brfalse, skip);
2601 converted_vars [i].Emit (ec);
2602 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2603 ig.MarkLabel (skip);
2604 ig.EndExceptionBlock ();
2606 ec.InFinally = old_in_finally;
2611 bool EmitExpression (EmitContext ec, Expression expr)
2613 Type expr_type = expr.Type;
2614 Expression conv = null;
2616 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2617 conv = Expression.ConvertImplicit (
2618 ec, expr, TypeManager.idisposable_type, loc);
2625 // Make a copy of the expression and operate on that.
2627 ILGenerator ig = ec.ig;
2628 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2633 ig.Emit (OpCodes.Stloc, local_copy);
2635 bool old_in_try = ec.InTry;
2637 ig.BeginExceptionBlock ();
2638 Statement.Emit (ec);
2639 ec.InTry = old_in_try;
2641 Label skip = ig.DefineLabel ();
2642 bool old_in_finally = ec.InFinally;
2643 ig.BeginFinallyBlock ();
2644 ig.Emit (OpCodes.Ldloc, local_copy);
2645 ig.Emit (OpCodes.Brfalse, skip);
2646 ig.Emit (OpCodes.Ldloc, local_copy);
2647 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2648 ig.MarkLabel (skip);
2649 ec.InFinally = old_in_finally;
2650 ig.EndExceptionBlock ();
2655 public override bool Resolve (EmitContext ec)
2657 return Statement.Resolve (ec);
2660 public override bool Emit (EmitContext ec)
2662 if (expression_or_block is DictionaryEntry){
2663 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2664 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2666 return EmitLocalVariableDecls (ec, t, var_list);
2667 } if (expression_or_block is Expression){
2668 Expression e = (Expression) expression_or_block;
2674 return EmitExpression (ec, e);
2681 /// Implementation of the foreach C# statement
2683 public class Foreach : Statement {
2685 LocalVariableReference variable;
2687 Statement statement;
2689 public Foreach (string type, LocalVariableReference var, Expression expr,
2690 Statement stmt, Location l)
2693 this.variable = var;
2699 public override bool Resolve (EmitContext ec)
2701 expr = expr.Resolve (ec);
2702 return statement.Resolve (ec) && expr != null;
2706 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2708 static MethodInfo FetchMethodMoveNext (Type t)
2710 MemberInfo [] move_next_list;
2712 move_next_list = TypeContainer.FindMembers (
2713 t, MemberTypes.Method,
2714 BindingFlags.Public | BindingFlags.Instance,
2715 Type.FilterName, "MoveNext");
2716 if (move_next_list == null || move_next_list.Length == 0)
2719 foreach (MemberInfo m in move_next_list){
2720 MethodInfo mi = (MethodInfo) m;
2723 args = TypeManager.GetArgumentTypes (mi);
2724 if (args != null && args.Length == 0){
2725 if (mi.ReturnType == TypeManager.bool_type)
2733 // Retrieves a `public T get_Current ()' method from the Type `t'
2735 static MethodInfo FetchMethodGetCurrent (Type t)
2737 MemberInfo [] move_next_list;
2739 move_next_list = TypeContainer.FindMembers (
2740 t, MemberTypes.Method,
2741 BindingFlags.Public | BindingFlags.Instance,
2742 Type.FilterName, "get_Current");
2743 if (move_next_list == null || move_next_list.Length == 0)
2746 foreach (MemberInfo m in move_next_list){
2747 MethodInfo mi = (MethodInfo) m;
2750 args = TypeManager.GetArgumentTypes (mi);
2751 if (args != null && args.Length == 0)
2758 // This struct records the helper methods used by the Foreach construct
2760 class ForeachHelperMethods {
2761 public EmitContext ec;
2762 public MethodInfo get_enumerator;
2763 public MethodInfo move_next;
2764 public MethodInfo get_current;
2766 public ForeachHelperMethods (EmitContext ec)
2772 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2777 if (!(m is MethodInfo))
2780 if (m.Name != "GetEnumerator")
2783 MethodInfo mi = (MethodInfo) m;
2784 Type [] args = TypeManager.GetArgumentTypes (mi);
2786 if (args.Length != 0)
2789 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2790 EmitContext ec = hm.ec;
2793 // Check whether GetEnumerator is accessible to us
2795 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2797 Type declaring = mi.DeclaringType;
2798 if (prot == MethodAttributes.Private){
2799 if (declaring != ec.ContainerType)
2801 } else if (prot == MethodAttributes.FamANDAssem){
2802 // If from a different assembly, false
2803 if (!(mi is MethodBuilder))
2806 // Are we being invoked from the same class, or from a derived method?
2808 if (ec.ContainerType != declaring){
2809 if (!ec.ContainerType.IsSubclassOf (declaring))
2812 } else if (prot == MethodAttributes.FamORAssem){
2813 if (!(mi is MethodBuilder ||
2814 ec.ContainerType == declaring ||
2815 ec.ContainerType.IsSubclassOf (declaring)))
2817 } if (prot == MethodAttributes.Family){
2818 if (!(ec.ContainerType == declaring ||
2819 ec.ContainerType.IsSubclassOf (declaring)))
2824 // Ok, we can access it, now make sure that we can do something
2825 // with this `GetEnumerator'
2827 if (mi.ReturnType == TypeManager.ienumerator_type ||
2828 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2829 hm.move_next = TypeManager.bool_movenext_void;
2830 hm.get_current = TypeManager.object_getcurrent_void;
2835 // Ok, so they dont return an IEnumerable, we will have to
2836 // find if they support the GetEnumerator pattern.
2838 Type return_type = mi.ReturnType;
2840 hm.move_next = FetchMethodMoveNext (return_type);
2841 if (hm.move_next == null)
2843 hm.get_current = FetchMethodGetCurrent (return_type);
2844 if (hm.get_current == null)
2851 /// This filter is used to find the GetEnumerator method
2852 /// on which IEnumerator operates
2854 static MemberFilter FilterEnumerator;
2858 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2861 void error1579 (Type t)
2863 Report.Error (1579, loc,
2864 "foreach statement cannot operate on variables of type `" +
2865 t.FullName + "' because that class does not provide a " +
2866 " GetEnumerator method or it is inaccessible");
2869 static bool TryType (Type t, ForeachHelperMethods hm)
2873 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2874 BindingFlags.Public | BindingFlags.NonPublic |
2875 BindingFlags.Instance,
2876 FilterEnumerator, hm);
2878 if (mi == null || mi.Length == 0)
2881 hm.get_enumerator = (MethodInfo) mi [0];
2886 // Looks for a usable GetEnumerator in the Type, and if found returns
2887 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2889 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2891 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2893 if (TryType (t, hm))
2897 // Now try to find the method in the interfaces
2900 Type [] ifaces = t.GetInterfaces ();
2902 foreach (Type i in ifaces){
2903 if (TryType (i, hm))
2908 // Since TypeBuilder.GetInterfaces only returns the interface
2909 // types for this type, we have to keep looping, but once
2910 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2911 // done, because it returns all the types
2913 if ((t is TypeBuilder))
2923 // FIXME: possible optimization.
2924 // We might be able to avoid creating `empty' if the type is the sam
2926 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2928 ILGenerator ig = ec.ig;
2929 LocalBuilder enumerator, disposable;
2930 Expression empty = new EmptyExpression ();
2934 // FIXME: maybe we can apply the same trick we do in the
2935 // array handling to avoid creating empty and conv in some cases.
2937 // Although it is not as important in this case, as the type
2938 // will not likely be object (what the enumerator will return).
2940 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2944 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2945 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2948 // Instantiate the enumerator
2950 if (expr.Type.IsValueType){
2951 if (expr is IMemoryLocation){
2952 IMemoryLocation ml = (IMemoryLocation) expr;
2954 ml.AddressOf (ec, AddressOp.Load);
2956 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2957 " does not implement IMemoryLocation");
2958 ig.Emit (OpCodes.Call, hm.get_enumerator);
2961 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2963 ig.Emit (OpCodes.Stloc, enumerator);
2966 // Protect the code in a try/finalize block, so that
2967 // if the beast implement IDisposable, we get rid of it
2969 Label l = ig.BeginExceptionBlock ();
2970 bool old_in_try = ec.InTry;
2973 Label end_try = ig.DefineLabel ();
2975 ig.MarkLabel (ec.LoopBegin);
2976 ig.Emit (OpCodes.Ldloc, enumerator);
2977 ig.Emit (OpCodes.Callvirt, hm.move_next);
2978 ig.Emit (OpCodes.Brfalse, end_try);
2979 ig.Emit (OpCodes.Ldloc, enumerator);
2980 ig.Emit (OpCodes.Callvirt, hm.get_current);
2981 variable.EmitAssign (ec, conv);
2982 statement.Emit (ec);
2983 ig.Emit (OpCodes.Br, ec.LoopBegin);
2984 ig.MarkLabel (end_try);
2985 ec.InTry = old_in_try;
2987 // The runtime provides this for us.
2988 // ig.Emit (OpCodes.Leave, end);
2991 // Now the finally block
2993 Label end_finally = ig.DefineLabel ();
2994 bool old_in_finally = ec.InFinally;
2995 ec.InFinally = true;
2996 ig.BeginFinallyBlock ();
2998 ig.Emit (OpCodes.Ldloc, enumerator);
2999 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
3000 ig.Emit (OpCodes.Stloc, disposable);
3001 ig.Emit (OpCodes.Ldloc, disposable);
3002 ig.Emit (OpCodes.Brfalse, end_finally);
3003 ig.Emit (OpCodes.Ldloc, disposable);
3004 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3005 ig.MarkLabel (end_finally);
3006 ec.InFinally = old_in_finally;
3008 // The runtime generates this anyways.
3009 // ig.Emit (OpCodes.Endfinally);
3011 ig.EndExceptionBlock ();
3013 ig.MarkLabel (ec.LoopEnd);
3018 // FIXME: possible optimization.
3019 // We might be able to avoid creating `empty' if the type is the sam
3021 bool EmitArrayForeach (EmitContext ec, Type var_type)
3023 Type array_type = expr.Type;
3024 Type element_type = array_type.GetElementType ();
3025 Expression conv = null;
3026 Expression empty = new EmptyExpression (element_type);
3028 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
3032 int rank = array_type.GetArrayRank ();
3033 ILGenerator ig = ec.ig;
3035 LocalBuilder copy = ig.DeclareLocal (array_type);
3038 // Make our copy of the array
3041 ig.Emit (OpCodes.Stloc, copy);
3044 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
3048 ig.Emit (OpCodes.Ldc_I4_0);
3049 ig.Emit (OpCodes.Stloc, counter);
3050 test = ig.DefineLabel ();
3051 ig.Emit (OpCodes.Br, test);
3053 loop = ig.DefineLabel ();
3054 ig.MarkLabel (loop);
3056 ig.Emit (OpCodes.Ldloc, copy);
3057 ig.Emit (OpCodes.Ldloc, counter);
3058 ArrayAccess.EmitLoadOpcode (ig, var_type);
3060 variable.EmitAssign (ec, conv);
3062 statement.Emit (ec);
3064 ig.MarkLabel (ec.LoopBegin);
3065 ig.Emit (OpCodes.Ldloc, counter);
3066 ig.Emit (OpCodes.Ldc_I4_1);
3067 ig.Emit (OpCodes.Add);
3068 ig.Emit (OpCodes.Stloc, counter);
3070 ig.MarkLabel (test);
3071 ig.Emit (OpCodes.Ldloc, counter);
3072 ig.Emit (OpCodes.Ldloc, copy);
3073 ig.Emit (OpCodes.Ldlen);
3074 ig.Emit (OpCodes.Conv_I4);
3075 ig.Emit (OpCodes.Blt, loop);
3077 LocalBuilder [] dim_len = new LocalBuilder [rank];
3078 LocalBuilder [] dim_count = new LocalBuilder [rank];
3079 Label [] loop = new Label [rank];
3080 Label [] test = new Label [rank];
3083 for (dim = 0; dim < rank; dim++){
3084 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
3085 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
3086 test [dim] = ig.DefineLabel ();
3087 loop [dim] = ig.DefineLabel ();
3090 for (dim = 0; dim < rank; dim++){
3091 ig.Emit (OpCodes.Ldloc, copy);
3092 IntLiteral.EmitInt (ig, dim);
3093 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
3094 ig.Emit (OpCodes.Stloc, dim_len [dim]);
3097 for (dim = 0; dim < rank; dim++){
3098 ig.Emit (OpCodes.Ldc_I4_0);
3099 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3100 ig.Emit (OpCodes.Br, test [dim]);
3101 ig.MarkLabel (loop [dim]);
3104 ig.Emit (OpCodes.Ldloc, copy);
3105 for (dim = 0; dim < rank; dim++)
3106 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3109 // FIXME: Maybe we can cache the computation of `get'?
3111 Type [] args = new Type [rank];
3114 for (int i = 0; i < rank; i++)
3115 args [i] = TypeManager.int32_type;
3117 ModuleBuilder mb = CodeGen.ModuleBuilder;
3118 get = mb.GetArrayMethod (
3120 CallingConventions.HasThis| CallingConventions.Standard,
3122 ig.Emit (OpCodes.Call, get);
3123 variable.EmitAssign (ec, conv);
3124 statement.Emit (ec);
3125 ig.MarkLabel (ec.LoopBegin);
3126 for (dim = rank - 1; dim >= 0; dim--){
3127 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3128 ig.Emit (OpCodes.Ldc_I4_1);
3129 ig.Emit (OpCodes.Add);
3130 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3132 ig.MarkLabel (test [dim]);
3133 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3134 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
3135 ig.Emit (OpCodes.Blt, loop [dim]);
3138 ig.MarkLabel (ec.LoopEnd);
3143 public override bool Emit (EmitContext ec)
3148 var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
3149 if (var_type == null)
3153 // We need an instance variable. Not sure this is the best
3154 // way of doing this.
3156 // FIXME: When we implement propertyaccess, will those turn
3157 // out to return values in ExprClass? I think they should.
3159 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3160 expr.eclass == ExprClass.PropertyAccess)){
3161 error1579 (expr.Type);
3165 ILGenerator ig = ec.ig;
3167 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
3168 bool old_inloop = ec.InLoop;
3169 ec.LoopBegin = ig.DefineLabel ();
3170 ec.LoopEnd = ig.DefineLabel ();
3173 if (expr.Type.IsArray)
3174 ret_val = EmitArrayForeach (ec, var_type);
3176 ForeachHelperMethods hm;
3178 hm = ProbeCollectionType (ec, expr.Type);
3180 error1579 (expr.Type);
3184 ret_val = EmitCollectionForeach (ec, var_type, hm);
3187 ec.LoopBegin = old_begin;
3188 ec.LoopEnd = old_end;
3189 ec.InLoop = old_inloop;