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 != null)
237 if (! (InitStatement is EmptyStatement))
238 InitStatement.Emit (ec);
240 ec.LoopBegin = ig.DefineLabel ();
241 ec.LoopEnd = ig.DefineLabel ();
245 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
247 ig.MarkLabel (ec.LoopBegin);
248 if (!(Increment is EmptyStatement))
250 ig.Emit (OpCodes.Br, loop);
251 ig.MarkLabel (ec.LoopEnd);
253 ec.LoopBegin = old_begin;
254 ec.LoopEnd = old_end;
255 ec.InLoop = old_inloop;
260 public class StatementExpression : Statement {
261 public readonly ExpressionStatement Expr;
263 public StatementExpression (ExpressionStatement expr)
268 public override bool Emit (EmitContext ec)
270 ILGenerator ig = ec.ig;
273 ne = Expr.Resolve (ec);
275 if (ne is ExpressionStatement)
276 ((ExpressionStatement) ne).EmitStatement (ec);
279 ig.Emit (OpCodes.Pop);
287 public class Return : Statement {
288 public Expression Expr;
289 public readonly Location loc;
291 public Return (Expression expr, Location l)
297 public override bool Emit (EmitContext ec)
299 if (ec.ReturnType == null){
301 Report.Error (127, loc, "Return with a value not allowed here");
306 Report.Error (126, loc, "An object of type `" +
307 TypeManager.CSharpName (ec.ReturnType) + "' is " +
308 "expected for the return statement");
312 Expr = Expr.Resolve (ec);
316 if (Expr.Type != ec.ReturnType)
317 Expr = Expression.ConvertImplicitRequired (
318 ec, Expr, ec.ReturnType, loc);
326 ec.ig.Emit (OpCodes.Ret);
332 public class Goto : Statement {
336 public Goto (string label, Location l)
342 public string Target {
348 public override bool Emit (EmitContext ec)
350 Console.WriteLine ("Attempting to goto to: " + target);
356 public class Throw : Statement {
357 public readonly Expression Expr;
359 public Throw (Expression expr)
364 public override bool Emit (EmitContext ec)
366 Expression e = Expr.Resolve (ec);
372 ec.ig.Emit (OpCodes.Throw);
378 public class Break : Statement {
381 public Break (Location l)
386 public override bool Emit (EmitContext ec)
388 ILGenerator ig = ec.ig;
391 Report.Error (139, loc, "No enclosing loop to continue to");
395 ig.Emit (OpCodes.Br, ec.LoopEnd);
400 public class Continue : Statement {
403 public Continue (Location l)
408 public override bool Emit (EmitContext ec)
410 Label begin = ec.LoopBegin;
413 Report.Error (139, loc, "No enclosing loop to continue to");
417 ec.ig.Emit (OpCodes.Br, begin);
422 public class VariableInfo {
423 public readonly string Type;
424 public LocalBuilder LocalBuilder;
425 public Type VariableType;
426 public readonly Location Location;
430 public bool Assigned;
432 public VariableInfo (string type, Location l)
443 throw new Exception ("Unassigned idx for variable");
456 // Used for Label management
459 public class Block : Statement {
460 public readonly Block Parent;
461 public readonly bool Implicit;
462 public readonly string Label;
465 // The statements in this block
467 StatementCollection statements;
470 // An array of Blocks. We keep track of children just
471 // to generate the local variable declarations.
473 // Statements and child statements are handled through the
479 // Labels. (label, block) pairs.
484 // Keeps track of (name, type) pairs
489 // Maps variable names to ILGenerator.LocalBuilders
491 Hashtable local_builders;
495 public Block (Block parent)
498 parent.AddChild (this);
500 this.Parent = parent;
501 this.Implicit = false;
504 public Block (Block parent, bool implicit_block)
507 parent.AddChild (this);
509 this.Parent = parent;
510 this.Implicit = true;
513 public Block (Block parent, string labeled)
516 parent.AddChild (this);
518 this.Parent = parent;
519 this.Implicit = true;
523 public void AddChild (Block b)
525 if (children == null)
526 children = new ArrayList ();
532 // Adds a label to the current block.
536 // false if the name already exists in this block. true
540 public bool AddLabel (string name, Block block)
543 labels = new Hashtable ();
544 if (labels.Contains (name))
547 labels.Add (name, block);
551 public bool AddVariable (string type, string name, Location l)
553 if (variables == null)
554 variables = new Hashtable ();
556 if (GetVariableType (name) != null)
559 VariableInfo vi = new VariableInfo (type, l);
561 variables.Add (name, vi);
565 public Hashtable Variables {
571 public VariableInfo GetVariableInfo (string name)
573 if (variables != null) {
575 temp = variables [name];
578 return (VariableInfo) temp;
582 return Parent.GetVariableInfo (name);
588 public string GetVariableType (string name)
590 VariableInfo vi = GetVariableInfo (name);
599 // True if the variable named @name has been defined
602 public bool IsVariableDefined (string name)
604 return GetVariableType (name) != null;
608 // Use to fetch the statement associated with this label
610 public Statement this [string name] {
612 return (Statement) labels [name];
617 // A list of labels that were not used within this block
619 public string [] GetUnreferenced ()
621 // FIXME: Implement me
625 public StatementCollection Statements {
627 if (statements == null)
628 statements = new StatementCollection ();
634 public void AddStatement (Statement s)
636 if (statements == null)
637 statements = new StatementCollection ();
655 // Creates a compiler-internal identifier, this is
656 // used to create temporary variables that should not
657 // be seen by the application
659 int internal_id_serial;
660 public string MakeInternalID () {
661 string ret = internal_id_serial.ToString ();
663 internal_id_serial++;
668 // Emits the variable declarations and labels.
671 // tc: is our typecontainer (to resolve type references)
672 // ig: is the code generator:
673 // toplevel: the toplevel block. This is used for checking
674 // that no two labels with the same name are used.
676 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
679 // Process this block variables
681 if (variables != null){
682 local_builders = new Hashtable ();
684 foreach (DictionaryEntry de in variables){
685 string name = (string) de.Key;
686 VariableInfo vi = (VariableInfo) de.Value;
689 t = tc.LookupType (vi.Type, false);
694 vi.LocalBuilder = ig.DeclareLocal (t);
700 // Now, handle the children
702 if (children != null){
703 foreach (Block b in children)
704 b.EmitMeta (tc, ig, toplevel, count);
708 public void UsageWarning ()
712 if (variables != null){
713 foreach (DictionaryEntry de in variables){
714 VariableInfo vi = (VariableInfo) de.Value;
719 name = (string) de.Key;
723 219, vi.Location, "The variable `" + name +
724 "' is assigned but its value is never used");
727 168, vi.Location, "The variable `" +
729 "' is declared but never used");
734 if (children != null)
735 foreach (Block b in children)
739 public override bool Emit (EmitContext ec)
742 Block prev_block = ec.CurrentBlock;
744 ec.CurrentBlock = this;
745 foreach (Statement s in Statements)
746 is_ret = s.Emit (ec);
748 ec.CurrentBlock = prev_block;
753 public class SwitchLabel {
757 // if expr == null, then it is the default case.
759 public SwitchLabel (Expression expr)
764 public Expression Label {
771 public class SwitchSection {
772 // An array of SwitchLabels.
776 public SwitchSection (ArrayList labels, Block block)
778 this.labels = labels;
788 public ArrayList Labels {
795 public class Switch : Statement {
799 public Switch (Expression expr, ArrayList sections)
802 this.sections = sections;
805 public Expression Expr {
811 public ArrayList Sections {
817 public override bool Emit (EmitContext ec)
819 throw new Exception ("Unimplemented");
823 public class Lock : Statement {
824 public readonly Expression Expr;
825 public readonly Statement Statement;
828 public Lock (Expression expr, Statement stmt, Location l)
835 public override bool Emit (EmitContext ec)
837 Expression e = Expr.Resolve (ec);
843 if (type.IsValueType){
844 Report.Error (185, loc, "lock statement requires the expression to be " +
845 " a reference type (type is: `" +
846 TypeManager.CSharpName (type) + "'");
850 LocalBuilder temp = ec.GetTemporaryStorage (type);
851 ILGenerator ig = ec.ig;
854 ig.Emit (OpCodes.Dup);
855 ig.Emit (OpCodes.Stloc, temp);
856 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
859 Label end = ig.BeginExceptionBlock ();
860 Label finish = ig.DefineLabel ();
862 // ig.Emit (OpCodes.Leave, finish);
864 ig.MarkLabel (finish);
867 ig.BeginFinallyBlock ();
868 ig.Emit (OpCodes.Ldloc, temp);
869 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
870 ig.EndExceptionBlock ();
876 public class Unchecked : Statement {
877 public readonly Block Block;
879 public Unchecked (Block b)
884 public override bool Emit (EmitContext ec)
886 bool previous_state = ec.CheckState;
889 ec.CheckState = false;
890 val = Block.Emit (ec);
891 ec.CheckState = previous_state;
897 public class Checked : Statement {
898 public readonly Block Block;
900 public Checked (Block b)
905 public override bool Emit (EmitContext ec)
907 bool previous_state = ec.CheckState;
910 ec.CheckState = true;
911 val = Block.Emit (ec);
912 ec.CheckState = previous_state;
919 public readonly string Type;
920 public readonly string Name;
921 public readonly Block Block;
923 public Catch (string type, string name, Block block)
931 public class Try : Statement {
932 public readonly Block Fini, Block;
933 public readonly ArrayList Specific;
934 public readonly Catch General;
937 // specific, general and fini might all be null.
939 public Try (Block block, ArrayList specific, Catch general, Block fini)
941 if (specific == null && general == null){
942 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
946 this.Specific = specific;
947 this.General = general;
951 public override bool Emit (EmitContext ec)
953 ILGenerator ig = ec.ig;
955 Label finish = ig.DefineLabel ();;
957 end = ig.BeginExceptionBlock ();
959 ig.Emit (OpCodes.Leave, finish);
961 foreach (Catch c in Specific){
962 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
965 if (catch_type == null)
968 ig.BeginCatchBlock (catch_type);
971 vi = c.Block.GetVariableInfo (c.Name);
973 Console.WriteLine ("This should not happen! variable does not exist in this block");
974 Environment.Exit (0);
977 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
979 ig.Emit (OpCodes.Pop);
984 if (General != null){
985 ig.BeginCatchBlock (TypeManager.object_type);
986 ig.Emit (OpCodes.Pop);
989 ig.MarkLabel (finish);
991 ig.BeginFinallyBlock ();
995 ig.EndExceptionBlock ();
1001 public class Using : Statement {
1002 object expression_or_block;
1003 Statement Statement;
1006 public Using (object expression_or_block, Statement stmt, Location l)
1008 this.expression_or_block = expression_or_block;
1013 public override bool Emit (EmitContext ec)
1016 // Expressions are simple.
1017 // The problem is with blocks, blocks might contain
1018 // more than one variable, ie like this:
1020 // using (a = new X (), b = new Y ()) stmt;
1022 // which is turned into:
1023 // using (a = new X ()) using (b = new Y ()) stmt;
1025 // The trick is that the block will contain a bunch
1026 // of potential Assign expressions
1029 // We need to signal an error if a variable lacks
1030 // an assignment. (210).
1032 // This is one solution. Another is to set a flag
1033 // when we get the USING token, and have declare_local_variables
1034 // do something *different* that we can better cope with
1036 throw new Exception ("Implement me!");
1040 public class Foreach : Statement {
1041 public readonly string Type;
1042 public readonly LocalVariableReference Variable;
1043 public readonly Expression Expr;
1044 public readonly Statement Statement;
1045 public readonly Location Location;
1047 public Foreach (string type, LocalVariableReference var, Expression expr,
1048 Statement stmt, Location l)
1057 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1062 if (!(m is MethodInfo))
1065 if (m.Name != "GetEnumerator")
1068 MethodInfo mi = (MethodInfo) m;
1070 if (mi.ReturnType != TypeManager.ienumerator_type)
1073 Type [] args = TypeManager.GetArgumentTypes (mi);
1077 if (args.Length == 0)
1084 // This filter is used to find the GetEnumerator method
1085 // on which IEnumerator operates
1087 static MemberFilter FilterEnumerator;
1091 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1094 void error1579 (Type t)
1096 Report.Error (1579, Location,
1097 "foreach statement cannot operate on variables of type `" +
1098 t.FullName + "' because that class does not provide a " +
1099 " GetEnumerator method or it is inaccessible");
1102 MethodInfo ProbeCollectionType (Type t)
1106 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1107 BindingFlags.Public,
1108 FilterEnumerator, null);
1115 if (mi.Length == 0){
1120 return (MethodInfo) mi [0];
1123 public override bool Emit (EmitContext ec)
1125 ILGenerator ig = ec.ig;
1126 Expression e = Expr;
1127 MethodInfo get_enum;
1128 LocalBuilder enumerator, disposable;
1135 var_type = ec.TypeContainer.LookupType (Type, false);
1136 if (var_type == null)
1140 // We need an instance variable. Not sure this is the best
1141 // way of doing this.
1143 // FIXME: When we implement propertyaccess, will those turn
1144 // out to return values in ExprClass? I think they should.
1146 if (!(e.ExprClass == ExprClass.Variable || e.ExprClass == ExprClass.Value)){
1151 if ((get_enum = ProbeCollectionType (e.Type)) == null)
1154 Expression empty = new EmptyExpression ();
1157 conv = Expression.ConvertExplicit (ec, empty, var_type, Location);
1161 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1162 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1165 // Instantiate the enumerator
1167 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1168 Label end_try = ig.DefineLabel ();
1169 bool old_inloop = ec.InLoop;
1170 ec.LoopBegin = ig.DefineLabel ();
1171 ec.LoopEnd = ig.DefineLabel ();
1175 // FIXME: This code does not work for cases like:
1176 // foreach (int a in ValueTypeVariable){
1179 // The code should emit an ldarga instruction
1180 // for the ValueTypeVariable rather than a ldarg
1182 if (e.Type.IsValueType){
1183 ig.Emit (OpCodes.Call, get_enum);
1186 ig.Emit (OpCodes.Callvirt, get_enum);
1188 ig.Emit (OpCodes.Stloc, enumerator);
1191 // Protect the code in a try/finalize block, so that
1192 // if the beast implement IDisposable, we get rid of it
1194 Label l = ig.BeginExceptionBlock ();
1195 ig.MarkLabel (ec.LoopBegin);
1196 ig.Emit (OpCodes.Ldloc, enumerator);
1197 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1198 ig.Emit (OpCodes.Brfalse, end_try);
1199 ig.Emit (OpCodes.Ldloc, enumerator);
1200 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1201 Variable.EmitAssign (ec, conv);
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;