2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2001 Ximian, Inc.
11 using System.Reflection;
12 using System.Reflection.Emit;
16 using System.Collections;
18 public abstract class Statement {
21 // Return value indicates whether the last instruction
22 // was a return instruction
24 public abstract bool Emit (EmitContext ec);
27 // Emits a bool expression. Generates a jump to the `t' label if true
28 // if defined, or to `f' if defined.
30 // t and f can not be both non-null
32 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
39 if (e.Type != TypeManager.bool_type)
40 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
45 31, "Can not convert the expression to a boolean");
53 if (u.Oper == Unary.Operator.LogicalNot){
56 u.EmitLogicalNot (ec);
65 ec.ig.Emit (OpCodes.Brfalse, l);
67 ec.ig.Emit (OpCodes.Brtrue, l);
70 ec.ig.Emit (OpCodes.Brtrue, l);
72 ec.ig.Emit (OpCodes.Brfalse, l);
80 public class EmptyStatement : Statement {
81 public override bool Emit (EmitContext ec)
87 public class If : Statement {
88 public readonly Expression Expr;
89 public readonly Statement TrueStatement;
90 public readonly Statement FalseStatement;
92 public If (Expression expr, Statement trueStatement)
95 TrueStatement = trueStatement;
98 public If (Expression expr,
99 Statement trueStatement,
100 Statement falseStatement)
103 TrueStatement = trueStatement;
104 FalseStatement = falseStatement;
107 public override bool Emit (EmitContext ec)
109 ILGenerator ig = ec.ig;
110 Label false_target = ig.DefineLabel ();
114 if (!EmitBoolExpression (ec, Expr, false_target, false))
117 is_ret = TrueStatement.Emit (ec);
119 if (FalseStatement != null){
120 bool branch_emitted = false;
122 end = ig.DefineLabel ();
124 ig.Emit (OpCodes.Br, end);
125 branch_emitted = true;
128 ig.MarkLabel (false_target);
129 is_ret = FalseStatement.Emit (ec);
134 ig.MarkLabel (false_target);
140 public class Do : Statement {
141 public readonly Expression Expr;
142 public readonly Statement EmbeddedStatement;
144 public Do (Statement statement, Expression boolExpr)
147 EmbeddedStatement = statement;
150 public override bool Emit (EmitContext ec)
152 ILGenerator ig = ec.ig;
153 Label loop = ig.DefineLabel ();
154 Label old_begin = ec.LoopBegin;
155 Label old_end = ec.LoopEnd;
156 bool old_inloop = ec.InLoop;
158 ec.LoopBegin = ig.DefineLabel ();
159 ec.LoopEnd = ig.DefineLabel ();
163 EmbeddedStatement.Emit (ec);
164 ig.MarkLabel (ec.LoopBegin);
165 EmitBoolExpression (ec, Expr, loop, true);
166 ig.MarkLabel (ec.LoopEnd);
168 ec.LoopBegin = old_begin;
169 ec.LoopEnd = old_end;
170 ec.InLoop = old_inloop;
176 public class While : Statement {
177 public readonly Expression Expr;
178 public readonly Statement Statement;
180 public While (Expression boolExpr, Statement statement)
183 Statement = statement;
186 public override bool Emit (EmitContext ec)
188 ILGenerator ig = ec.ig;
189 Label old_begin = ec.LoopBegin;
190 Label old_end = ec.LoopEnd;
191 bool old_inloop = ec.InLoop;
193 ec.LoopBegin = ig.DefineLabel ();
194 ec.LoopEnd = ig.DefineLabel ();
197 ig.MarkLabel (ec.LoopBegin);
198 EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
200 ig.Emit (OpCodes.Br, ec.LoopBegin);
201 ig.MarkLabel (ec.LoopEnd);
203 ec.LoopBegin = old_begin;
204 ec.LoopEnd = old_end;
205 ec.InLoop = old_inloop;
211 public class For : Statement {
212 public readonly Statement InitStatement;
213 public readonly Expression Test;
214 public readonly Statement Increment;
215 public readonly Statement Statement;
217 public For (Statement initStatement,
222 InitStatement = initStatement;
224 Increment = increment;
225 Statement = statement;
228 public override bool Emit (EmitContext ec)
230 ILGenerator ig = ec.ig;
231 Label old_begin = ec.LoopBegin;
232 Label old_end = ec.LoopEnd;
233 bool old_inloop = ec.InLoop;
234 Label loop = ig.DefineLabel ();
236 if (! (InitStatement is EmptyStatement))
237 InitStatement.Emit (ec);
239 ec.LoopBegin = ig.DefineLabel ();
240 ec.LoopEnd = ig.DefineLabel ();
244 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
246 ig.MarkLabel (ec.LoopBegin);
247 if (!(Increment is EmptyStatement))
249 ig.Emit (OpCodes.Br, loop);
250 ig.MarkLabel (ec.LoopEnd);
252 ec.LoopBegin = old_begin;
253 ec.LoopEnd = old_end;
254 ec.InLoop = old_inloop;
259 public class StatementExpression : Statement {
260 public readonly ExpressionStatement Expr;
262 public StatementExpression (ExpressionStatement expr)
267 public override bool Emit (EmitContext ec)
269 ILGenerator ig = ec.ig;
272 ne = Expr.Resolve (ec);
274 if (ne is ExpressionStatement)
275 ((ExpressionStatement) ne).EmitStatement (ec);
278 ig.Emit (OpCodes.Pop);
286 public class Return : Statement {
287 public Expression Expr;
288 public readonly Location loc;
290 public Return (Expression expr, Location l)
296 public override bool Emit (EmitContext ec)
298 if (ec.ReturnType == null){
300 Report.Error (127, loc, "Return with a value not allowed here");
305 Report.Error (126, loc, "An object of type `" +
306 TypeManager.CSharpName (ec.ReturnType) + "' is " +
307 "expected for the return statement");
311 Expr = Expr.Resolve (ec);
315 if (Expr.Type != ec.ReturnType)
316 Expr = Expression.ConvertImplicitRequired (
317 ec, Expr, ec.ReturnType, loc);
325 ec.ig.Emit (OpCodes.Ret);
331 public class Goto : Statement {
335 public Goto (string label, Location l)
341 public string Target {
347 public override bool Emit (EmitContext ec)
349 Console.WriteLine ("Attempting to goto to: " + target);
355 public class Throw : Statement {
356 public readonly Expression Expr;
358 public Throw (Expression expr)
363 public override bool Emit (EmitContext ec)
365 Expression e = Expr.Resolve (ec);
371 ec.ig.Emit (OpCodes.Throw);
377 public class Break : Statement {
380 public Break (Location l)
385 public override bool Emit (EmitContext ec)
387 ILGenerator ig = ec.ig;
390 Report.Error (139, loc, "No enclosing loop to continue to");
394 ig.Emit (OpCodes.Br, ec.LoopEnd);
399 public class Continue : Statement {
402 public Continue (Location l)
407 public override bool Emit (EmitContext ec)
409 Label begin = ec.LoopBegin;
412 Report.Error (139, loc, "No enclosing loop to continue to");
416 ec.ig.Emit (OpCodes.Br, begin);
421 public class VariableInfo {
422 public readonly string Type;
423 public LocalBuilder LocalBuilder;
424 public Type VariableType;
425 public readonly Location Location;
429 public bool Assigned;
431 public VariableInfo (string type, Location l)
442 throw new Exception ("Unassigned idx for variable");
455 // Used for Label management
458 public class Block : Statement {
459 public readonly Block Parent;
460 public readonly bool Implicit;
461 public readonly string Label;
464 // The statements in this block
466 StatementCollection statements;
469 // An array of Blocks. We keep track of children just
470 // to generate the local variable declarations.
472 // Statements and child statements are handled through the
478 // Labels. (label, block) pairs.
483 // Keeps track of (name, type) pairs
488 // Maps variable names to ILGenerator.LocalBuilders
490 Hashtable local_builders;
494 public Block (Block parent)
497 parent.AddChild (this);
499 this.Parent = parent;
500 this.Implicit = false;
503 public Block (Block parent, bool implicit_block)
506 parent.AddChild (this);
508 this.Parent = parent;
509 this.Implicit = true;
512 public Block (Block parent, string labeled)
515 parent.AddChild (this);
517 this.Parent = parent;
518 this.Implicit = true;
522 public void AddChild (Block b)
524 if (children == null)
525 children = new ArrayList ();
531 // Adds a label to the current block.
535 // false if the name already exists in this block. true
539 public bool AddLabel (string name, Block block)
542 labels = new Hashtable ();
543 if (labels.Contains (name))
546 labels.Add (name, block);
550 public bool AddVariable (string type, string name, Location l)
552 if (variables == null)
553 variables = new Hashtable ();
555 if (GetVariableType (name) != null)
558 VariableInfo vi = new VariableInfo (type, l);
560 variables.Add (name, vi);
564 public Hashtable Variables {
570 public VariableInfo GetVariableInfo (string name)
572 if (variables != null) {
574 temp = variables [name];
577 return (VariableInfo) temp;
581 return Parent.GetVariableInfo (name);
587 public string GetVariableType (string name)
589 VariableInfo vi = GetVariableInfo (name);
598 // True if the variable named @name has been defined
601 public bool IsVariableDefined (string name)
603 return GetVariableType (name) != null;
607 // Use to fetch the statement associated with this label
609 public Statement this [string name] {
611 return (Statement) labels [name];
616 // A list of labels that were not used within this block
618 public string [] GetUnreferenced ()
620 // FIXME: Implement me
624 public StatementCollection Statements {
626 if (statements == null)
627 statements = new StatementCollection ();
633 public void AddStatement (Statement s)
635 if (statements == null)
636 statements = new StatementCollection ();
654 // Creates a compiler-internal identifier, this is
655 // used to create temporary variables that should not
656 // be seen by the application
658 int internal_id_serial;
659 public string MakeInternalID () {
660 string ret = internal_id_serial.ToString ();
662 internal_id_serial++;
667 // Emits the variable declarations and labels.
670 // tc: is our typecontainer (to resolve type references)
671 // ig: is the code generator:
672 // toplevel: the toplevel block. This is used for checking
673 // that no two labels with the same name are used.
675 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
678 // Process this block variables
680 if (variables != null){
681 local_builders = new Hashtable ();
683 foreach (DictionaryEntry de in variables){
684 string name = (string) de.Key;
685 VariableInfo vi = (VariableInfo) de.Value;
688 t = tc.LookupType (vi.Type, false);
693 vi.LocalBuilder = ig.DeclareLocal (t);
699 // Now, handle the children
701 if (children != null){
702 foreach (Block b in children)
703 b.EmitMeta (tc, ig, toplevel, count);
707 public void UsageWarning ()
711 if (variables != null){
712 foreach (DictionaryEntry de in variables){
713 VariableInfo vi = (VariableInfo) de.Value;
718 name = (string) de.Key;
722 219, vi.Location, "The variable `" + name +
723 "' is assigned but its value is never used");
726 168, vi.Location, "The variable `" +
728 "' is declared but never used");
733 if (children != null)
734 foreach (Block b in children)
738 public override bool Emit (EmitContext ec)
741 Block prev_block = ec.CurrentBlock;
743 ec.CurrentBlock = this;
744 foreach (Statement s in Statements)
745 is_ret = s.Emit (ec);
747 ec.CurrentBlock = prev_block;
752 public class SwitchLabel {
756 // if expr == null, then it is the default case.
758 public SwitchLabel (Expression expr)
763 public Expression Label {
770 public class SwitchSection {
771 // An array of SwitchLabels.
775 public SwitchSection (ArrayList labels, Block block)
777 this.labels = labels;
787 public ArrayList Labels {
794 public class Switch : Statement {
798 public Switch (Expression expr, ArrayList sections)
801 this.sections = sections;
804 public Expression Expr {
810 public ArrayList Sections {
816 public override bool Emit (EmitContext ec)
818 throw new Exception ("Unimplemented");
822 public class Lock : Statement {
823 public readonly Expression Expr;
824 public readonly Statement Statement;
827 public Lock (Expression expr, Statement stmt, Location l)
834 public override bool Emit (EmitContext ec)
836 Expression e = Expr.Resolve (ec);
842 if (type.IsValueType){
843 Report.Error (185, loc, "lock statement requires the expression to be " +
844 " a reference type (type is: `" +
845 TypeManager.CSharpName (type) + "'");
849 LocalBuilder temp = ec.GetTemporaryStorage (type);
850 ILGenerator ig = ec.ig;
853 ig.Emit (OpCodes.Dup);
854 ig.Emit (OpCodes.Stloc, temp);
855 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
858 Label end = ig.BeginExceptionBlock ();
859 Label finish = ig.DefineLabel ();
861 // ig.Emit (OpCodes.Leave, finish);
863 ig.MarkLabel (finish);
866 ig.BeginFinallyBlock ();
867 ig.Emit (OpCodes.Ldloc, temp);
868 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
869 ig.EndExceptionBlock ();
875 public class Unchecked : Statement {
876 public readonly Block Block;
878 public Unchecked (Block b)
883 public override bool Emit (EmitContext ec)
885 bool previous_state = ec.CheckState;
888 ec.CheckState = false;
889 val = Block.Emit (ec);
890 ec.CheckState = previous_state;
896 public class Checked : Statement {
897 public readonly Block Block;
899 public Checked (Block b)
904 public override bool Emit (EmitContext ec)
906 bool previous_state = ec.CheckState;
909 ec.CheckState = true;
910 val = Block.Emit (ec);
911 ec.CheckState = previous_state;
918 public readonly string Type;
919 public readonly string Name;
920 public readonly Block Block;
922 public Catch (string type, string name, Block block)
930 public class Try : Statement {
931 public readonly Block Fini, Block;
932 public readonly ArrayList Specific;
933 public readonly Catch General;
936 // specific, general and fini might all be null.
938 public Try (Block block, ArrayList specific, Catch general, Block fini)
940 if (specific == null && general == null){
941 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
945 this.Specific = specific;
946 this.General = general;
950 public override bool Emit (EmitContext ec)
952 ILGenerator ig = ec.ig;
954 Label finish = ig.DefineLabel ();;
956 end = ig.BeginExceptionBlock ();
958 ig.Emit (OpCodes.Leave, finish);
960 foreach (Catch c in Specific){
961 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
964 if (catch_type == null)
967 ig.BeginCatchBlock (catch_type);
970 vi = c.Block.GetVariableInfo (c.Name);
972 Console.WriteLine ("This should not happen! variable does not exist in this block");
973 Environment.Exit (0);
976 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
978 ig.Emit (OpCodes.Pop);
983 if (General != null){
984 ig.BeginCatchBlock (TypeManager.object_type);
985 ig.Emit (OpCodes.Pop);
988 ig.MarkLabel (finish);
990 ig.BeginFinallyBlock ();
994 ig.EndExceptionBlock ();
1000 public class Using : Statement {
1001 object expression_or_block;
1002 Statement Statement;
1005 public Using (object expression_or_block, Statement stmt, Location l)
1007 this.expression_or_block = expression_or_block;
1012 public override bool Emit (EmitContext ec)
1015 // Expressions are simple.
1016 // The problem is with blocks, blocks might contain
1017 // more than one variable, ie like this:
1019 // using (a = new X (), b = new Y ()) stmt;
1021 // which is turned into:
1022 // using (a = new X ()) using (b = new Y ()) stmt;
1024 // The trick is that the block will contain a bunch
1025 // of potential Assign expressions
1028 // We need to signal an error if a variable lacks
1029 // an assignment. (210).
1031 // This is one solution. Another is to set a flag
1032 // when we get the USING token, and have declare_local_variables
1033 // do something *different* that we can better cope with
1035 throw new Exception ("Implement me!");
1039 public class Foreach : Statement {
1040 public readonly string Type;
1041 public readonly LocalVariableReference Variable;
1042 public readonly Expression Expr;
1043 public readonly Statement Statement;
1044 public readonly Location Location;
1046 public Foreach (string type, LocalVariableReference var, Expression expr,
1047 Statement stmt, Location l)
1056 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1061 if (!(m is MethodInfo))
1064 if (m.Name != "GetEnumerator")
1067 MethodInfo mi = (MethodInfo) m;
1069 if (mi.ReturnType != TypeManager.ienumerator_type)
1072 Type [] args = TypeManager.GetArgumentTypes (mi);
1076 if (args.Length == 0)
1083 // This filter is used to find the GetEnumerator method
1084 // on which IEnumerator operates
1086 static MemberFilter FilterEnumerator;
1090 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1093 void error1579 (Type t)
1095 Report.Error (1579, Location,
1096 "foreach statement cannot operate on variables of type `" +
1097 t.FullName + "' because that class does not provide a " +
1098 " GetEnumerator method or it is inaccessible");
1101 MethodInfo ProbeCollectionType (Type t)
1105 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1106 BindingFlags.Public,
1107 FilterEnumerator, null);
1114 if (mi.Length == 0){
1119 return (MethodInfo) mi [0];
1122 public override bool Emit (EmitContext ec)
1124 ILGenerator ig = ec.ig;
1125 Expression e = Expr;
1126 MethodInfo get_enum;
1127 LocalBuilder enumerator, disposable;
1134 var_type = ec.TypeContainer.LookupType (Type, false);
1135 if (var_type == null)
1139 // We need an instance variable. Not sure this is the best
1140 // way of doing this.
1142 // FIXME: When we implement propertyaccess, will those turn
1143 // out to return values in ExprClass? I think they should.
1145 if (!(e.ExprClass == ExprClass.Variable || e.ExprClass == ExprClass.Value)){
1150 if ((get_enum = ProbeCollectionType (e.Type)) == null)
1153 Expression empty = new EmptyExpression ();
1156 conv = Expression.ConvertExplicit (ec, empty, var_type, Location);
1160 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1161 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1164 // Instantiate the enumerator
1166 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1167 Label end_try = ig.DefineLabel ();
1168 bool old_inloop = ec.InLoop;
1169 ec.LoopBegin = ig.DefineLabel ();
1170 ec.LoopEnd = ig.DefineLabel ();
1174 // FIXME: This code does not work for cases like:
1175 // foreach (int a in ValueTypeVariable){
1178 // The code should emit an ldarga instruction
1179 // for the ValueTypeVariable rather than a ldarg
1181 if (e.Type.IsValueType){
1182 ig.Emit (OpCodes.Call, get_enum);
1185 ig.Emit (OpCodes.Callvirt, get_enum);
1187 ig.Emit (OpCodes.Stloc, enumerator);
1190 // Protect the code in a try/finalize block, so that
1191 // if the beast implement IDisposable, we get rid of it
1193 Label l = ig.BeginExceptionBlock ();
1194 ig.MarkLabel (ec.LoopBegin);
1195 ig.Emit (OpCodes.Ldloc, enumerator);
1196 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1197 ig.Emit (OpCodes.Brfalse, end_try);
1198 ig.Emit (OpCodes.Ldloc, enumerator);
1199 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1201 Variable.Store (ec);
1202 Statement.Emit (ec);
1203 ig.Emit (OpCodes.Br, ec.LoopBegin);
1204 ig.MarkLabel (end_try);
1206 // The runtime provides this for us.
1207 // ig.Emit (OpCodes.Leave, end);
1210 // Now the finally block
1212 Label end_finally = ig.DefineLabel ();
1214 ig.BeginFinallyBlock ();
1215 ig.Emit (OpCodes.Ldloc, enumerator);
1216 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1217 ig.Emit (OpCodes.Stloc, disposable);
1218 ig.Emit (OpCodes.Ldloc, disposable);
1219 ig.Emit (OpCodes.Brfalse, end_finally);
1220 ig.Emit (OpCodes.Ldloc, disposable);
1221 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1222 ig.MarkLabel (end_finally);
1224 // The runtime generates this anyways.
1225 // ig.Emit (OpCodes.Endfinally);
1227 ig.EndExceptionBlock ();
1229 ig.MarkLabel (ec.LoopEnd);
1231 ec.LoopBegin = old_begin;
1232 ec.LoopEnd = old_end;
1233 ec.InLoop = old_inloop;