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 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
320 ec.LoopBegin = ig.DefineLabel ();
321 ec.LoopEnd = ig.DefineLabel ();
323 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
326 // Inform whether we are infinite or not
328 if (expr is BoolConstant){
329 BoolConstant bc = (BoolConstant) expr;
331 ig.MarkLabel (ec.LoopBegin);
332 if (bc.Value == false){
333 Warning_DeadCodeFound (Statement.loc);
341 ig.Emit (OpCodes.Br, ec.LoopBegin);
344 // Inform that we are infinite (ie, `we return'), only
345 // if we do not `break' inside the code.
347 ret = breaks == false;
349 ig.MarkLabel (ec.LoopEnd);
351 ig.MarkLabel (ec.LoopBegin);
353 EmitBoolExpression (ec, expr, ec.LoopEnd, false);
357 ig.Emit (OpCodes.Br, ec.LoopBegin);
359 ig.MarkLabel (ec.LoopEnd);
364 ec.LoopBegin = old_begin;
365 ec.LoopEnd = old_end;
366 ec.InLoop = old_inloop;
367 ec.Breaks = old_breaks;
368 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
374 public class For : Statement {
376 readonly Statement InitStatement;
377 readonly Statement Increment;
378 readonly Statement Statement;
380 public For (Statement initStatement,
386 InitStatement = initStatement;
388 Increment = increment;
389 Statement = statement;
393 public override bool Resolve (EmitContext ec)
398 Test = ResolveBoolean (ec, Test, loc);
403 if (InitStatement != null){
404 if (!InitStatement.Resolve (ec))
408 if (Increment != null){
409 if (!Increment.Resolve (ec))
413 return Statement.Resolve (ec) && ok;
416 public override bool Emit (EmitContext ec)
418 ILGenerator ig = ec.ig;
419 Label old_begin = ec.LoopBegin;
420 Label old_end = ec.LoopEnd;
421 bool old_inloop = ec.InLoop;
422 bool old_breaks = ec.Breaks;
423 int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
424 Label test = ig.DefineLabel ();
426 if (InitStatement != null)
427 if (! (InitStatement is EmptyStatement))
428 InitStatement.Emit (ec);
430 ec.LoopBegin = ig.DefineLabel ();
431 ec.LoopEnd = ig.DefineLabel ();
433 ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
437 // If test is null, there is no test, and we are just
441 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
445 bool breaks = ec.Breaks;
447 ig.MarkLabel (ec.LoopBegin);
448 if (!(Increment is EmptyStatement))
451 ig.Emit (OpCodes.Br, test);
452 ig.MarkLabel (ec.LoopEnd);
454 ec.LoopBegin = old_begin;
455 ec.LoopEnd = old_end;
456 ec.InLoop = old_inloop;
457 ec.Breaks = old_breaks;
458 ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
461 // Inform whether we are infinite or not
464 if (Test is BoolConstant){
465 BoolConstant bc = (BoolConstant) Test;
468 return breaks == false;
476 public class StatementExpression : Statement {
479 public StatementExpression (ExpressionStatement expr, Location l)
485 public override bool Resolve (EmitContext ec)
487 expr = (Expression) expr.Resolve (ec);
491 public override bool Emit (EmitContext ec)
493 ILGenerator ig = ec.ig;
495 if (expr is ExpressionStatement)
496 ((ExpressionStatement) expr).EmitStatement (ec);
499 ig.Emit (OpCodes.Pop);
505 public override string ToString ()
507 return "StatementExpression (" + expr + ")";
512 /// Implements the return statement
514 public class Return : Statement {
515 public Expression Expr;
517 public Return (Expression expr, Location l)
523 public override bool Resolve (EmitContext ec)
526 Expr = Expr.Resolve (ec);
533 public override bool Emit (EmitContext ec)
536 Report.Error (157,loc,"Control can not leave the body of the finally block");
540 if (ec.ReturnType == null){
542 Report.Error (127, loc, "Return with a value not allowed here");
547 Report.Error (126, loc, "An object of type `" +
548 TypeManager.CSharpName (ec.ReturnType) + "' is " +
549 "expected for the return statement");
553 if (Expr.Type != ec.ReturnType)
554 Expr = Expression.ConvertImplicitRequired (
555 ec, Expr, ec.ReturnType, loc);
562 if (ec.InTry || ec.InCatch)
563 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
566 if (ec.InTry || ec.InCatch) {
567 if (!ec.HasReturnLabel) {
568 ec.ReturnLabel = ec.ig.DefineLabel ();
569 ec.HasReturnLabel = true;
571 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
573 ec.ig.Emit (OpCodes.Ret);
579 public class Goto : Statement {
583 public override bool Resolve (EmitContext ec)
588 public Goto (Block parent_block, string label, Location l)
590 block = parent_block;
595 public string Target {
601 public override bool Emit (EmitContext ec)
603 LabeledStatement label = block.LookupLabel (target);
607 // Maybe we should catch this before?
611 "No such label `" + target + "' in this scope");
614 Label l = label.LabelTarget (ec);
615 ec.ig.Emit (OpCodes.Br, l);
621 public class LabeledStatement : Statement {
626 public LabeledStatement (string label_name)
628 this.label_name = label_name;
631 public Label LabelTarget (EmitContext ec)
635 label = ec.ig.DefineLabel ();
641 public override bool Emit (EmitContext ec)
644 ec.ig.MarkLabel (label);
652 /// `goto default' statement
654 public class GotoDefault : Statement {
656 public GotoDefault (Location l)
661 public override bool Emit (EmitContext ec)
663 if (ec.Switch == null){
664 Report.Error (153, loc, "goto default is only valid in a switch statement");
668 if (!ec.Switch.GotDefault){
669 Report.Error (159, loc, "No default target on switch statement");
672 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
678 /// `goto case' statement
680 public class GotoCase : Statement {
683 public GotoCase (Expression e, Location l)
689 public override bool Emit (EmitContext ec)
691 if (ec.Switch == null){
692 Report.Error (153, loc, "goto case is only valid in a switch statement");
696 expr = expr.Resolve (ec);
700 if (!(expr is Constant)){
701 Report.Error (159, loc, "Target expression for goto case is not constant");
705 object val = Expression.ConvertIntLiteral (
706 (Constant) expr, ec.Switch.SwitchType, loc);
711 SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
716 "No such label 'case " + val + "': for the goto case");
719 ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
724 public class Throw : Statement {
727 public Throw (Expression expr, Location l)
733 public override bool Resolve (EmitContext ec)
736 expr = expr.Resolve (ec);
743 public override bool Emit (EmitContext ec)
747 ec.ig.Emit (OpCodes.Rethrow);
751 "A throw statement with no argument is only " +
752 "allowed in a catch clause");
759 ec.ig.Emit (OpCodes.Throw);
765 public class Break : Statement {
767 public Break (Location l)
772 public override bool Emit (EmitContext ec)
774 ILGenerator ig = ec.ig;
776 if (ec.InLoop == false && ec.Switch == null){
777 Report.Error (139, loc, "No enclosing loop or switch to continue to");
782 if (ec.InTry || ec.InCatch)
783 ig.Emit (OpCodes.Leave, ec.LoopEnd);
785 ig.Emit (OpCodes.Br, ec.LoopEnd);
791 public class Continue : Statement {
793 public Continue (Location l)
798 public override bool Emit (EmitContext ec)
800 Label begin = ec.LoopBegin;
803 Report.Error (139, loc, "No enclosing loop to continue to");
808 // UGH: Non trivial. This Br might cross a try/catch boundary
812 // try { ... } catch { continue; }
816 // try {} catch { while () { continue; }}
818 if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
819 ec.ig.Emit (OpCodes.Leave, begin);
820 else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
821 throw new Exception ("Should never happen");
823 ec.ig.Emit (OpCodes.Br, begin);
828 public class VariableInfo {
829 public Expression Type;
830 public LocalBuilder LocalBuilder;
831 public Type VariableType;
832 public readonly Location Location;
835 public bool Assigned;
836 public bool ReadOnly;
838 public VariableInfo (Expression type, Location l)
845 public void MakePinned ()
847 TypeManager.MakePinned (LocalBuilder);
852 /// Block represents a C# block.
856 /// This class is used in a number of places: either to represent
857 /// explicit blocks that the programmer places or implicit blocks.
859 /// Implicit blocks are used as labels or to introduce variable
862 public class Block : Statement {
863 public readonly Block Parent;
864 public readonly bool Implicit;
865 public readonly Location StartLocation;
866 public Location EndLocation;
869 // The statements in this block
871 ArrayList statements;
874 // An array of Blocks. We keep track of children just
875 // to generate the local variable declarations.
877 // Statements and child statements are handled through the
883 // Labels. (label, block) pairs.
888 // Keeps track of (name, type) pairs
893 // Keeps track of constants
897 // Maps variable names to ILGenerator.LocalBuilders
899 Hashtable local_builders;
907 public Block (Block parent)
908 : this (parent, false, Location.Null, Location.Null)
911 public Block (Block parent, bool implicit_block)
912 : this (parent, implicit_block, Location.Null, Location.Null)
915 public Block (Block parent, Location start, Location end)
916 : this (parent, false, start, end)
919 public Block (Block parent, bool implicit_block, Location start, Location end)
922 parent.AddChild (this);
924 this.Parent = parent;
925 this.Implicit = implicit_block;
926 this.StartLocation = start;
927 this.EndLocation = end;
930 statements = new ArrayList ();
939 void AddChild (Block b)
941 if (children == null)
942 children = new ArrayList ();
947 public void SetEndLocation (Location loc)
953 /// Adds a label to the current block.
957 /// false if the name already exists in this block. true
961 public bool AddLabel (string name, LabeledStatement target)
964 labels = new Hashtable ();
965 if (labels.Contains (name))
968 labels.Add (name, target);
972 public LabeledStatement LookupLabel (string name)
975 if (labels.Contains (name))
976 return ((LabeledStatement) labels [name]);
980 return Parent.LookupLabel (name);
985 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
987 if (variables == null)
988 variables = new Hashtable ();
990 if (GetVariableType (name) != null)
995 Parameter p = pars.GetParameterByName (name, out idx);
1000 VariableInfo vi = new VariableInfo (type, l);
1002 variables.Add (name, vi);
1004 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1008 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1010 if (AddVariable (type, name, pars, l) == null)
1013 if (constants == null)
1014 constants = new Hashtable ();
1016 constants.Add (name, value);
1020 public Hashtable Variables {
1026 public VariableInfo GetVariableInfo (string name)
1028 if (variables != null) {
1030 temp = variables [name];
1033 return (VariableInfo) temp;
1038 return Parent.GetVariableInfo (name);
1043 public Expression GetVariableType (string name)
1045 VariableInfo vi = GetVariableInfo (name);
1053 public Expression GetConstantExpression (string name)
1055 if (constants != null) {
1057 temp = constants [name];
1060 return (Expression) temp;
1064 return Parent.GetConstantExpression (name);
1070 /// True if the variable named @name has been defined
1073 public bool IsVariableDefined (string name)
1075 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1076 if (variables != null) {
1077 if (variables.Contains (name))
1082 return Parent.IsVariableDefined (name);
1088 /// True if the variable named @name is a constant
1090 public bool IsConstant (string name)
1092 Expression e = null;
1094 e = GetConstantExpression (name);
1100 /// Use to fetch the statement associated with this label
1102 public Statement this [string name] {
1104 return (Statement) labels [name];
1109 /// A list of labels that were not used within this block
1111 public string [] GetUnreferenced ()
1113 // FIXME: Implement me
1117 public void AddStatement (Statement s)
1135 /// Emits the variable declarations and labels.
1138 /// tc: is our typecontainer (to resolve type references)
1139 /// ig: is the code generator:
1140 /// toplevel: the toplevel block. This is used for checking
1141 /// that no two labels with the same name are used.
1143 public void EmitMeta (EmitContext ec, Block toplevel)
1145 DeclSpace ds = ec.DeclSpace;
1146 ILGenerator ig = ec.ig;
1149 // Process this block variables
1151 if (variables != null){
1152 local_builders = new Hashtable ();
1154 foreach (DictionaryEntry de in variables){
1155 string name = (string) de.Key;
1156 VariableInfo vi = (VariableInfo) de.Value;
1159 t = ds.ResolveType (vi.Type, false, vi.Location);
1163 vi.VariableType = t;
1164 vi.LocalBuilder = ig.DeclareLocal (t);
1166 if (CodeGen.SymbolWriter != null)
1167 vi.LocalBuilder.SetLocalSymInfo (name);
1169 if (constants == null)
1172 Expression cv = (Expression) constants [name];
1176 Expression e = cv.Resolve (ec);
1180 if (!(e is Constant)){
1181 Report.Error (133, vi.Location,
1182 "The expression being assigned to `" +
1183 name + "' must be constant (" + e + ")");
1187 constants.Remove (name);
1188 constants.Add (name, e);
1193 // Now, handle the children
1195 if (children != null){
1196 foreach (Block b in children)
1197 b.EmitMeta (ec, toplevel);
1201 public void UsageWarning ()
1205 if (variables != null){
1206 foreach (DictionaryEntry de in variables){
1207 VariableInfo vi = (VariableInfo) de.Value;
1212 name = (string) de.Key;
1216 219, vi.Location, "The variable `" + name +
1217 "' is assigned but its value is never used");
1220 168, vi.Location, "The variable `" +
1222 "' is declared but never used");
1227 if (children != null)
1228 foreach (Block b in children)
1232 public override bool Resolve (EmitContext ec)
1234 Block prev_block = ec.CurrentBlock;
1237 ec.CurrentBlock = this;
1238 foreach (Statement s in statements){
1239 if (s.Resolve (ec) == false)
1243 ec.CurrentBlock = prev_block;
1247 public override bool Emit (EmitContext ec)
1249 bool is_ret = false;
1250 Block prev_block = ec.CurrentBlock;
1252 ec.CurrentBlock = this;
1254 if (CodeGen.SymbolWriter != null) {
1255 ec.Mark (StartLocation);
1257 foreach (Statement s in statements) {
1260 is_ret = s.Emit (ec);
1263 ec.Mark (EndLocation);
1265 foreach (Statement s in statements)
1266 is_ret = s.Emit (ec);
1269 ec.CurrentBlock = prev_block;
1274 public class SwitchLabel {
1277 public Location loc;
1278 public Label ILLabel;
1279 public Label ILLabelCode;
1282 // if expr == null, then it is the default case.
1284 public SwitchLabel (Expression expr, Location l)
1290 public Expression Label {
1296 public object Converted {
1303 // Resolves the expression, reduces it to a literal if possible
1304 // and then converts it to the requested type.
1306 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1308 ILLabel = ec.ig.DefineLabel ();
1309 ILLabelCode = ec.ig.DefineLabel ();
1314 Expression e = label.Resolve (ec);
1319 if (!(e is Constant)){
1320 Console.WriteLine ("Value is: " + label);
1321 Report.Error (150, loc, "A constant value is expected");
1325 if (e is StringConstant || e is NullLiteral){
1326 if (required_type == TypeManager.string_type){
1328 ILLabel = ec.ig.DefineLabel ();
1333 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1334 if (converted == null)
1341 public class SwitchSection {
1342 // An array of SwitchLabels.
1343 public readonly ArrayList Labels;
1344 public readonly Block Block;
1346 public SwitchSection (ArrayList labels, Block block)
1353 public class Switch : Statement {
1354 public readonly ArrayList Sections;
1355 public Expression Expr;
1358 /// Maps constants whose type type SwitchType to their SwitchLabels.
1360 public Hashtable Elements;
1363 /// The governing switch type
1365 public Type SwitchType;
1371 Label default_target;
1374 // The types allowed to be implicitly cast from
1375 // on the governing type
1377 static Type [] allowed_types;
1379 public Switch (Expression e, ArrayList sects, Location l)
1386 public bool GotDefault {
1392 public Label DefaultTarget {
1394 return default_target;
1399 // Determines the governing type for a switch. The returned
1400 // expression might be the expression from the switch, or an
1401 // expression that includes any potential conversions to the
1402 // integral types or to string.
1404 Expression SwitchGoverningType (EmitContext ec, Type t)
1406 if (t == TypeManager.int32_type ||
1407 t == TypeManager.uint32_type ||
1408 t == TypeManager.char_type ||
1409 t == TypeManager.byte_type ||
1410 t == TypeManager.sbyte_type ||
1411 t == TypeManager.ushort_type ||
1412 t == TypeManager.short_type ||
1413 t == TypeManager.uint64_type ||
1414 t == TypeManager.int64_type ||
1415 t == TypeManager.string_type ||
1416 t == TypeManager.bool_type ||
1417 t.IsSubclassOf (TypeManager.enum_type))
1420 if (allowed_types == null){
1421 allowed_types = new Type [] {
1422 TypeManager.sbyte_type,
1423 TypeManager.byte_type,
1424 TypeManager.short_type,
1425 TypeManager.ushort_type,
1426 TypeManager.int32_type,
1427 TypeManager.uint32_type,
1428 TypeManager.int64_type,
1429 TypeManager.uint64_type,
1430 TypeManager.char_type,
1431 TypeManager.bool_type,
1432 TypeManager.string_type
1437 // Try to find a *user* defined implicit conversion.
1439 // If there is no implicit conversion, or if there are multiple
1440 // conversions, we have to report an error
1442 Expression converted = null;
1443 foreach (Type tt in allowed_types){
1446 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1450 if (converted != null){
1451 Report.Error (-12, loc, "More than one conversion to an integral " +
1452 " type exists for type `" +
1453 TypeManager.CSharpName (Expr.Type)+"'");
1461 void error152 (string n)
1464 152, "The label `" + n + ":' " +
1465 "is already present on this switch statement");
1469 // Performs the basic sanity checks on the switch statement
1470 // (looks for duplicate keys and non-constant expressions).
1472 // It also returns a hashtable with the keys that we will later
1473 // use to compute the switch tables
1475 bool CheckSwitch (EmitContext ec)
1479 Elements = new Hashtable ();
1481 got_default = false;
1483 if (TypeManager.IsEnumType (SwitchType)){
1484 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1486 compare_type = SwitchType;
1488 foreach (SwitchSection ss in Sections){
1489 foreach (SwitchLabel sl in ss.Labels){
1490 if (!sl.ResolveAndReduce (ec, SwitchType)){
1495 if (sl.Label == null){
1497 error152 ("default");
1504 object key = sl.Converted;
1506 if (key is Constant)
1507 key = ((Constant) key).GetValue ();
1510 key = NullLiteral.Null;
1512 string lname = null;
1513 if (compare_type == TypeManager.uint64_type){
1514 ulong v = (ulong) key;
1516 if (Elements.Contains (v))
1517 lname = v.ToString ();
1519 Elements.Add (v, sl);
1520 } else if (compare_type == TypeManager.int64_type){
1521 long v = (long) key;
1523 if (Elements.Contains (v))
1524 lname = v.ToString ();
1526 Elements.Add (v, sl);
1527 } else if (compare_type == TypeManager.uint32_type){
1528 uint v = (uint) key;
1530 if (Elements.Contains (v))
1531 lname = v.ToString ();
1533 Elements.Add (v, sl);
1534 } else if (compare_type == TypeManager.char_type){
1535 char v = (char) key;
1537 if (Elements.Contains (v))
1538 lname = v.ToString ();
1540 Elements.Add (v, sl);
1541 } else if (compare_type == TypeManager.byte_type){
1542 byte v = (byte) key;
1544 if (Elements.Contains (v))
1545 lname = v.ToString ();
1547 Elements.Add (v, sl);
1548 } else if (compare_type == TypeManager.sbyte_type){
1549 sbyte v = (sbyte) key;
1551 if (Elements.Contains (v))
1552 lname = v.ToString ();
1554 Elements.Add (v, sl);
1555 } else if (compare_type == TypeManager.short_type){
1556 short v = (short) key;
1558 if (Elements.Contains (v))
1559 lname = v.ToString ();
1561 Elements.Add (v, sl);
1562 } else if (compare_type == TypeManager.ushort_type){
1563 ushort v = (ushort) key;
1565 if (Elements.Contains (v))
1566 lname = v.ToString ();
1568 Elements.Add (v, sl);
1569 } else if (compare_type == TypeManager.string_type){
1570 if (key is NullLiteral){
1571 if (Elements.Contains (NullLiteral.Null))
1574 Elements.Add (NullLiteral.Null, null);
1576 string s = (string) key;
1578 if (Elements.Contains (s))
1581 Elements.Add (s, sl);
1583 } else if (compare_type == TypeManager.int32_type) {
1586 if (Elements.Contains (v))
1587 lname = v.ToString ();
1589 Elements.Add (v, sl);
1590 } else if (compare_type == TypeManager.bool_type) {
1591 bool v = (bool) key;
1593 if (Elements.Contains (v))
1594 lname = v.ToString ();
1596 Elements.Add (v, sl);
1600 throw new Exception ("Unknown switch type!" +
1601 SwitchType + " " + compare_type);
1605 error152 ("case + " + lname);
1616 void EmitObjectInteger (ILGenerator ig, object k)
1619 IntConstant.EmitInt (ig, (int) k);
1620 else if (k is Constant) {
1621 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1624 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1627 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1629 IntConstant.EmitInt (ig, (int) (long) k);
1630 ig.Emit (OpCodes.Conv_I8);
1633 LongConstant.EmitLong (ig, (long) k);
1635 else if (k is ulong)
1637 if ((ulong) k < (1L<<32))
1639 IntConstant.EmitInt (ig, (int) (long) k);
1640 ig.Emit (OpCodes.Conv_U8);
1644 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1648 IntConstant.EmitInt (ig, (int) ((char) k));
1649 else if (k is sbyte)
1650 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1652 IntConstant.EmitInt (ig, (int) ((byte) k));
1653 else if (k is short)
1654 IntConstant.EmitInt (ig, (int) ((short) k));
1655 else if (k is ushort)
1656 IntConstant.EmitInt (ig, (int) ((ushort) k));
1658 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1660 throw new Exception ("Unhandled case");
1663 // structure used to hold blocks of keys while calculating table switch
1664 class KeyBlock : IComparable
1666 public KeyBlock (long _nFirst)
1668 nFirst = nLast = _nFirst;
1672 public ArrayList rgKeys = null;
1675 get { return (int) (nLast - nFirst + 1); }
1677 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1679 return kbLast.nLast - kbFirst.nFirst + 1;
1681 public int CompareTo (object obj)
1683 KeyBlock kb = (KeyBlock) obj;
1684 int nLength = Length;
1685 int nLengthOther = kb.Length;
1686 if (nLengthOther == nLength)
1687 return (int) (kb.nFirst - nFirst);
1688 return nLength - nLengthOther;
1693 /// This method emits code for a lookup-based switch statement (non-string)
1694 /// Basically it groups the cases into blocks that are at least half full,
1695 /// and then spits out individual lookup opcodes for each block.
1696 /// It emits the longest blocks first, and short blocks are just
1697 /// handled with direct compares.
1699 /// <param name="ec"></param>
1700 /// <param name="val"></param>
1701 /// <returns></returns>
1702 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1704 int cElements = Elements.Count;
1705 object [] rgKeys = new object [cElements];
1706 Elements.Keys.CopyTo (rgKeys, 0);
1707 Array.Sort (rgKeys);
1709 // initialize the block list with one element per key
1710 ArrayList rgKeyBlocks = new ArrayList ();
1711 foreach (object key in rgKeys)
1712 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1715 // iteratively merge the blocks while they are at least half full
1716 // there's probably a really cool way to do this with a tree...
1717 while (rgKeyBlocks.Count > 1)
1719 ArrayList rgKeyBlocksNew = new ArrayList ();
1720 kbCurr = (KeyBlock) rgKeyBlocks [0];
1721 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1723 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1724 if ((kbCurr.Length + kb.Length) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
1727 kbCurr.nLast = kb.nLast;
1731 // start a new block
1732 rgKeyBlocksNew.Add (kbCurr);
1736 rgKeyBlocksNew.Add (kbCurr);
1737 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1739 rgKeyBlocks = rgKeyBlocksNew;
1742 // initialize the key lists
1743 foreach (KeyBlock kb in rgKeyBlocks)
1744 kb.rgKeys = new ArrayList ();
1746 // fill the key lists
1748 if (rgKeyBlocks.Count > 0) {
1749 kbCurr = (KeyBlock) rgKeyBlocks [0];
1750 foreach (object key in rgKeys)
1752 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1754 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1755 kbCurr.rgKeys.Add (key);
1759 // sort the blocks so we can tackle the largest ones first
1760 rgKeyBlocks.Sort ();
1762 // okay now we can start...
1763 ILGenerator ig = ec.ig;
1764 Label lblEnd = ig.DefineLabel (); // at the end ;-)
1765 Label lblDefault = ig.DefineLabel ();
1767 Type typeKeys = null;
1768 if (rgKeys.Length > 0)
1769 typeKeys = rgKeys [0].GetType (); // used for conversions
1771 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1773 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1774 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1777 foreach (object key in kb.rgKeys)
1779 ig.Emit (OpCodes.Ldloc, val);
1780 EmitObjectInteger (ig, key);
1781 SwitchLabel sl = (SwitchLabel) Elements [key];
1782 ig.Emit (OpCodes.Beq, sl.ILLabel);
1787 // TODO: if all the keys in the block are the same and there are
1788 // no gaps/defaults then just use a range-check.
1789 if (SwitchType == TypeManager.int64_type ||
1790 SwitchType == TypeManager.uint64_type)
1792 // TODO: optimize constant/I4 cases
1794 // check block range (could be > 2^31)
1795 ig.Emit (OpCodes.Ldloc, val);
1796 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1797 ig.Emit (OpCodes.Blt, lblDefault);
1798 ig.Emit (OpCodes.Ldloc, val);
1799 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1800 ig.Emit (OpCodes.Bgt, lblDefault);
1803 ig.Emit (OpCodes.Ldloc, val);
1806 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1807 ig.Emit (OpCodes.Sub);
1809 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
1814 ig.Emit (OpCodes.Ldloc, val);
1815 int nFirst = (int) kb.nFirst;
1818 IntConstant.EmitInt (ig, nFirst);
1819 ig.Emit (OpCodes.Sub);
1821 else if (nFirst < 0)
1823 IntConstant.EmitInt (ig, -nFirst);
1824 ig.Emit (OpCodes.Add);
1828 // first, build the list of labels for the switch
1830 int cJumps = kb.Length;
1831 Label [] rgLabels = new Label [cJumps];
1832 for (int iJump = 0; iJump < cJumps; iJump++)
1834 object key = kb.rgKeys [iKey];
1835 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1837 SwitchLabel sl = (SwitchLabel) Elements [key];
1838 rgLabels [iJump] = sl.ILLabel;
1842 rgLabels [iJump] = lblDefault;
1844 // emit the switch opcode
1845 ig.Emit (OpCodes.Switch, rgLabels);
1848 // mark the default for this block
1850 ig.MarkLabel (lblDefault);
1853 // TODO: find the default case and emit it here,
1854 // to prevent having to do the following jump.
1855 // make sure to mark other labels in the default section
1857 // the last default just goes to the end
1858 ig.Emit (OpCodes.Br, lblDefault);
1860 // now emit the code for the sections
1861 bool fFoundDefault = false;
1862 bool fAllReturn = true;
1863 foreach (SwitchSection ss in Sections)
1865 foreach (SwitchLabel sl in ss.Labels)
1867 ig.MarkLabel (sl.ILLabel);
1868 ig.MarkLabel (sl.ILLabelCode);
1869 if (sl.Label == null)
1871 ig.MarkLabel (lblDefault);
1872 fFoundDefault = true;
1875 fAllReturn &= ss.Block.Emit (ec);
1876 //ig.Emit (OpCodes.Br, lblEnd);
1879 if (!fFoundDefault) {
1880 ig.MarkLabel (lblDefault);
1883 ig.MarkLabel (lblEnd);
1888 // This simple emit switch works, but does not take advantage of the
1890 // TODO: remove non-string logic from here
1891 // TODO: binary search strings?
1893 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1895 ILGenerator ig = ec.ig;
1896 Label end_of_switch = ig.DefineLabel ();
1897 Label next_test = ig.DefineLabel ();
1898 Label null_target = ig.DefineLabel ();
1899 bool default_found = false;
1900 bool first_test = true;
1901 bool pending_goto_end = false;
1902 bool all_return = true;
1903 bool is_string = false;
1907 // Special processing for strings: we cant compare
1910 if (SwitchType == TypeManager.string_type){
1911 ig.Emit (OpCodes.Ldloc, val);
1914 if (Elements.Contains (NullLiteral.Null)){
1915 ig.Emit (OpCodes.Brfalse, null_target);
1917 ig.Emit (OpCodes.Brfalse, default_target);
1919 ig.Emit (OpCodes.Ldloc, val);
1920 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1921 ig.Emit (OpCodes.Stloc, val);
1924 SwitchSection last_section;
1925 last_section = (SwitchSection) Sections [Sections.Count-1];
1927 foreach (SwitchSection ss in Sections){
1928 Label sec_begin = ig.DefineLabel ();
1930 if (pending_goto_end)
1931 ig.Emit (OpCodes.Br, end_of_switch);
1933 int label_count = ss.Labels.Count;
1935 foreach (SwitchLabel sl in ss.Labels){
1936 ig.MarkLabel (sl.ILLabel);
1939 ig.MarkLabel (next_test);
1940 next_test = ig.DefineLabel ();
1943 // If we are the default target
1945 if (sl.Label == null){
1946 ig.MarkLabel (default_target);
1947 default_found = true;
1949 object lit = sl.Converted;
1951 if (lit is NullLiteral){
1953 if (label_count == 1)
1954 ig.Emit (OpCodes.Br, next_test);
1959 StringConstant str = (StringConstant) lit;
1961 ig.Emit (OpCodes.Ldloc, val);
1962 ig.Emit (OpCodes.Ldstr, str.Value);
1963 if (label_count == 1)
1964 ig.Emit (OpCodes.Bne_Un, next_test);
1966 ig.Emit (OpCodes.Beq, sec_begin);
1968 ig.Emit (OpCodes.Ldloc, val);
1969 EmitObjectInteger (ig, lit);
1970 ig.Emit (OpCodes.Ceq);
1971 if (label_count == 1)
1972 ig.Emit (OpCodes.Brfalse, next_test);
1974 ig.Emit (OpCodes.Brtrue, sec_begin);
1978 if (label_count != 1 && ss != last_section)
1979 ig.Emit (OpCodes.Br, next_test);
1982 ig.MarkLabel (null_target);
1983 ig.MarkLabel (sec_begin);
1984 foreach (SwitchLabel sl in ss.Labels)
\r
1985 ig.MarkLabel (sl.ILLabelCode);
1986 if (ss.Block.Emit (ec))
1987 pending_goto_end = false;
1990 pending_goto_end = true;
1994 if (!default_found){
1995 ig.MarkLabel (default_target);
1998 ig.MarkLabel (next_test);
1999 ig.MarkLabel (end_of_switch);
2004 public override bool Resolve (EmitContext ec)
2006 foreach (SwitchSection ss in Sections){
2007 if (ss.Block.Resolve (ec) != true)
2014 public override bool Emit (EmitContext ec)
2016 Expr = Expr.Resolve (ec);
2020 Expression new_expr = SwitchGoverningType (ec, Expr.Type);
2021 if (new_expr == null){
2022 Report.Error (151, loc, "An integer type or string was expected for switch");
2027 SwitchType = new_expr.Type;
2029 if (!CheckSwitch (ec))
2032 // Store variable for comparission purposes
2033 LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2035 ec.ig.Emit (OpCodes.Stloc, value);
2037 ILGenerator ig = ec.ig;
2039 default_target = ig.DefineLabel ();
2042 // Setup the codegen context
2044 Label old_end = ec.LoopEnd;
2045 Switch old_switch = ec.Switch;
2047 ec.LoopEnd = ig.DefineLabel ();
2052 if (SwitchType == TypeManager.string_type)
2053 all_return = SimpleSwitchEmit (ec, value);
2055 all_return = TableSwitchEmit (ec, value);
2057 // Restore context state.
2058 ig.MarkLabel (ec.LoopEnd);
2061 // Restore the previous context
2063 ec.LoopEnd = old_end;
2064 ec.Switch = old_switch;
2070 public class Lock : Statement {
2072 Statement Statement;
2074 public Lock (Expression expr, Statement stmt, Location l)
2081 public override bool Resolve (EmitContext ec)
2083 expr = expr.Resolve (ec);
2084 return Statement.Resolve (ec) && expr != null;
2087 public override bool Emit (EmitContext ec)
2089 Type type = expr.Type;
2092 if (type.IsValueType){
2093 Report.Error (185, loc, "lock statement requires the expression to be " +
2094 " a reference type (type is: `" +
2095 TypeManager.CSharpName (type) + "'");
2099 ILGenerator ig = ec.ig;
2100 LocalBuilder temp = ig.DeclareLocal (type);
2103 ig.Emit (OpCodes.Dup);
2104 ig.Emit (OpCodes.Stloc, temp);
2105 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2108 Label end = ig.BeginExceptionBlock ();
2109 bool old_in_try = ec.InTry;
2111 Label finish = ig.DefineLabel ();
2112 val = Statement.Emit (ec);
2113 ec.InTry = old_in_try;
2114 // ig.Emit (OpCodes.Leave, finish);
2116 ig.MarkLabel (finish);
2119 ig.BeginFinallyBlock ();
2120 ig.Emit (OpCodes.Ldloc, temp);
2121 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2122 ig.EndExceptionBlock ();
2128 public class Unchecked : Statement {
2129 public readonly Block Block;
2131 public Unchecked (Block b)
2136 public override bool Resolve (EmitContext ec)
2138 return Block.Resolve (ec);
2141 public override bool Emit (EmitContext ec)
2143 bool previous_state = ec.CheckState;
2144 bool previous_state_const = ec.ConstantCheckState;
2147 ec.CheckState = false;
2148 ec.ConstantCheckState = false;
2149 val = Block.Emit (ec);
2150 ec.CheckState = previous_state;
2151 ec.ConstantCheckState = previous_state_const;
2157 public class Checked : Statement {
2158 public readonly Block Block;
2160 public Checked (Block b)
2165 public override bool Resolve (EmitContext ec)
2167 bool previous_state = ec.CheckState;
2168 bool previous_state_const = ec.ConstantCheckState;
2170 ec.CheckState = true;
2171 ec.ConstantCheckState = true;
2172 bool ret = Block.Resolve (ec);
2173 ec.CheckState = previous_state;
2174 ec.ConstantCheckState = previous_state_const;
2179 public override bool Emit (EmitContext ec)
2181 bool previous_state = ec.CheckState;
2182 bool previous_state_const = ec.ConstantCheckState;
2185 ec.CheckState = true;
2186 ec.ConstantCheckState = true;
2187 val = Block.Emit (ec);
2188 ec.CheckState = previous_state;
2189 ec.ConstantCheckState = previous_state_const;
2195 public class Unsafe : Statement {
2196 public readonly Block Block;
2198 public Unsafe (Block b)
2203 public override bool Resolve (EmitContext ec)
2205 bool previous_state = ec.InUnsafe;
2209 val = Block.Resolve (ec);
2210 ec.InUnsafe = previous_state;
2215 public override bool Emit (EmitContext ec)
2217 bool previous_state = ec.InUnsafe;
2221 val = Block.Emit (ec);
2222 ec.InUnsafe = previous_state;
2231 public class Fixed : Statement {
2233 ArrayList declarators;
2234 Statement statement;
2236 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
2239 declarators = decls;
2244 public override bool Resolve (EmitContext ec)
2246 return statement.Resolve (ec);
2249 public override bool Emit (EmitContext ec)
2251 ILGenerator ig = ec.ig;
2254 t = ec.DeclSpace.ResolveType (type, false, loc);
2258 bool is_ret = false;
2260 foreach (Pair p in declarators){
2261 VariableInfo vi = (VariableInfo) p.First;
2262 Expression e = (Expression) p.Second;
2265 // The rules for the possible declarators are pretty wise,
2266 // but the production on the grammar is more concise.
2268 // So we have to enforce these rules here.
2270 // We do not resolve before doing the case 1 test,
2271 // because the grammar is explicit in that the token &
2272 // is present, so we need to test for this particular case.
2276 // Case 1: & object.
2278 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2279 Expression child = ((Unary) e).Expr;
2282 if (child is ParameterReference || child is LocalVariableReference){
2285 "No need to use fixed statement for parameters or " +
2286 "local variable declarations (address is already " +
2295 child = ((Unary) e).Expr;
2297 if (!TypeManager.VerifyUnManaged (child.Type, loc))
2301 // Store pointer in pinned location
2304 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2306 is_ret = statement.Emit (ec);
2308 // Clear the pinned variable.
2309 ig.Emit (OpCodes.Ldc_I4_0);
2310 ig.Emit (OpCodes.Conv_U);
2311 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2323 if (e.Type.IsArray){
2324 Type array_type = e.Type.GetElementType ();
2328 // Provided that array_type is unmanaged,
2330 if (!TypeManager.VerifyUnManaged (array_type, loc))
2334 // and T* is implicitly convertible to the
2335 // pointer type given in the fixed statement.
2337 ArrayPtr array_ptr = new ArrayPtr (e);
2339 Expression converted = Expression.ConvertImplicitRequired (
2340 ec, array_ptr, vi.VariableType, loc);
2341 if (converted == null)
2345 // Store pointer in pinned location
2347 converted.Emit (ec);
2349 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2351 is_ret = statement.Emit (ec);
2353 // Clear the pinned variable.
2354 ig.Emit (OpCodes.Ldc_I4_0);
2355 ig.Emit (OpCodes.Conv_U);
2356 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2364 if (e.Type == TypeManager.string_type){
2365 LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2366 TypeManager.MakePinned (pinned_string);
2369 ig.Emit (OpCodes.Stloc, pinned_string);
2371 Expression sptr = new StringPtr (pinned_string);
2372 Expression converted = Expression.ConvertImplicitRequired (
2373 ec, sptr, vi.VariableType, loc);
2375 if (converted == null)
2378 converted.Emit (ec);
2379 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2381 is_ret = statement.Emit (ec);
2383 // Clear the pinned variable
2384 ig.Emit (OpCodes.Ldnull);
2385 ig.Emit (OpCodes.Stloc, pinned_string);
2393 public class Catch {
2394 public readonly Expression Type;
2395 public readonly string Name;
2396 public readonly Block Block;
2397 public readonly Location Location;
2399 public Catch (Expression type, string name, Block block, Location l)
2408 public class Try : Statement {
2409 public readonly Block Fini, Block;
2410 public readonly ArrayList Specific;
2411 public readonly Catch General;
2414 // specific, general and fini might all be null.
2416 public Try (Block block, ArrayList specific, Catch general, Block fini)
2418 if (specific == null && general == null){
2419 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2423 this.Specific = specific;
2424 this.General = general;
2428 public override bool Resolve (EmitContext ec)
2432 if (General != null)
2433 if (!General.Block.Resolve (ec))
2436 foreach (Catch c in Specific){
2437 if (!c.Block.Resolve (ec))
2441 if (!Block.Resolve (ec))
2445 if (!Fini.Resolve (ec))
2451 public override bool Emit (EmitContext ec)
2453 ILGenerator ig = ec.ig;
2455 Label finish = ig.DefineLabel ();;
2459 end = ig.BeginExceptionBlock ();
2460 bool old_in_try = ec.InTry;
2462 returns = Block.Emit (ec);
2463 ec.InTry = old_in_try;
2466 // System.Reflection.Emit provides this automatically:
2467 // ig.Emit (OpCodes.Leave, finish);
2469 bool old_in_catch = ec.InCatch;
2471 DeclSpace ds = ec.DeclSpace;
2473 foreach (Catch c in Specific){
2474 Type catch_type = ds.ResolveType (c.Type, false, c.Location);
2477 if (catch_type == null)
2480 ig.BeginCatchBlock (catch_type);
2482 if (c.Name != null){
2483 vi = c.Block.GetVariableInfo (c.Name);
2485 Console.WriteLine ("This should not happen! variable does not exist in this block");
2486 Environment.Exit (0);
2489 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2491 ig.Emit (OpCodes.Pop);
2493 if (!c.Block.Emit (ec))
2497 if (General != null){
2498 ig.BeginCatchBlock (TypeManager.object_type);
2499 ig.Emit (OpCodes.Pop);
2500 if (!General.Block.Emit (ec))
2503 ec.InCatch = old_in_catch;
2505 ig.MarkLabel (finish);
2507 ig.BeginFinallyBlock ();
2508 bool old_in_finally = ec.InFinally;
2509 ec.InFinally = true;
2511 ec.InFinally = old_in_finally;
2514 ig.EndExceptionBlock ();
2517 if (!returns || ec.InTry || ec.InCatch)
2520 // Unfortunately, System.Reflection.Emit automatically emits a leave
2521 // to the end of the finally block. This is a problem if `returns'
2522 // is true since we may jump to a point after the end of the method.
2523 // As a workaround, emit an explicit ret here.
2525 if (ec.ReturnType != null)
2526 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2527 ec.ig.Emit (OpCodes.Ret);
2534 // FIXME: We still do not support the expression variant of the using
2537 public class Using : Statement {
2538 object expression_or_block;
2539 Statement Statement;
2541 public Using (object expression_or_block, Statement stmt, Location l)
2543 this.expression_or_block = expression_or_block;
2549 // Emits the code for the case of using using a local variable declaration.
2551 bool EmitLocalVariableDecls (EmitContext ec, Expression expr_type, ArrayList var_list)
2553 ILGenerator ig = ec.ig;
2554 Expression [] converted_vars;
2555 bool need_conv = false;
2556 Type type = ec.DeclSpace.ResolveType (expr_type, false, loc);
2563 // The type must be an IDisposable or an implicit conversion
2566 converted_vars = new Expression [var_list.Count];
2567 if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2568 foreach (DictionaryEntry e in var_list){
2569 Expression var = (Expression) e.Key;
2571 var = var.Resolve (ec);
2575 converted_vars [i] = Expression.ConvertImplicit (
2576 ec, var, TypeManager.idisposable_type, loc);
2578 if (converted_vars [i] == null)
2586 bool old_in_try = ec.InTry;
2589 foreach (DictionaryEntry e in var_list){
2590 LocalVariableReference var = (LocalVariableReference) e.Key;
2591 Expression expr = (Expression) e.Value;
2594 a = new Assign (var, expr, loc);
2597 converted_vars [i] = var;
2603 ((ExpressionStatement) a).EmitStatement (ec);
2605 ig.BeginExceptionBlock ();
2610 Statement.Emit (ec);
2611 ec.InTry = old_in_try;
2613 bool old_in_finally = ec.InFinally;
2614 ec.InFinally = true;
2615 var_list.Reverse ();
2616 foreach (DictionaryEntry e in var_list){
2617 LocalVariableReference var = (LocalVariableReference) e.Key;
2618 Label skip = ig.DefineLabel ();
2621 ig.BeginFinallyBlock ();
2624 ig.Emit (OpCodes.Brfalse, skip);
2625 converted_vars [i].Emit (ec);
2626 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2627 ig.MarkLabel (skip);
2628 ig.EndExceptionBlock ();
2630 ec.InFinally = old_in_finally;
2635 bool EmitExpression (EmitContext ec, Expression expr)
2637 Type expr_type = expr.Type;
2638 Expression conv = null;
2640 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2641 conv = Expression.ConvertImplicit (
2642 ec, expr, TypeManager.idisposable_type, loc);
2649 // Make a copy of the expression and operate on that.
2651 ILGenerator ig = ec.ig;
2652 LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2657 ig.Emit (OpCodes.Stloc, local_copy);
2659 bool old_in_try = ec.InTry;
2661 ig.BeginExceptionBlock ();
2662 Statement.Emit (ec);
2663 ec.InTry = old_in_try;
2665 Label skip = ig.DefineLabel ();
2666 bool old_in_finally = ec.InFinally;
2667 ig.BeginFinallyBlock ();
2668 ig.Emit (OpCodes.Ldloc, local_copy);
2669 ig.Emit (OpCodes.Brfalse, skip);
2670 ig.Emit (OpCodes.Ldloc, local_copy);
2671 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2672 ig.MarkLabel (skip);
2673 ec.InFinally = old_in_finally;
2674 ig.EndExceptionBlock ();
2679 public override bool Resolve (EmitContext ec)
2681 return Statement.Resolve (ec);
2684 public override bool Emit (EmitContext ec)
2686 if (expression_or_block is DictionaryEntry){
2687 Expression expr_type = (Expression) ((DictionaryEntry) expression_or_block).Key;
2688 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2690 return EmitLocalVariableDecls (ec, expr_type, var_list);
2691 } if (expression_or_block is Expression){
2692 Expression e = (Expression) expression_or_block;
2698 return EmitExpression (ec, e);
2705 /// Implementation of the foreach C# statement
2707 public class Foreach : Statement {
2709 LocalVariableReference variable;
2711 Statement statement;
2713 public Foreach (Expression type, LocalVariableReference var, Expression expr,
2714 Statement stmt, Location l)
2717 this.variable = var;
2723 public override bool Resolve (EmitContext ec)
2725 expr = expr.Resolve (ec);
2726 return statement.Resolve (ec) && expr != null;
2730 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2732 static MethodInfo FetchMethodMoveNext (Type t)
2734 MemberInfo [] move_next_list;
2736 move_next_list = TypeContainer.FindMembers (
2737 t, MemberTypes.Method,
2738 BindingFlags.Public | BindingFlags.Instance,
2739 Type.FilterName, "MoveNext");
2740 if (move_next_list == null || move_next_list.Length == 0)
2743 foreach (MemberInfo m in move_next_list){
2744 MethodInfo mi = (MethodInfo) m;
2747 args = TypeManager.GetArgumentTypes (mi);
2748 if (args != null && args.Length == 0){
2749 if (mi.ReturnType == TypeManager.bool_type)
2757 // Retrieves a `public T get_Current ()' method from the Type `t'
2759 static MethodInfo FetchMethodGetCurrent (Type t)
2761 MemberInfo [] move_next_list;
2763 move_next_list = TypeContainer.FindMembers (
2764 t, MemberTypes.Method,
2765 BindingFlags.Public | BindingFlags.Instance,
2766 Type.FilterName, "get_Current");
2767 if (move_next_list == null || move_next_list.Length == 0)
2770 foreach (MemberInfo m in move_next_list){
2771 MethodInfo mi = (MethodInfo) m;
2774 args = TypeManager.GetArgumentTypes (mi);
2775 if (args != null && args.Length == 0)
2782 // This struct records the helper methods used by the Foreach construct
2784 class ForeachHelperMethods {
2785 public EmitContext ec;
2786 public MethodInfo get_enumerator;
2787 public MethodInfo move_next;
2788 public MethodInfo get_current;
2789 public Type element_type;
2791 public ForeachHelperMethods (EmitContext ec)
2794 this.element_type = TypeManager.object_type;
2798 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2803 if (!(m is MethodInfo))
2806 if (m.Name != "GetEnumerator")
2809 MethodInfo mi = (MethodInfo) m;
2810 Type [] args = TypeManager.GetArgumentTypes (mi);
2812 if (args.Length != 0)
2815 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2816 EmitContext ec = hm.ec;
2819 // Check whether GetEnumerator is accessible to us
2821 MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2823 Type declaring = mi.DeclaringType;
2824 if (prot == MethodAttributes.Private){
2825 if (declaring != ec.ContainerType)
2827 } else if (prot == MethodAttributes.FamANDAssem){
2828 // If from a different assembly, false
2829 if (!(mi is MethodBuilder))
2832 // Are we being invoked from the same class, or from a derived method?
2834 if (ec.ContainerType != declaring){
2835 if (!ec.ContainerType.IsSubclassOf (declaring))
2838 } else if (prot == MethodAttributes.FamORAssem){
2839 if (!(mi is MethodBuilder ||
2840 ec.ContainerType == declaring ||
2841 ec.ContainerType.IsSubclassOf (declaring)))
2843 } if (prot == MethodAttributes.Family){
2844 if (!(ec.ContainerType == declaring ||
2845 ec.ContainerType.IsSubclassOf (declaring)))
2850 // Ok, we can access it, now make sure that we can do something
2851 // with this `GetEnumerator'
2854 if (mi.ReturnType == TypeManager.ienumerator_type ||
2855 TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
2856 (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
2857 hm.move_next = TypeManager.bool_movenext_void;
2858 hm.get_current = TypeManager.object_getcurrent_void;
2863 // Ok, so they dont return an IEnumerable, we will have to
2864 // find if they support the GetEnumerator pattern.
2866 Type return_type = mi.ReturnType;
2868 hm.move_next = FetchMethodMoveNext (return_type);
2869 if (hm.move_next == null)
2871 hm.get_current = FetchMethodGetCurrent (return_type);
2872 if (hm.get_current == null)
2875 hm.element_type = hm.get_current.ReturnType;
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 (hm.element_type);
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;