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;
244 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
246 ec.LoopBegin = ig.DefineLabel ();
247 ec.LoopEnd = ig.DefineLabel ();
249 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
253 EmbeddedStatement.Emit (ec);
254 bool breaks = ec.Breaks;
255 ig.MarkLabel (ec.LoopBegin);
258 // Dead code elimination
260 if (expr is BoolConstant){
261 bool res = ((BoolConstant) expr).Value;
264 ec.ig.Emit (OpCodes.Br, loop);
266 EmitBoolExpression (ec, expr, loop, true);
268 ig.MarkLabel (ec.LoopEnd);
270 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
271 ec.LoopBegin = old_begin;
272 ec.LoopEnd = old_end;
273 ec.InLoop = old_inloop;
274 ec.Breaks = old_breaks;
277 // Inform whether we are infinite or not
279 if (expr is BoolConstant){
280 BoolConstant bc = (BoolConstant) expr;
282 if (bc.Value == true)
283 return breaks == false;
290 public class While : Statement {
291 public Expression expr;
292 public readonly Statement Statement;
294 public While (Expression boolExpr, Statement statement, Location l)
296 this.expr = boolExpr;
297 Statement = statement;
301 public override bool Resolve (EmitContext ec)
303 expr = ResolveBoolean (ec, expr, loc);
307 return Statement.Resolve (ec);
310 public override bool Emit (EmitContext ec)
312 ILGenerator ig = ec.ig;
313 Label old_begin = ec.LoopBegin;
314 Label old_end = ec.LoopEnd;
315 bool old_inloop = ec.InLoop;
316 bool old_breaks = ec.Breaks;
317 Label while_loop = ig.DefineLabel ();
318 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
321 ec.LoopBegin = ig.DefineLabel ();
322 ec.LoopEnd = ig.DefineLabel ();
324 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
326 ig.Emit (OpCodes.Br, ec.LoopBegin);
327 ig.MarkLabel (while_loop);
330 // Inform whether we are infinite or not
332 if (expr is BoolConstant){
333 BoolConstant bc = (BoolConstant) expr;
335 ig.MarkLabel (ec.LoopBegin);
336 if (bc.Value == false){
337 Warning_DeadCodeFound (Statement.loc);
345 ig.Emit (OpCodes.Br, ec.LoopBegin);
348 // Inform that we are infinite (ie, `we return'), only
349 // if we do not `break' inside the code.
351 ret = breaks == false;
353 ig.MarkLabel (ec.LoopEnd);
357 ig.MarkLabel (ec.LoopBegin);
359 EmitBoolExpression (ec, expr, while_loop, true);
360 ig.MarkLabel (ec.LoopEnd);
365 ec.LoopBegin = old_begin;
366 ec.LoopEnd = old_end;
367 ec.InLoop = old_inloop;
368 ec.Breaks = old_breaks;
369 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
375 public class For : Statement {
377 readonly Statement InitStatement;
378 readonly Statement Increment;
379 readonly Statement Statement;
381 public For (Statement initStatement,
387 InitStatement = initStatement;
389 Increment = increment;
390 Statement = statement;
394 public override bool Resolve (EmitContext ec)
399 Test = ResolveBoolean (ec, Test, loc);
404 if (InitStatement != null){
405 if (!InitStatement.Resolve (ec))
409 if (Increment != null){
410 if (!Increment.Resolve (ec))
414 return Statement.Resolve (ec) && ok;
417 public override bool Emit (EmitContext ec)
419 ILGenerator ig = ec.ig;
420 Label old_begin = ec.LoopBegin;
421 Label old_end = ec.LoopEnd;
422 bool old_inloop = ec.InLoop;
423 bool old_breaks = ec.Breaks;
424 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
425 Label loop = ig.DefineLabel ();
426 Label test = ig.DefineLabel ();
428 if (InitStatement != null)
429 if (! (InitStatement is EmptyStatement))
430 InitStatement.Emit (ec);
432 ec.LoopBegin = ig.DefineLabel ();
433 ec.LoopEnd = ig.DefineLabel ();
435 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
437 ig.Emit (OpCodes.Br, test);
441 bool breaks = ec.Breaks;
443 ig.MarkLabel (ec.LoopBegin);
444 if (!(Increment is EmptyStatement))
449 // If test is null, there is no test, and we are just
453 EmitBoolExpression (ec, Test, loop, true);
455 ig.Emit (OpCodes.Br, loop);
456 ig.MarkLabel (ec.LoopEnd);
458 ec.LoopBegin = old_begin;
459 ec.LoopEnd = old_end;
460 ec.InLoop = old_inloop;
461 ec.Breaks = old_breaks;
462 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
465 // Inform whether we are infinite or not
468 if (Test is BoolConstant){
469 BoolConstant bc = (BoolConstant) Test;
472 return breaks == false;
480 public class StatementExpression : Statement {
483 public StatementExpression (ExpressionStatement expr, Location l)
489 public override bool Resolve (EmitContext ec)
491 expr = (Expression) expr.Resolve (ec);
495 public override bool Emit (EmitContext ec)
497 ILGenerator ig = ec.ig;
499 if (expr is ExpressionStatement)
500 ((ExpressionStatement) expr).EmitStatement (ec);
503 ig.Emit (OpCodes.Pop);
509 public override string ToString ()
511 return "StatementExpression (" + expr + ")";
516 /// Implements the return statement
518 public class Return : Statement {
519 public Expression Expr;
521 public Return (Expression expr, Location l)
527 public override bool Resolve (EmitContext ec)
530 Expr = Expr.Resolve (ec);
537 public override bool Emit (EmitContext ec)
540 Report.Error (157,loc,"Control can not leave the body of the finally block");
544 if (ec.ReturnType == null){
546 Report.Error (127, loc, "Return with a value not allowed here");
551 Report.Error (126, loc, "An object of type `" +
552 TypeManager.CSharpName (ec.ReturnType) + "' is " +
553 "expected for the return statement");
557 if (Expr.Type != ec.ReturnType)
558 Expr = Expression.ConvertImplicitRequired (
559 ec, Expr, ec.ReturnType, loc);
566 if (ec.InTry || ec.InCatch)
567 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
570 if (ec.InTry || ec.InCatch) {
571 if (!ec.HasReturnLabel) {
572 ec.ReturnLabel = ec.ig.DefineLabel ();
573 ec.HasReturnLabel = true;
575 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
577 ec.ig.Emit (OpCodes.Ret);
583 public class Goto : Statement {
587 public override bool Resolve (EmitContext ec)
592 public Goto (Block parent_block, string label, Location l)
594 block = parent_block;
599 public string Target {
605 public override bool Emit (EmitContext ec)
607 LabeledStatement label = block.LookupLabel (target);
611 // Maybe we should catch this before?
615 "No such label `" + target + "' in this scope");
618 Label l = label.LabelTarget (ec);
619 ec.ig.Emit (OpCodes.Br, l);
625 public class LabeledStatement : Statement {
630 public LabeledStatement (string label_name)
632 this.label_name = label_name;
635 public Label LabelTarget (EmitContext ec)
639 label = ec.ig.DefineLabel ();
645 public override bool Emit (EmitContext ec)
648 ec.ig.MarkLabel (label);
656 /// `goto default' statement
658 public class GotoDefault : Statement {
660 public GotoDefault (Location l)
665 public override bool Emit (EmitContext ec)
667 if (ec.Switch == null){
668 Report.Error (153, loc, "goto default is only valid in a switch statement");
672 if (!ec.Switch.GotDefault){
673 Report.Error (159, loc, "No default target on switch statement");
676 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
682 /// `goto case' statement
684 public class GotoCase : Statement {
687 public GotoCase (Expression e, Location l)
693 public override bool Emit (EmitContext ec)
695 if (ec.Switch == null){
696 Report.Error (153, loc, "goto case is only valid in a switch statement");
700 expr = expr.Resolve (ec);
704 if (!(expr is Constant)){
705 Report.Error (159, loc, "Target expression for goto case is not constant");
709 object val = Expression.ConvertIntLiteral (
710 (Constant) expr, ec.Switch.SwitchType, loc);
715 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
720 "No such label 'case " + val + "': for the goto case");
723 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
728 public class Throw : Statement {
731 public Throw (Expression expr, Location l)
737 public override bool Resolve (EmitContext ec)
740 expr = expr.Resolve (ec);
747 public override bool Emit (EmitContext ec)
751 ec.ig.Emit (OpCodes.Rethrow);
755 "A throw statement with no argument is only " +
756 "allowed in a catch clause");
763 ec.ig.Emit (OpCodes.Throw);
769 public class Break : Statement {
771 public Break (Location l)
776 public override bool Emit (EmitContext ec)
778 ILGenerator ig = ec.ig;
780 if (ec.InLoop == false && ec.Switch == null){
781 Report.Error (139, loc, "No enclosing loop or switch to continue to");
786 if (ec.InTry || ec.InCatch)
787 ig.Emit (OpCodes.Leave, ec.LoopEnd);
789 ig.Emit (OpCodes.Br, ec.LoopEnd);
795 public class Continue : Statement {
797 public Continue (Location l)
802 public override bool Emit (EmitContext ec)
804 Label begin = ec.LoopBegin;
807 Report.Error (139, loc, "No enclosing loop to continue to");
812 // UGH: Non trivial. This Br might cross a try/catch boundary
816 // try { ... } catch { continue; }
820 // try {} catch { while () { continue; }}
822 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
823 ec.ig.Emit (OpCodes.Leave, begin);
824 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
825 throw new Exception ("Should never happen");
827 ec.ig.Emit (OpCodes.Br, begin);
832 public class VariableInfo {
833 public Expression Type;
834 public LocalBuilder LocalBuilder;
835 public Type VariableType;
836 public readonly Location Location;
839 public bool Assigned;
840 public bool ReadOnly;
842 public VariableInfo (Expression type, Location l)
849 public void MakePinned ()
851 TypeManager.MakePinned (LocalBuilder);
856 /// Block represents a C# block.
860 /// This class is used in a number of places: either to represent
861 /// explicit blocks that the programmer places or implicit blocks.
863 /// Implicit blocks are used as labels or to introduce variable
866 public class Block : Statement {
867 public readonly Block Parent;
868 public readonly bool Implicit;
869 public readonly Location StartLocation;
870 public Location EndLocation;
873 // The statements in this block
875 ArrayList statements;
878 // An array of Blocks. We keep track of children just
879 // to generate the local variable declarations.
881 // Statements and child statements are handled through the
887 // Labels. (label, block) pairs.
892 // Keeps track of (name, type) pairs
897 // Keeps track of constants
901 // Maps variable names to ILGenerator.LocalBuilders
903 Hashtable local_builders;
911 public Block (Block parent)
912 : this (parent, false, Location.Null, Location.Null)
915 public Block (Block parent, bool implicit_block)
916 : this (parent, implicit_block, Location.Null, Location.Null)
919 public Block (Block parent, Location start, Location end)
920 : this (parent, false, start, end)
923 public Block (Block parent, bool implicit_block, Location start, Location end)
926 parent.AddChild (this);
928 this.Parent = parent;
929 this.Implicit = implicit_block;
930 this.StartLocation = start;
931 this.EndLocation = end;
934 statements = new ArrayList ();
943 void AddChild (Block b)
945 if (children == null)
946 children = new ArrayList ();
951 public void SetEndLocation (Location loc)
957 /// Adds a label to the current block.
961 /// false if the name already exists in this block. true
965 public bool AddLabel (string name, LabeledStatement target)
968 labels = new Hashtable ();
969 if (labels.Contains (name))
972 labels.Add (name, target);
976 public LabeledStatement LookupLabel (string name)
979 if (labels.Contains (name))
980 return ((LabeledStatement) labels [name]);
984 return Parent.LookupLabel (name);
989 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
991 if (variables == null)
992 variables = new Hashtable ();
994 if (GetVariableType (name) != null)
999 Parameter p = pars.GetParameterByName (name, out idx);
1004 VariableInfo vi = new VariableInfo (type, l);
1006 variables.Add (name, vi);
1008 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1012 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1014 if (AddVariable (type, name, pars, l) == null)
1017 if (constants == null)
1018 constants = new Hashtable ();
1020 constants.Add (name, value);
1024 public Hashtable Variables {
1030 public VariableInfo GetVariableInfo (string name)
1032 if (variables != null) {
1034 temp = variables [name];
1037 return (VariableInfo) temp;
1042 return Parent.GetVariableInfo (name);
1047 public Expression GetVariableType (string name)
1049 VariableInfo vi = GetVariableInfo (name);
1057 public Expression GetConstantExpression (string name)
1059 if (constants != null) {
1061 temp = constants [name];
1064 return (Expression) temp;
1068 return Parent.GetConstantExpression (name);
1074 /// True if the variable named @name has been defined
1077 public bool IsVariableDefined (string name)
1079 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1080 if (variables != null) {
1081 if (variables.Contains (name))
1086 return Parent.IsVariableDefined (name);
1092 /// True if the variable named @name is a constant
1094 public bool IsConstant (string name)
1096 Expression e = null;
1098 e = GetConstantExpression (name);
1104 /// Use to fetch the statement associated with this label
1106 public Statement this [string name] {
1108 return (Statement) labels [name];
1113 /// A list of labels that were not used within this block
1115 public string [] GetUnreferenced ()
1117 // FIXME: Implement me
1121 public void AddStatement (Statement s)
1139 /// Emits the variable declarations and labels.
1142 /// tc: is our typecontainer (to resolve type references)
1143 /// ig: is the code generator:
1144 /// toplevel: the toplevel block. This is used for checking
1145 /// that no two labels with the same name are used.
1147 public void EmitMeta (EmitContext ec, Block toplevel)
1149 DeclSpace ds = ec.DeclSpace;
1150 ILGenerator ig = ec.ig;
1153 // Process this block variables
1155 if (variables != null){
1156 local_builders = new Hashtable ();
1158 foreach (DictionaryEntry de in variables){
1159 string name = (string) de.Key;
1160 VariableInfo vi = (VariableInfo) de.Value;
1163 t = ds.ResolveType (vi.Type, false, vi.Location);
1167 vi.VariableType = t;
1168 vi.LocalBuilder = ig.DeclareLocal (t);
1170 if (CodeGen.SymbolWriter != null)
1171 vi.LocalBuilder.SetLocalSymInfo (name);
1173 if (constants == null)
1176 Expression cv = (Expression) constants [name];
1180 Expression e = cv.Resolve (ec);
1184 if (!(e is Constant)){
1185 Report.Error (133, vi.Location,
1186 "The expression being assigned to `" +
1187 name + "' must be constant (" + e + ")");
1191 constants.Remove (name);
1192 constants.Add (name, e);
1197 // Now, handle the children
1199 if (children != null){
1200 foreach (Block b in children)
1201 b.EmitMeta (ec, toplevel);
1205 public void UsageWarning ()
1209 if (variables != null){
1210 foreach (DictionaryEntry de in variables){
1211 VariableInfo vi = (VariableInfo) de.Value;
1216 name = (string) de.Key;
1220 219, vi.Location, "The variable `" + name +
1221 "' is assigned but its value is never used");
1224 168, vi.Location, "The variable `" +
1226 "' is declared but never used");
1231 if (children != null)
1232 foreach (Block b in children)
1236 public override bool Resolve (EmitContext ec)
1238 Block prev_block = ec.CurrentBlock;
1241 ec.CurrentBlock = this;
1242 foreach (Statement s in statements){
1243 if (s.Resolve (ec) == false)
1247 ec.CurrentBlock = prev_block;
1251 public override bool Emit (EmitContext ec)
1253 bool is_ret = false;
1254 Block prev_block = ec.CurrentBlock;
1256 ec.CurrentBlock = this;
1258 if (CodeGen.SymbolWriter != null) {
1259 ec.Mark (StartLocation);
1261 foreach (Statement s in statements) {
1264 is_ret = s.Emit (ec);
1267 ec.Mark (EndLocation);
1269 foreach (Statement s in statements)
1270 is_ret = s.Emit (ec);
1273 ec.CurrentBlock = prev_block;
1278 public class SwitchLabel {
1281 public Location loc;
1282 public Label ILLabel;
1283 public Label ILLabelCode;
1286 // if expr == null, then it is the default case.
1288 public SwitchLabel (Expression expr, Location l)
1294 public Expression Label {
1300 public object Converted {
1307 // Resolves the expression, reduces it to a literal if possible
1308 // and then converts it to the requested type.
1310 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1312 ILLabel = ec.ig.DefineLabel ();
1313 ILLabelCode = ec.ig.DefineLabel ();
1318 Expression e = label.Resolve (ec);
1323 if (!(e is Constant)){
1324 Console.WriteLine ("Value is: " + label);
1325 Report.Error (150, loc, "A constant value is expected");
1329 if (e is StringConstant || e is NullLiteral){
1330 if (required_type == TypeManager.string_type){
1332 ILLabel = ec.ig.DefineLabel ();
1337 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1338 if (converted == null)
1345 public class SwitchSection {
1346 // An array of SwitchLabels.
1347 public readonly ArrayList Labels;
1348 public readonly Block Block;
1350 public SwitchSection (ArrayList labels, Block block)
1357 public class Switch : Statement {
1358 public readonly ArrayList Sections;
1359 public Expression Expr;
1362 /// Maps constants whose type type SwitchType to their SwitchLabels.
1364 public Hashtable Elements;
1367 /// The governing switch type
1369 public Type SwitchType;
1375 Label default_target;
1378 // The types allowed to be implicitly cast from
1379 // on the governing type
1381 static Type [] allowed_types;
1383 public Switch (Expression e, ArrayList sects, Location l)
1390 public bool GotDefault {
1396 public Label DefaultTarget {
1398 return default_target;
1403 // Determines the governing type for a switch. The returned
1404 // expression might be the expression from the switch, or an
1405 // expression that includes any potential conversions to the
1406 // integral types or to string.
1408 Expression SwitchGoverningType (EmitContext ec, Type t)
1410 if (t == TypeManager.int32_type ||
1411 t == TypeManager.uint32_type ||
1412 t == TypeManager.char_type ||
1413 t == TypeManager.byte_type ||
1414 t == TypeManager.sbyte_type ||
1415 t == TypeManager.ushort_type ||
1416 t == TypeManager.short_type ||
1417 t == TypeManager.uint64_type ||
1418 t == TypeManager.int64_type ||
1419 t == TypeManager.string_type ||
1420 t == TypeManager.bool_type ||
1421 t.IsSubclassOf (TypeManager.enum_type))
1424 if (allowed_types == null){
1425 allowed_types = new Type [] {
1426 TypeManager.sbyte_type,
1427 TypeManager.byte_type,
1428 TypeManager.short_type,
1429 TypeManager.ushort_type,
1430 TypeManager.int32_type,
1431 TypeManager.uint32_type,
1432 TypeManager.int64_type,
1433 TypeManager.uint64_type,
1434 TypeManager.char_type,
1435 TypeManager.bool_type,
1436 TypeManager.string_type
1441 // Try to find a *user* defined implicit conversion.
1443 // If there is no implicit conversion, or if there are multiple
1444 // conversions, we have to report an error
1446 Expression converted = null;
1447 foreach (Type tt in allowed_types){
1450 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1454 if (converted != null){
1455 Report.Error (-12, loc, "More than one conversion to an integral " +
1456 " type exists for type `" +
1457 TypeManager.CSharpName (Expr.Type)+"'");
1465 void error152 (string n)
1468 152, "The label `" + n + ":' " +
1469 "is already present on this switch statement");
1473 // Performs the basic sanity checks on the switch statement
1474 // (looks for duplicate keys and non-constant expressions).
1476 // It also returns a hashtable with the keys that we will later
1477 // use to compute the switch tables
1479 bool CheckSwitch (EmitContext ec)
1483 Elements = new Hashtable ();
1485 got_default = false;
1487 if (TypeManager.IsEnumType (SwitchType)){
1488 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1490 compare_type = SwitchType;
1492 foreach (SwitchSection ss in Sections){
1493 foreach (SwitchLabel sl in ss.Labels){
1494 if (!sl.ResolveAndReduce (ec, SwitchType)){
1499 if (sl.Label == null){
1501 error152 ("default");
1508 object key = sl.Converted;
1510 if (key is Constant)
1511 key = ((Constant) key).GetValue ();
1514 key = NullLiteral.Null;
1516 string lname = null;
1517 if (compare_type == TypeManager.uint64_type){
1518 ulong v = (ulong) key;
1520 if (Elements.Contains (v))
1521 lname = v.ToString ();
1523 Elements.Add (v, sl);
1524 } else if (compare_type == TypeManager.int64_type){
1525 long v = (long) key;
1527 if (Elements.Contains (v))
1528 lname = v.ToString ();
1530 Elements.Add (v, sl);
1531 } else if (compare_type == TypeManager.uint32_type){
1532 uint v = (uint) key;
1534 if (Elements.Contains (v))
1535 lname = v.ToString ();
1537 Elements.Add (v, sl);
1538 } else if (compare_type == TypeManager.char_type){
1539 char v = (char) key;
1541 if (Elements.Contains (v))
1542 lname = v.ToString ();
1544 Elements.Add (v, sl);
1545 } else if (compare_type == TypeManager.byte_type){
1546 byte v = (byte) key;
1548 if (Elements.Contains (v))
1549 lname = v.ToString ();
1551 Elements.Add (v, sl);
1552 } else if (compare_type == TypeManager.sbyte_type){
1553 sbyte v = (sbyte) key;
1555 if (Elements.Contains (v))
1556 lname = v.ToString ();
1558 Elements.Add (v, sl);
1559 } else if (compare_type == TypeManager.short_type){
1560 short v = (short) key;
1562 if (Elements.Contains (v))
1563 lname = v.ToString ();
1565 Elements.Add (v, sl);
1566 } else if (compare_type == TypeManager.ushort_type){
1567 ushort v = (ushort) key;
1569 if (Elements.Contains (v))
1570 lname = v.ToString ();
1572 Elements.Add (v, sl);
1573 } else if (compare_type == TypeManager.string_type){
1574 if (key is NullLiteral){
1575 if (Elements.Contains (NullLiteral.Null))
1578 Elements.Add (NullLiteral.Null, null);
1580 string s = (string) key;
1582 if (Elements.Contains (s))
1585 Elements.Add (s, sl);
1587 } else if (compare_type == TypeManager.int32_type) {
1590 if (Elements.Contains (v))
1591 lname = v.ToString ();
1593 Elements.Add (v, sl);
1594 } else if (compare_type == TypeManager.bool_type) {
1595 bool v = (bool) key;
1597 if (Elements.Contains (v))
1598 lname = v.ToString ();
1600 Elements.Add (v, sl);
1604 throw new Exception ("Unknown switch type!" +
1605 SwitchType + " " + compare_type);
1609 error152 ("case + " + lname);
1620 void EmitObjectInteger (ILGenerator ig, object k)
1623 IntConstant.EmitInt (ig, (int) k);
1624 else if (k is Constant) {
1625 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1628 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1631 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1633 IntConstant.EmitInt (ig, (int) (long) k);
1634 ig.Emit (OpCodes.Conv_I8);
1637 LongConstant.EmitLong (ig, (long) k);
1639 else if (k is ulong)
1641 if ((ulong) k < (1L<<32))
1643 IntConstant.EmitInt (ig, (int) (long) k);
1644 ig.Emit (OpCodes.Conv_U8);
1648 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1652 IntConstant.EmitInt (ig, (int) ((char) k));
1653 else if (k is sbyte)
1654 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1656 IntConstant.EmitInt (ig, (int) ((byte) k));
1657 else if (k is short)
1658 IntConstant.EmitInt (ig, (int) ((short) k));
1659 else if (k is ushort)
1660 IntConstant.EmitInt (ig, (int) ((ushort) k));
1662 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1664 throw new Exception ("Unhandled case");
1667 // structure used to hold blocks of keys while calculating table switch
1668 class KeyBlock : IComparable
1670 public KeyBlock (long _nFirst)
1672 nFirst = nLast = _nFirst;
1676 public ArrayList rgKeys = null;
1679 get { return (int) (nLast - nFirst + 1); }
1681 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1683 return kbLast.nLast - kbFirst.nFirst + 1;
1685 public int CompareTo (object obj)
1687 KeyBlock kb = (KeyBlock) obj;
1688 int nLength = Length;
1689 int nLengthOther = kb.Length;
1690 if (nLengthOther == nLength)
1691 return (int) (kb.nFirst - nFirst);
1692 return nLength - nLengthOther;
1697 /// This method emits code for a lookup-based switch statement (non-string)
1698 /// Basically it groups the cases into blocks that are at least half full,
1699 /// and then spits out individual lookup opcodes for each block.
1700 /// It emits the longest blocks first, and short blocks are just
1701 /// handled with direct compares.
1703 /// <param name="ec"></param>
1704 /// <param name="val"></param>
1705 /// <returns></returns>
1706 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1708 int cElements = Elements.Count;
1709 object [] rgKeys = new object [cElements];
1710 Elements.Keys.CopyTo (rgKeys, 0);
1711 Array.Sort (rgKeys);
1713 // initialize the block list with one element per key
1714 ArrayList rgKeyBlocks = new ArrayList ();
1715 foreach (object key in rgKeys)
1716 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1719 // iteratively merge the blocks while they are at least half full
1720 // there's probably a really cool way to do this with a tree...
1721 while (rgKeyBlocks.Count > 1)
1723 ArrayList rgKeyBlocksNew = new ArrayList ();
1724 kbCurr = (KeyBlock) rgKeyBlocks [0];
1725 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1727 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1728 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1731 kbCurr.nLast = kb.nLast;
1735 // start a new block
1736 rgKeyBlocksNew.Add (kbCurr);
1740 rgKeyBlocksNew.Add (kbCurr);
1741 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1743 rgKeyBlocks = rgKeyBlocksNew;
1746 // initialize the key lists
1747 foreach (KeyBlock kb in rgKeyBlocks)
1748 kb.rgKeys = new ArrayList ();
1750 // fill the key lists
1752 if (rgKeyBlocks.Count > 0) {
1753 kbCurr = (KeyBlock) rgKeyBlocks [0];
1754 foreach (object key in rgKeys)
1756 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1758 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1759 kbCurr.rgKeys.Add (key);
1763 // sort the blocks so we can tackle the largest ones first
1764 rgKeyBlocks.Sort ();
1766 // okay now we can start...
1767 ILGenerator ig = ec.ig;
1768 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1769 Label lblDefault = ig.DefineLabel ();
1771 Type typeKeys = null;
1772 if (rgKeys.Length > 0)
1773 typeKeys = rgKeys [0].GetType (); // used for conversions
1775 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1777 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1778 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1781 foreach (object key in kb.rgKeys)
1783 ig.Emit (OpCodes.Ldloc, val);
1784 EmitObjectInteger (ig, key);
1785 SwitchLabel sl = (SwitchLabel) Elements [key];
1786 ig.Emit (OpCodes.Beq, sl.ILLabel);
1791 // TODO: if all the keys in the block are the same and there are
1792 // no gaps/defaults then just use a range-check.
1793 if (SwitchType == TypeManager.int64_type ||
1794 SwitchType == TypeManager.uint64_type)
1796 // TODO: optimize constant/I4 cases
1798 // check block range (could be > 2^31)
1799 ig.Emit (OpCodes.Ldloc, val);
1800 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1801 ig.Emit (OpCodes.Blt, lblDefault);
1802 ig.Emit (OpCodes.Ldloc, val);
1803 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1804 ig.Emit (OpCodes.Bgt, lblDefault);
1807 ig.Emit (OpCodes.Ldloc, val);
1810 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1811 ig.Emit (OpCodes.Sub);
1813 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1818 ig.Emit (OpCodes.Ldloc, val);
1819 int nFirst = (int) kb.nFirst;
1822 IntConstant.EmitInt (ig, nFirst);
1823 ig.Emit (OpCodes.Sub);
1825 else if (nFirst < 0)
1827 IntConstant.EmitInt (ig, -nFirst);
1828 ig.Emit (OpCodes.Add);
1832 // first, build the list of labels for the switch
1834 int cJumps = kb.Length;
1835 Label [] rgLabels = new Label [cJumps];
1836 for (int iJump = 0; iJump < cJumps; iJump++)
1838 object key = kb.rgKeys [iKey];
1839 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1841 SwitchLabel sl = (SwitchLabel) Elements [key];
1842 rgLabels [iJump] = sl.ILLabel;
1846 rgLabels [iJump] = lblDefault;
1848 // emit the switch opcode
1849 ig.Emit (OpCodes.Switch, rgLabels);
1852 // mark the default for this block
1854 ig.MarkLabel (lblDefault);
1857 // TODO: find the default case and emit it here,
1858 // to prevent having to do the following jump.
1859 // make sure to mark other labels in the default section
1861 // the last default just goes to the end
1862 ig.Emit (OpCodes.Br, lblDefault);
1864 // now emit the code for the sections
1865 bool fFoundDefault = false;
1866 bool fAllReturn = true;
1867 foreach (SwitchSection ss in Sections)
1869 foreach (SwitchLabel sl in ss.Labels)
1871 ig.MarkLabel (sl.ILLabel);
1872 ig.MarkLabel (sl.ILLabelCode);
1873 if (sl.Label == null)
1875 ig.MarkLabel (lblDefault);
1876 fFoundDefault = true;
1879 fAllReturn &= ss.Block.Emit (ec);
1880 //ig.Emit (OpCodes.Br, lblEnd);
1883 if (!fFoundDefault) {
1884 ig.MarkLabel (lblDefault);
1887 ig.MarkLabel (lblEnd);
1892 // This simple emit switch works, but does not take advantage of the
1894 // TODO: remove non-string logic from here
1895 // TODO: binary search strings?
1897 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1899 ILGenerator ig = ec.ig;
1900 Label end_of_switch = ig.DefineLabel ();
1901 Label next_test = ig.DefineLabel ();
1902 Label null_target = ig.DefineLabel ();
1903 bool default_found = false;
1904 bool first_test = true;
1905 bool pending_goto_end = false;
1906 bool all_return = true;
1907 bool is_string = false;
1911 // Special processing for strings: we cant compare
1914 if (SwitchType == TypeManager.string_type){
1915 ig.Emit (OpCodes.Ldloc, val);
1918 if (Elements.Contains (NullLiteral.Null)){
1919 ig.Emit (OpCodes.Brfalse, null_target);
1921 ig.Emit (OpCodes.Brfalse, default_target);
1923 ig.Emit (OpCodes.Ldloc, val);
1924 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1925 ig.Emit (OpCodes.Stloc, val);
1928 SwitchSection last_section;
1929 last_section = (SwitchSection) Sections [Sections.Count-1];
1931 foreach (SwitchSection ss in Sections){
1932 Label sec_begin = ig.DefineLabel ();
1934 if (pending_goto_end)
1935 ig.Emit (OpCodes.Br, end_of_switch);
1937 int label_count = ss.Labels.Count;
1939 foreach (SwitchLabel sl in ss.Labels){
1940 ig.MarkLabel (sl.ILLabel);
1943 ig.MarkLabel (next_test);
1944 next_test = ig.DefineLabel ();
1947 // If we are the default target
1949 if (sl.Label == null){
1950 ig.MarkLabel (default_target);
1951 default_found = true;
1953 object lit = sl.Converted;
1955 if (lit is NullLiteral){
1957 if (label_count == 1)
1958 ig.Emit (OpCodes.Br, next_test);
1963 StringConstant str = (StringConstant) lit;
1965 ig.Emit (OpCodes.Ldloc, val);
1966 ig.Emit (OpCodes.Ldstr, str.Value);
1967 if (label_count == 1)
1968 ig.Emit (OpCodes.Bne_Un, next_test);
1970 ig.Emit (OpCodes.Beq, sec_begin);
1972 ig.Emit (OpCodes.Ldloc, val);
1973 EmitObjectInteger (ig, lit);
1974 ig.Emit (OpCodes.Ceq);
1975 if (label_count == 1)
1976 ig.Emit (OpCodes.Brfalse, next_test);
1978 ig.Emit (OpCodes.Brtrue, sec_begin);
1982 if (label_count != 1 && ss != last_section)
1983 ig.Emit (OpCodes.Br, next_test);
1986 ig.MarkLabel (null_target);
1987 ig.MarkLabel (sec_begin);
1988 foreach (SwitchLabel sl in ss.Labels)
\r
1989 ig.MarkLabel (sl.ILLabelCode);
1990 if (ss.Block.Emit (ec))
1991 pending_goto_end = false;
1994 pending_goto_end = true;
1998 if (!default_found){
1999 ig.MarkLabel (default_target);
2002 ig.MarkLabel (next_test);
2003 ig.MarkLabel (end_of_switch);
2008 public override bool Resolve (EmitContext ec)
2010 foreach (SwitchSection ss in Sections){
2011 if (ss.Block.Resolve (ec) != true)
2018 public override bool Emit (EmitContext ec)
2020 Expr = Expr.Resolve (ec);
2024 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
2025 if (new_expr == null){
2026 Report.Error (151, loc, "An integer type or string was expected for switch");
2031 SwitchType = new_expr.Type;
2033 if (!CheckSwitch (ec))
2036 // Store variable for comparission purposes
2037 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2039 ec.ig.Emit (OpCodes.Stloc, value);
2041 ILGenerator ig = ec.ig;
2043 default_target = ig.DefineLabel ();
2046 // Setup the codegen context
2048 Label old_end = ec.LoopEnd;
2049 Switch old_switch = ec.Switch;
2051 ec.LoopEnd = ig.DefineLabel ();
2056 if (SwitchType == TypeManager.string_type)
2057 all_return = SimpleSwitchEmit (ec, value);
2059 all_return = TableSwitchEmit (ec, value);
2061 // Restore context state.
2062 ig.MarkLabel (ec.LoopEnd);
2065 // Restore the previous context
2067 ec.LoopEnd = old_end;
2068 ec.Switch = old_switch;
2074 public class Lock : Statement {
2076 Statement Statement;
2078 public Lock (Expression expr, Statement stmt, Location l)
2085 public override bool Resolve (EmitContext ec)
2087 expr = expr.Resolve (ec);
2088 return Statement.Resolve (ec) && expr != null;
2091 public override bool Emit (EmitContext ec)
2093 Type type = expr.Type;
2096 if (type.IsValueType){
2097 Report.Error (185, loc, "lock statement requires the expression to be " +
2098 " a reference type (type is: `" +
2099 TypeManager.CSharpName (type) + "'");
2103 ILGenerator ig = ec.ig;
2104 LocalBuilder temp = ig.DeclareLocal (type);
2107 ig.Emit (OpCodes.Dup);
2108 ig.Emit (OpCodes.Stloc, temp);
2109 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2112 Label end = ig.BeginExceptionBlock ();
2113 bool old_in_try = ec.InTry;
2115 Label finish = ig.DefineLabel ();
2116 val = Statement.Emit (ec);
2117 ec.InTry = old_in_try;
2118 // ig.Emit (OpCodes.Leave, finish);
2120 ig.MarkLabel (finish);
2123 ig.BeginFinallyBlock ();
2124 ig.Emit (OpCodes.Ldloc, temp);
2125 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2126 ig.EndExceptionBlock ();
2132 public class Unchecked : Statement {
2133 public readonly Block Block;
2135 public Unchecked (Block b)
2140 public override bool Resolve (EmitContext ec)
2142 return Block.Resolve (ec);
2145 public override bool Emit (EmitContext ec)
2147 bool previous_state = ec.CheckState;
2148 bool previous_state_const = ec.ConstantCheckState;
2151 ec.CheckState = false;
2152 ec.ConstantCheckState = false;
2153 val = Block.Emit (ec);
2154 ec.CheckState = previous_state;
2155 ec.ConstantCheckState = previous_state_const;
2161 public class Checked : Statement {
2162 public readonly Block Block;
2164 public Checked (Block b)
2169 public override bool Resolve (EmitContext ec)
2171 bool previous_state = ec.CheckState;
2172 bool previous_state_const = ec.ConstantCheckState;
2174 ec.CheckState = true;
2175 ec.ConstantCheckState = true;
2176 bool ret = Block.Resolve (ec);
2177 ec.CheckState = previous_state;
2178 ec.ConstantCheckState = previous_state_const;
2183 public override bool Emit (EmitContext ec)
2185 bool previous_state = ec.CheckState;
2186 bool previous_state_const = ec.ConstantCheckState;
2189 ec.CheckState = true;
2190 ec.ConstantCheckState = true;
2191 val = Block.Emit (ec);
2192 ec.CheckState = previous_state;
2193 ec.ConstantCheckState = previous_state_const;
2199 public class Unsafe : Statement {
2200 public readonly Block Block;
2202 public Unsafe (Block b)
2207 public override bool Resolve (EmitContext ec)
2209 bool previous_state = ec.InUnsafe;
2213 val = Block.Resolve (ec);
2214 ec.InUnsafe = previous_state;
2219 public override bool Emit (EmitContext ec)
2221 bool previous_state = ec.InUnsafe;
2225 val = Block.Emit (ec);
2226 ec.InUnsafe = previous_state;
2235 public class Fixed : Statement {
2237 ArrayList declarators;
2238 Statement statement;
2240 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
2243 declarators = decls;
2248 public override bool Resolve (EmitContext ec)
2250 return statement.Resolve (ec);
2253 public override bool Emit (EmitContext ec)
2255 ILGenerator ig = ec.ig;
2258 t = ec.DeclSpace.ResolveType (type, false, loc);
2262 bool is_ret = false;
2264 foreach (Pair p in declarators){
2265 VariableInfo vi = (VariableInfo) p.First;
2266 Expression e = (Expression) p.Second;
2269 // The rules for the possible declarators are pretty wise,
2270 // but the production on the grammar is more concise.
2272 // So we have to enforce these rules here.
2274 // We do not resolve before doing the case 1 test,
2275 // because the grammar is explicit in that the token &
2276 // is present, so we need to test for this particular case.
2280 // Case 1: & object.
2282 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2283 Expression child = ((Unary) e).Expr;
2286 if (child is ParameterReference || child is LocalVariableReference){
2289 "No need to use fixed statement for parameters or " +
2290 "local variable declarations (address is already " +
2299 child = ((Unary) e).Expr;
2301 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2305 // Store pointer in pinned location
2308 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2310 is_ret = statement.Emit (ec);
2312 // Clear the pinned variable.
2313 ig.Emit (OpCodes.Ldc_I4_0);
2314 ig.Emit (OpCodes.Conv_U);
2315 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2327 if (e.Type.IsArray){
2328 Type array_type = e.Type.GetElementType ();
2332 // Provided that array_type is unmanaged,
2334 if (!TypeManager.VerifyUnManaged (array_type, loc))
2338 // and T* is implicitly convertible to the
2339 // pointer type given in the fixed statement.
2341 ArrayPtr array_ptr = new ArrayPtr (e);
2343 Expression converted = Expression.ConvertImplicitRequired (
2344 ec, array_ptr, vi.VariableType, loc);
2345 if (converted == null)
2349 // Store pointer in pinned location
2351 converted.Emit (ec);
2353 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2355 is_ret = statement.Emit (ec);
2357 // Clear the pinned variable.
2358 ig.Emit (OpCodes.Ldc_I4_0);
2359 ig.Emit (OpCodes.Conv_U);
2360 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2368 if (e.Type == TypeManager.string_type){
2369 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2370 TypeManager.MakePinned (pinned_string);
2373 ig.Emit (OpCodes.Stloc, pinned_string);
2375 Expression sptr = new StringPtr (pinned_string);
2376 Expression converted = Expression.ConvertImplicitRequired (
2377 ec, sptr, vi.VariableType, loc);
2379 if (converted == null)
2382 converted.Emit (ec);
2383 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2385 is_ret = statement.Emit (ec);
2387 // Clear the pinned variable
2388 ig.Emit (OpCodes.Ldnull);
2389 ig.Emit (OpCodes.Stloc, pinned_string);
2397 public class Catch {
2398 public readonly Expression Type;
2399 public readonly string Name;
2400 public readonly Block Block;
2401 public readonly Location Location;
2403 public Catch (Expression type, string name, Block block, Location l)
2412 public class Try : Statement {
2413 public readonly Block Fini, Block;
2414 public readonly ArrayList Specific;
2415 public readonly Catch General;
2418 // specific, general and fini might all be null.
2420 public Try (Block block, ArrayList specific, Catch general, Block fini)
2422 if (specific == null && general == null){
2423 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2427 this.Specific = specific;
2428 this.General = general;
2432 public override bool Resolve (EmitContext ec)
2436 if (General != null)
2437 if (!General.Block.Resolve (ec))
2440 foreach (Catch c in Specific){
2441 if (!c.Block.Resolve (ec))
2445 if (!Block.Resolve (ec))
2449 if (!Fini.Resolve (ec))
2455 public override bool Emit (EmitContext ec)
2457 ILGenerator ig = ec.ig;
2459 Label finish = ig.DefineLabel ();;
2463 end = ig.BeginExceptionBlock ();
2464 bool old_in_try = ec.InTry;
2466 returns = Block.Emit (ec);
2467 ec.InTry = old_in_try;
2470 // System.Reflection.Emit provides this automatically:
2471 // ig.Emit (OpCodes.Leave, finish);
2473 bool old_in_catch = ec.InCatch;
2475 DeclSpace ds = ec.DeclSpace;
2477 foreach (Catch c in Specific){
2478 Type catch_type = ds.ResolveType (c.Type, false, c.Location);
2481 if (catch_type == null)
2484 ig.BeginCatchBlock (catch_type);
2486 if (c.Name != null){
2487 vi = c.Block.GetVariableInfo (c.Name);
2489 Console.WriteLine ("This should not happen! variable does not exist in this block");
2490 Environment.Exit (0);
2493 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2495 ig.Emit (OpCodes.Pop);
2497 if (!c.Block.Emit (ec))
2501 if (General != null){
2502 ig.BeginCatchBlock (TypeManager.object_type);
2503 ig.Emit (OpCodes.Pop);
2504 if (!General.Block.Emit (ec))
2507 ec.InCatch = old_in_catch;
2509 ig.MarkLabel (finish);
2511 ig.BeginFinallyBlock ();
2512 bool old_in_finally = ec.InFinally;
2513 ec.InFinally = true;
2515 ec.InFinally = old_in_finally;
2518 ig.EndExceptionBlock ();
2521 if (!returns || ec.InTry || ec.InCatch)
2524 // Unfortunately, System.Reflection.Emit automatically emits a leave
2525 // to the end of the finally block. This is a problem if `returns'
2526 // is true since we may jump to a point after the end of the method.
2527 // As a workaround, emit an explicit ret here.
2529 if (ec.ReturnType != null)
2530 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2531 ec.ig.Emit (OpCodes.Ret);
2538 // FIXME: We still do not support the expression variant of the using
2541 public class Using : Statement {
2542 object expression_or_block;
2543 Statement Statement;
2545 public Using (object expression_or_block, Statement stmt, Location l)
2547 this.expression_or_block = expression_or_block;
2553 // Emits the code for the case of using using a local variable declaration.
2555 bool EmitLocalVariableDecls (EmitContext ec, Expression expr_type, ArrayList var_list)
2557 ILGenerator ig = ec.ig;
2558 Expression [] converted_vars;
2559 bool need_conv = false;
2560 Type type = ec.DeclSpace.ResolveType (expr_type, false, loc);
2567 // The type must be an IDisposable or an implicit conversion
2570 converted_vars = new Expression [var_list.Count];
2571 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2572 foreach (DictionaryEntry e in var_list){
2573 Expression var = (Expression) e.Key;
2575 var = var.Resolve (ec);
2579 converted_vars [i] = Expression.ConvertImplicit (
2580 ec, var, TypeManager.idisposable_type, loc);
2582 if (converted_vars [i] == null)
2590 bool old_in_try = ec.InTry;
2593 foreach (DictionaryEntry e in var_list){
2594 LocalVariableReference var = (LocalVariableReference) e.Key;
2595 Expression expr = (Expression) e.Value;
2598 a = new Assign (var, expr, loc);
2601 converted_vars [i] = var;
2607 ((ExpressionStatement) a).EmitStatement (ec);
2609 ig.BeginExceptionBlock ();
2614 Statement.Emit (ec);
2615 ec.InTry = old_in_try;
2617 bool old_in_finally = ec.InFinally;
2618 ec.InFinally = true;
2619 var_list.Reverse ();
2620 foreach (DictionaryEntry e in var_list){
2621 LocalVariableReference var = (LocalVariableReference) e.Key;
2622 Label skip = ig.DefineLabel ();
2625 ig.BeginFinallyBlock ();
2628 ig.Emit (OpCodes.Brfalse, skip);
2629 converted_vars [i].Emit (ec);
2630 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2631 ig.MarkLabel (skip);
2632 ig.EndExceptionBlock ();
2634 ec.InFinally = old_in_finally;
2639 bool EmitExpression (EmitContext ec, Expression expr)
2641 Type expr_type = expr.Type;
2642 Expression conv = null;
2644 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2645 conv = Expression.ConvertImplicit (
2646 ec, expr, TypeManager.idisposable_type, loc);
2653 // Make a copy of the expression and operate on that.
2655 ILGenerator ig = ec.ig;
2656 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2661 ig.Emit (OpCodes.Stloc, local_copy);
2663 bool old_in_try = ec.InTry;
2665 ig.BeginExceptionBlock ();
2666 Statement.Emit (ec);
2667 ec.InTry = old_in_try;
2669 Label skip = ig.DefineLabel ();
2670 bool old_in_finally = ec.InFinally;
2671 ig.BeginFinallyBlock ();
2672 ig.Emit (OpCodes.Ldloc, local_copy);
2673 ig.Emit (OpCodes.Brfalse, skip);
2674 ig.Emit (OpCodes.Ldloc, local_copy);
2675 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2676 ig.MarkLabel (skip);
2677 ec.InFinally = old_in_finally;
2678 ig.EndExceptionBlock ();
2683 public override bool Resolve (EmitContext ec)
2685 return Statement.Resolve (ec);
2688 public override bool Emit (EmitContext ec)
2690 if (expression_or_block is DictionaryEntry){
2691 Expression expr_type = (Expression) ((DictionaryEntry) expression_or_block).Key;
2692 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2694 return EmitLocalVariableDecls (ec, expr_type, var_list);
2695 } if (expression_or_block is Expression){
2696 Expression e = (Expression) expression_or_block;
2702 return EmitExpression (ec, e);
2709 /// Implementation of the foreach C# statement
2711 public class Foreach : Statement {
2713 LocalVariableReference variable;
2715 Statement statement;
2717 public Foreach (Expression type, LocalVariableReference var, Expression expr,
2718 Statement stmt, Location l)
2721 this.variable = var;
2727 public override bool Resolve (EmitContext ec)
2729 expr = expr.Resolve (ec);
2730 return statement.Resolve (ec) && expr != null;
2734 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2736 static MethodInfo FetchMethodMoveNext (Type t)
2738 MemberInfo [] move_next_list;
2740 move_next_list = TypeContainer.FindMembers (
2741 t, MemberTypes.Method,
2742 BindingFlags.Public | BindingFlags.Instance,
2743 Type.FilterName, "MoveNext");
2744 if (move_next_list == null || move_next_list.Length == 0)
2747 foreach (MemberInfo m in move_next_list){
2748 MethodInfo mi = (MethodInfo) m;
2751 args = TypeManager.GetArgumentTypes (mi);
2752 if (args != null && args.Length == 0){
2753 if (mi.ReturnType == TypeManager.bool_type)
2761 // Retrieves a `public T get_Current ()' method from the Type `t'
2763 static MethodInfo FetchMethodGetCurrent (Type t)
2765 MemberInfo [] move_next_list;
2767 move_next_list = TypeContainer.FindMembers (
2768 t, MemberTypes.Method,
2769 BindingFlags.Public | BindingFlags.Instance,
2770 Type.FilterName, "get_Current");
2771 if (move_next_list == null || move_next_list.Length == 0)
2774 foreach (MemberInfo m in move_next_list){
2775 MethodInfo mi = (MethodInfo) m;
2778 args = TypeManager.GetArgumentTypes (mi);
2779 if (args != null && args.Length == 0)
2786 // This struct records the helper methods used by the Foreach construct
2788 class ForeachHelperMethods {
2789 public EmitContext ec;
2790 public MethodInfo get_enumerator;
2791 public MethodInfo move_next;
2792 public MethodInfo get_current;
2794 public ForeachHelperMethods (EmitContext ec)
2800 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2805 if (!(m is MethodInfo))
2808 if (m.Name != "GetEnumerator")
2811 MethodInfo mi = (MethodInfo) m;
2812 Type [] args = TypeManager.GetArgumentTypes (mi);
2814 if (args.Length != 0)
2817 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2818 EmitContext ec = hm.ec;
2821 // Check whether GetEnumerator is accessible to us
2823 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2825 Type declaring = mi.DeclaringType;
2826 if (prot == MethodAttributes.Private){
2827 if (declaring != ec.ContainerType)
2829 } else if (prot == MethodAttributes.FamANDAssem){
2830 // If from a different assembly, false
2831 if (!(mi is MethodBuilder))
2834 // Are we being invoked from the same class, or from a derived method?
2836 if (ec.ContainerType != declaring){
2837 if (!ec.ContainerType.IsSubclassOf (declaring))
2840 } else if (prot == MethodAttributes.FamORAssem){
2841 if (!(mi is MethodBuilder ||
2842 ec.ContainerType == declaring ||
2843 ec.ContainerType.IsSubclassOf (declaring)))
2845 } if (prot == MethodAttributes.Family){
2846 if (!(ec.ContainerType == declaring ||
2847 ec.ContainerType.IsSubclassOf (declaring)))
2852 // Ok, we can access it, now make sure that we can do something
2853 // with this `GetEnumerator'
2856 if (mi.ReturnType == TypeManager.ienumerator_type ||
2857 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
2858 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
2859 hm.move_next = TypeManager.bool_movenext_void;
2860 hm.get_current = TypeManager.object_getcurrent_void;
2865 // Ok, so they dont return an IEnumerable, we will have to
2866 // find if they support the GetEnumerator pattern.
2868 Type return_type = mi.ReturnType;
2870 hm.move_next = FetchMethodMoveNext (return_type);
2871 if (hm.move_next == null)
2873 hm.get_current = FetchMethodGetCurrent (return_type);
2874 if (hm.get_current == null)
2881 /// This filter is used to find the GetEnumerator method
2882 /// on which IEnumerator operates
2884 static MemberFilter FilterEnumerator;
2888 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2891 void error1579 (Type t)
2893 Report.Error (1579, loc,
2894 "foreach statement cannot operate on variables of type `" +
2895 t.FullName + "' because that class does not provide a " +
2896 " GetEnumerator method or it is inaccessible");
2899 static bool TryType (Type t, ForeachHelperMethods hm)
2903 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2904 BindingFlags.Public | BindingFlags.NonPublic |
2905 BindingFlags.Instance,
2906 FilterEnumerator, hm);
2908 if (mi == null || mi.Length == 0)
2911 hm.get_enumerator = (MethodInfo) mi [0];
2916 // Looks for a usable GetEnumerator in the Type, and if found returns
2917 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2919 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2921 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2923 if (TryType (t, hm))
2927 // Now try to find the method in the interfaces
2930 Type [] ifaces = t.GetInterfaces ();
2932 foreach (Type i in ifaces){
2933 if (TryType (i, hm))
2938 // Since TypeBuilder.GetInterfaces only returns the interface
2939 // types for this type, we have to keep looping, but once
2940 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2941 // done, because it returns all the types
2943 if ((t is TypeBuilder))
2953 // FIXME: possible optimization.
2954 // We might be able to avoid creating `empty' if the type is the sam
2956 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2958 ILGenerator ig = ec.ig;
2959 LocalBuilder enumerator, disposable;
2960 Expression empty = new EmptyExpression ();
2964 // FIXME: maybe we can apply the same trick we do in the
2965 // array handling to avoid creating empty and conv in some cases.
2967 // Although it is not as important in this case, as the type
2968 // will not likely be object (what the enumerator will return).
2970 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2974 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2975 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2978 // Instantiate the enumerator
2980 if (expr.Type.IsValueType){
2981 if (expr is IMemoryLocation){
2982 IMemoryLocation ml = (IMemoryLocation) expr;
2984 ml.AddressOf (ec, AddressOp.Load);
2986 throw new Exception ("Expr " + expr + " of type " + expr.Type +
2987 " does not implement IMemoryLocation");
2988 ig.Emit (OpCodes.Call, hm.get_enumerator);
2991 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2993 ig.Emit (OpCodes.Stloc, enumerator);
2996 // Protect the code in a try/finalize block, so that
2997 // if the beast implement IDisposable, we get rid of it
2999 Label l = ig.BeginExceptionBlock ();
3000 bool old_in_try = ec.InTry;
3003 Label end_try = ig.DefineLabel ();
3005 ig.MarkLabel (ec.LoopBegin);
3006 ig.Emit (OpCodes.Ldloc, enumerator);
3007 ig.Emit (OpCodes.Callvirt, hm.move_next);
3008 ig.Emit (OpCodes.Brfalse, end_try);
3009 ig.Emit (OpCodes.Ldloc, enumerator);
3010 ig.Emit (OpCodes.Callvirt, hm.get_current);
3011 variable.EmitAssign (ec, conv);
3012 statement.Emit (ec);
3013 ig.Emit (OpCodes.Br, ec.LoopBegin);
3014 ig.MarkLabel (end_try);
3015 ec.InTry = old_in_try;
3017 // The runtime provides this for us.
3018 // ig.Emit (OpCodes.Leave, end);
3021 // Now the finally block
3023 Label end_finally = ig.DefineLabel ();
3024 bool old_in_finally = ec.InFinally;
3025 ec.InFinally = true;
3026 ig.BeginFinallyBlock ();
3028 ig.Emit (OpCodes.Ldloc, enumerator);
3029 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
3030 ig.Emit (OpCodes.Stloc, disposable);
3031 ig.Emit (OpCodes.Ldloc, disposable);
3032 ig.Emit (OpCodes.Brfalse, end_finally);
3033 ig.Emit (OpCodes.Ldloc, disposable);
3034 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3035 ig.MarkLabel (end_finally);
3036 ec.InFinally = old_in_finally;
3038 // The runtime generates this anyways.
3039 // ig.Emit (OpCodes.Endfinally);
3041 ig.EndExceptionBlock ();
3043 ig.MarkLabel (ec.LoopEnd);
3048 // FIXME: possible optimization.
3049 // We might be able to avoid creating `empty' if the type is the sam
3051 bool EmitArrayForeach (EmitContext ec, Type var_type)
3053 Type array_type = expr.Type;
3054 Type element_type = array_type.GetElementType ();
3055 Expression conv = null;
3056 Expression empty = new EmptyExpression (element_type);
3058 conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
3062 int rank = array_type.GetArrayRank ();
3063 ILGenerator ig = ec.ig;
3065 LocalBuilder copy = ig.DeclareLocal (array_type);
3068 // Make our copy of the array
3071 ig.Emit (OpCodes.Stloc, copy);
3074 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
3078 ig.Emit (OpCodes.Ldc_I4_0);
3079 ig.Emit (OpCodes.Stloc, counter);
3080 test = ig.DefineLabel ();
3081 ig.Emit (OpCodes.Br, test);
3083 loop = ig.DefineLabel ();
3084 ig.MarkLabel (loop);
3086 ig.Emit (OpCodes.Ldloc, copy);
3087 ig.Emit (OpCodes.Ldloc, counter);
3088 ArrayAccess.EmitLoadOpcode (ig, var_type);
3090 variable.EmitAssign (ec, conv);
3092 statement.Emit (ec);
3094 ig.MarkLabel (ec.LoopBegin);
3095 ig.Emit (OpCodes.Ldloc, counter);
3096 ig.Emit (OpCodes.Ldc_I4_1);
3097 ig.Emit (OpCodes.Add);
3098 ig.Emit (OpCodes.Stloc, counter);
3100 ig.MarkLabel (test);
3101 ig.Emit (OpCodes.Ldloc, counter);
3102 ig.Emit (OpCodes.Ldloc, copy);
3103 ig.Emit (OpCodes.Ldlen);
3104 ig.Emit (OpCodes.Conv_I4);
3105 ig.Emit (OpCodes.Blt, loop);
3107 LocalBuilder [] dim_len = new LocalBuilder [rank];
3108 LocalBuilder [] dim_count = new LocalBuilder [rank];
3109 Label [] loop = new Label [rank];
3110 Label [] test = new Label [rank];
3113 for (dim = 0; dim < rank; dim++){
3114 dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
3115 dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
3116 test [dim] = ig.DefineLabel ();
3117 loop [dim] = ig.DefineLabel ();
3120 for (dim = 0; dim < rank; dim++){
3121 ig.Emit (OpCodes.Ldloc, copy);
3122 IntLiteral.EmitInt (ig, dim);
3123 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
3124 ig.Emit (OpCodes.Stloc, dim_len [dim]);
3127 for (dim = 0; dim < rank; dim++){
3128 ig.Emit (OpCodes.Ldc_I4_0);
3129 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3130 ig.Emit (OpCodes.Br, test [dim]);
3131 ig.MarkLabel (loop [dim]);
3134 ig.Emit (OpCodes.Ldloc, copy);
3135 for (dim = 0; dim < rank; dim++)
3136 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3139 // FIXME: Maybe we can cache the computation of `get'?
3141 Type [] args = new Type [rank];
3144 for (int i = 0; i < rank; i++)
3145 args [i] = TypeManager.int32_type;
3147 ModuleBuilder mb = CodeGen.ModuleBuilder;
3148 get = mb.GetArrayMethod (
3150 CallingConventions.HasThis| CallingConventions.Standard,
3152 ig.Emit (OpCodes.Call, get);
3153 variable.EmitAssign (ec, conv);
3154 statement.Emit (ec);
3155 ig.MarkLabel (ec.LoopBegin);
3156 for (dim = rank - 1; dim >= 0; dim--){
3157 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3158 ig.Emit (OpCodes.Ldc_I4_1);
3159 ig.Emit (OpCodes.Add);
3160 ig.Emit (OpCodes.Stloc, dim_count [dim]);
3162 ig.MarkLabel (test [dim]);
3163 ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3164 ig.Emit (OpCodes.Ldloc, dim_len [dim]);
3165 ig.Emit (OpCodes.Blt, loop [dim]);
3168 ig.MarkLabel (ec.LoopEnd);
3173 public override bool Emit (EmitContext ec)
3178 var_type = ec.DeclSpace.ResolveType (type, false, loc);
3179 if (var_type == null)
3183 // We need an instance variable. Not sure this is the best
3184 // way of doing this.
3186 // FIXME: When we implement propertyaccess, will those turn
3187 // out to return values in ExprClass? I think they should.
3189 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3190 expr.eclass == ExprClass.PropertyAccess)){
3191 error1579 (expr.Type);
3195 ILGenerator ig = ec.ig;
3197 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
3198 bool old_inloop = ec.InLoop;
3199 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
3200 ec.LoopBegin = ig.DefineLabel ();
3201 ec.LoopEnd = ig.DefineLabel ();
3203 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
3205 if (expr.Type.IsArray)
3206 ret_val = EmitArrayForeach (ec, var_type);
3208 ForeachHelperMethods hm;
3210 hm = ProbeCollectionType (ec, expr.Type);
3212 error1579 (expr.Type);
3216 ret_val = EmitCollectionForeach (ec, var_type, hm);
3219 ec.LoopBegin = old_begin;
3220 ec.LoopEnd = old_end;
3221 ec.InLoop = old_inloop;
3222 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;