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);
26 public static bool EmitBoolExpression (EmitContext ec, Expression e)
33 if (e.Type != TypeManager.bool_type)
34 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
39 31, "Can not convert the expression to a boolean");
50 public class EmptyStatement : Statement {
51 public override bool Emit (EmitContext ec)
57 public class If : Statement {
58 public readonly Expression Expr;
59 public readonly Statement TrueStatement;
60 public readonly Statement FalseStatement;
62 public If (Expression expr, Statement trueStatement)
65 TrueStatement = trueStatement;
68 public If (Expression expr,
69 Statement trueStatement,
70 Statement falseStatement)
73 TrueStatement = trueStatement;
74 FalseStatement = falseStatement;
77 public override bool Emit (EmitContext ec)
79 ILGenerator ig = ec.ig;
80 Label false_target = ig.DefineLabel ();
84 if (!EmitBoolExpression (ec, Expr))
87 ig.Emit (OpCodes.Brfalse, false_target);
88 is_ret = TrueStatement.Emit (ec);
90 if (FalseStatement != null){
91 bool branch_emitted = false;
93 end = ig.DefineLabel ();
95 ig.Emit (OpCodes.Br, end);
96 branch_emitted = true;
99 ig.MarkLabel (false_target);
100 is_ret = FalseStatement.Emit (ec);
105 ig.MarkLabel (false_target);
111 public class Do : Statement {
112 public readonly Expression Expr;
113 public readonly Statement EmbeddedStatement;
115 public Do (Statement statement, Expression boolExpr)
118 EmbeddedStatement = statement;
121 public override bool Emit (EmitContext ec)
123 ILGenerator ig = ec.ig;
124 Label loop = ig.DefineLabel ();
125 Label old_begin = ec.LoopBegin;
126 Label old_end = ec.LoopEnd;
127 bool old_inloop = ec.InLoop;
129 ec.LoopBegin = ig.DefineLabel ();
130 ec.LoopEnd = ig.DefineLabel ();
134 EmbeddedStatement.Emit (ec);
135 ig.MarkLabel (ec.LoopBegin);
136 EmitBoolExpression (ec, Expr);
137 ig.Emit (OpCodes.Brtrue, loop);
138 ig.MarkLabel (ec.LoopEnd);
140 ec.LoopBegin = old_begin;
141 ec.LoopEnd = old_end;
142 ec.InLoop = old_inloop;
148 public class While : Statement {
149 public readonly Expression Expr;
150 public readonly Statement Statement;
152 public While (Expression boolExpr, Statement statement)
155 Statement = statement;
158 public override bool Emit (EmitContext ec)
160 ILGenerator ig = ec.ig;
161 Label old_begin = ec.LoopBegin;
162 Label old_end = ec.LoopEnd;
163 bool old_inloop = ec.InLoop;
165 ec.LoopBegin = ig.DefineLabel ();
166 ec.LoopEnd = ig.DefineLabel ();
169 ig.MarkLabel (ec.LoopBegin);
170 EmitBoolExpression (ec, Expr);
171 ig.Emit (OpCodes.Brfalse, ec.LoopEnd);
173 ig.Emit (OpCodes.Br, ec.LoopBegin);
174 ig.MarkLabel (ec.LoopEnd);
176 ec.LoopBegin = old_begin;
177 ec.LoopEnd = old_end;
178 ec.InLoop = old_inloop;
184 public class For : Statement {
185 public readonly Statement InitStatement;
186 public readonly Expression Test;
187 public readonly Statement Increment;
188 public readonly Statement Statement;
190 public For (Statement initStatement,
195 InitStatement = initStatement;
197 Increment = increment;
198 Statement = statement;
201 public override bool Emit (EmitContext ec)
203 ILGenerator ig = ec.ig;
204 Label old_begin = ec.LoopBegin;
205 Label old_end = ec.LoopEnd;
206 bool old_inloop = ec.InLoop;
207 Label loop = ig.DefineLabel ();
209 if (! (InitStatement is EmptyStatement))
210 InitStatement.Emit (ec);
212 ec.LoopBegin = ig.DefineLabel ();
213 ec.LoopEnd = ig.DefineLabel ();
217 EmitBoolExpression (ec, Test);
218 ig.Emit (OpCodes.Brfalse, ec.LoopEnd);
220 ig.MarkLabel (ec.LoopBegin);
221 if (!(Increment is EmptyStatement))
223 ig.Emit (OpCodes.Br, loop);
224 ig.MarkLabel (ec.LoopEnd);
226 ec.LoopBegin = old_begin;
227 ec.LoopEnd = old_end;
228 ec.InLoop = old_inloop;
233 public class StatementExpression : Statement {
234 public readonly ExpressionStatement Expr;
236 public StatementExpression (ExpressionStatement expr)
241 public override bool Emit (EmitContext ec)
243 ILGenerator ig = ec.ig;
246 ne = Expr.Resolve (ec);
248 if (ne is ExpressionStatement)
249 ((ExpressionStatement) ne).EmitStatement (ec);
252 ig.Emit (OpCodes.Pop);
260 public class Return : Statement {
261 public Expression Expr;
262 public readonly Location loc;
264 public Return (Expression expr, Location l)
270 public override bool Emit (EmitContext ec)
272 if (ec.ReturnType == null){
274 Report.Error (127, loc, "Return with a value not allowed here");
279 Report.Error (126, loc, "An object of type `" +
280 TypeManager.CSharpName (ec.ReturnType) + "' is " +
281 "expected for the return statement");
285 Expr = Expr.Resolve (ec);
289 if (Expr.Type != ec.ReturnType)
290 Expr = Expression.ConvertImplicitRequired (
291 ec, Expr, ec.ReturnType, loc);
299 ec.ig.Emit (OpCodes.Ret);
305 public class Goto : Statement {
309 public Goto (string label, Location l)
315 public string Target {
321 public override bool Emit (EmitContext ec)
323 Console.WriteLine ("Attempting to goto to: " + target);
329 public class Throw : Statement {
330 public readonly Expression Expr;
332 public Throw (Expression expr)
337 public override bool Emit (EmitContext ec)
339 Expression e = Expr.Resolve (ec);
345 ec.ig.Emit (OpCodes.Throw);
351 public class Break : Statement {
354 public Break (Location l)
359 public override bool Emit (EmitContext ec)
361 ILGenerator ig = ec.ig;
364 Report.Error (139, loc, "No enclosing loop to continue to");
368 ig.Emit (OpCodes.Br, ec.LoopEnd);
373 public class Continue : Statement {
376 public Continue (Location l)
381 public override bool Emit (EmitContext ec)
383 Label begin = ec.LoopBegin;
386 Report.Error (139, loc, "No enclosing loop to continue to");
390 ec.ig.Emit (OpCodes.Br, begin);
395 public class VariableInfo {
396 public readonly string Type;
397 public LocalBuilder LocalBuilder;
398 public Type VariableType;
399 public readonly Location Location;
403 public bool Assigned;
405 public VariableInfo (string type, Location l)
416 throw new Exception ("Unassigned idx for variable");
429 // Used for Label management
432 public class Block : Statement {
433 public readonly Block Parent;
434 public readonly bool Implicit;
435 public readonly string Label;
438 // The statements in this block
440 StatementCollection statements;
443 // An array of Blocks. We keep track of children just
444 // to generate the local variable declarations.
446 // Statements and child statements are handled through the
452 // Labels. (label, block) pairs.
457 // Keeps track of (name, type) pairs
462 // Maps variable names to ILGenerator.LocalBuilders
464 Hashtable local_builders;
468 public Block (Block parent)
471 parent.AddChild (this);
473 this.Parent = parent;
474 this.Implicit = false;
477 public Block (Block parent, bool implicit_block)
480 parent.AddChild (this);
482 this.Parent = parent;
483 this.Implicit = true;
486 public Block (Block parent, string labeled)
489 parent.AddChild (this);
491 this.Parent = parent;
492 this.Implicit = true;
496 public void AddChild (Block b)
498 if (children == null)
499 children = new ArrayList ();
505 // Adds a label to the current block.
509 // false if the name already exists in this block. true
513 public bool AddLabel (string name, Block block)
516 labels = new Hashtable ();
517 if (labels.Contains (name))
520 labels.Add (name, block);
524 public bool AddVariable (string type, string name, Location l)
526 if (variables == null)
527 variables = new Hashtable ();
529 if (GetVariableType (name) != null)
532 VariableInfo vi = new VariableInfo (type, l);
534 variables.Add (name, vi);
538 public Hashtable Variables {
544 public VariableInfo GetVariableInfo (string name)
546 if (variables != null) {
548 temp = variables [name];
551 return (VariableInfo) temp;
555 return Parent.GetVariableInfo (name);
561 public string GetVariableType (string name)
563 VariableInfo vi = GetVariableInfo (name);
572 // True if the variable named @name has been defined
575 public bool IsVariableDefined (string name)
577 return GetVariableType (name) != null;
581 // Use to fetch the statement associated with this label
583 public Statement this [string name] {
585 return (Statement) labels [name];
590 // A list of labels that were not used within this block
592 public string [] GetUnreferenced ()
594 // FIXME: Implement me
598 public StatementCollection Statements {
600 if (statements == null)
601 statements = new StatementCollection ();
607 public void AddStatement (Statement s)
609 if (statements == null)
610 statements = new StatementCollection ();
628 // Creates a compiler-internal identifier, this is
629 // used to create temporary variables that should not
630 // be seen by the application
632 int internal_id_serial;
633 public string MakeInternalID () {
634 string ret = internal_id_serial.ToString ();
636 internal_id_serial++;
641 // Emits the variable declarations and labels.
644 // tc: is our typecontainer (to resolve type references)
645 // ig: is the code generator:
646 // toplevel: the toplevel block. This is used for checking
647 // that no two labels with the same name are used.
649 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
652 // Process this block variables
654 if (variables != null){
655 local_builders = new Hashtable ();
657 foreach (DictionaryEntry de in variables){
658 string name = (string) de.Key;
659 VariableInfo vi = (VariableInfo) de.Value;
662 t = tc.LookupType (vi.Type, false);
667 vi.LocalBuilder = ig.DeclareLocal (t);
673 // Now, handle the children
675 if (children != null){
676 foreach (Block b in children)
677 b.EmitMeta (tc, ig, toplevel, count);
681 public void UsageWarning ()
685 if (variables != null){
686 foreach (DictionaryEntry de in variables){
687 VariableInfo vi = (VariableInfo) de.Value;
692 name = (string) de.Key;
696 219, vi.Location, "The variable `" + name +
697 "' is assigned but its value is never used");
700 168, vi.Location, "The variable `" +
702 "' is declared but never used");
707 if (children != null)
708 foreach (Block b in children)
712 public override bool Emit (EmitContext ec)
715 Block prev_block = ec.CurrentBlock;
717 ec.CurrentBlock = this;
718 foreach (Statement s in Statements)
719 is_ret = s.Emit (ec);
721 ec.CurrentBlock = prev_block;
726 public class SwitchLabel {
730 // if expr == null, then it is the default case.
732 public SwitchLabel (Expression expr)
737 public Expression Label {
744 public class SwitchSection {
745 // An array of SwitchLabels.
749 public SwitchSection (ArrayList labels, Block block)
751 this.labels = labels;
761 public ArrayList Labels {
768 public class Switch : Statement {
772 public Switch (Expression expr, ArrayList sections)
775 this.sections = sections;
778 public Expression Expr {
784 public ArrayList Sections {
790 public override bool Emit (EmitContext ec)
792 throw new Exception ("Unimplemented");
796 public class Lock : Statement {
797 public readonly Expression Expr;
798 public readonly Statement Statement;
801 public Lock (Expression expr, Statement stmt, Location l)
808 public override bool Emit (EmitContext ec)
810 Expression e = Expr.Resolve (ec);
816 if (type.IsValueType){
817 Report.Error (185, loc, "lock statement requires the expression to be " +
818 " a reference type (type is: `" +
819 TypeManager.CSharpName (type) + "'");
823 LocalBuilder temp = ec.GetTemporaryStorage (type);
824 ILGenerator ig = ec.ig;
827 ig.Emit (OpCodes.Dup);
828 ig.Emit (OpCodes.Stloc, temp);
829 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
832 Label end = ig.BeginExceptionBlock ();
833 Label finish = ig.DefineLabel ();
835 // ig.Emit (OpCodes.Leave, finish);
837 ig.MarkLabel (finish);
840 ig.BeginFinallyBlock ();
841 ig.Emit (OpCodes.Ldloc, temp);
842 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
843 ig.EndExceptionBlock ();
849 public class Unchecked : Statement {
850 public readonly Block Block;
852 public Unchecked (Block b)
857 public override bool Emit (EmitContext ec)
859 bool previous_state = ec.CheckState;
862 ec.CheckState = false;
863 val = Block.Emit (ec);
864 ec.CheckState = previous_state;
870 public class Checked : Statement {
871 public readonly Block Block;
873 public Checked (Block b)
878 public override bool Emit (EmitContext ec)
880 bool previous_state = ec.CheckState;
883 ec.CheckState = true;
884 val = Block.Emit (ec);
885 ec.CheckState = previous_state;
892 public readonly string Type;
893 public readonly string Name;
894 public readonly Block Block;
896 public Catch (string type, string name, Block block)
904 public class Try : Statement {
905 public readonly Block Fini, Block;
906 public readonly ArrayList Specific;
907 public readonly Catch General;
910 // specific, general and fini might all be null.
912 public Try (Block block, ArrayList specific, Catch general, Block fini)
914 if (specific == null && general == null){
915 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
919 this.Specific = specific;
920 this.General = general;
924 public override bool Emit (EmitContext ec)
926 ILGenerator ig = ec.ig;
928 Label finish = ig.DefineLabel ();;
930 end = ig.BeginExceptionBlock ();
932 ig.Emit (OpCodes.Leave, finish);
934 foreach (Catch c in Specific){
935 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
938 if (catch_type == null)
941 ig.BeginCatchBlock (catch_type);
944 vi = c.Block.GetVariableInfo (c.Name);
946 Console.WriteLine ("This should not happen! variable does not exist in this block");
947 Environment.Exit (0);
950 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
952 ig.Emit (OpCodes.Pop);
957 if (General != null){
958 ig.BeginCatchBlock (TypeManager.object_type);
959 ig.Emit (OpCodes.Pop);
962 ig.MarkLabel (finish);
964 ig.BeginFinallyBlock ();
968 ig.EndExceptionBlock ();
974 public class Using : Statement {
975 object expression_or_block;
979 public Using (object expression_or_block, Statement stmt, Location l)
981 this.expression_or_block = expression_or_block;
986 public override bool Emit (EmitContext ec)
989 // Expressions are simple.
990 // The problem is with blocks, blocks might contain
991 // more than one variable, ie like this:
993 // using (a = new X (), b = new Y ()) stmt;
995 // which is turned into:
996 // using (a = new X ()) using (b = new Y ()) stmt;
998 // The trick is that the block will contain a bunch
999 // of potential Assign expressions
1002 // We need to signal an error if a variable lacks
1003 // an assignment. (210).
1005 // This is one solution. Another is to set a flag
1006 // when we get the USING token, and have declare_local_variables
1007 // do something *different* that we can better cope with
1009 throw new Exception ("Implement me!");
1013 public class Foreach : Statement {
1014 public readonly string Type;
1015 public readonly LocalVariableReference Variable;
1016 public readonly Expression Expr;
1017 public readonly Statement Statement;
1018 public readonly Location Location;
1020 public Foreach (string type, LocalVariableReference var, Expression expr,
1021 Statement stmt, Location l)
1030 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1035 if (!(m is MethodInfo))
1038 if (m.Name != "GetEnumerator")
1041 MethodInfo mi = (MethodInfo) m;
1043 if (mi.ReturnType != TypeManager.ienumerator_type)
1046 Type [] args = TypeManager.GetArgumentTypes (mi);
1050 if (args.Length == 0)
1057 // This filter is used to find the GetEnumerator method
1058 // on which IEnumerator operates
1060 static MemberFilter FilterEnumerator;
1064 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1067 void error1579 (Type t)
1069 Report.Error (1579, Location,
1070 "foreach statement cannot operate on variables of type `" +
1071 t.FullName + "' because that class does not provide a " +
1072 " GetEnumerator method or it is inaccessible");
1075 MethodInfo ProbeCollectionType (Type t)
1079 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1080 BindingFlags.Public,
1081 FilterEnumerator, null);
1088 if (mi.Length == 0){
1093 return (MethodInfo) mi [0];
1096 public override bool Emit (EmitContext ec)
1098 ILGenerator ig = ec.ig;
1099 Expression e = Expr;
1100 MethodInfo get_enum;
1101 LocalBuilder enumerator, disposable;
1108 var_type = ec.TypeContainer.LookupType (Type, false);
1109 if (var_type == null)
1113 // We need an instance variable. Not sure this is the best
1114 // way of doing this.
1116 // FIXME: When we implement propertyaccess, will those turn
1117 // out to return values in ExprClass? I think they should.
1119 if (!(e.ExprClass == ExprClass.Variable || e.ExprClass == ExprClass.Value)){
1124 if ((get_enum = ProbeCollectionType (e.Type)) == null)
1127 Expression empty = new EmptyExpression ();
1130 conv = Expression.ConvertExplicit (ec, empty, var_type, Location);
1134 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1135 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1138 // Instantiate the enumerator
1140 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1141 Label end_try = ig.DefineLabel ();
1142 bool old_inloop = ec.InLoop;
1143 ec.LoopBegin = ig.DefineLabel ();
1144 ec.LoopEnd = ig.DefineLabel ();
1148 // FIXME: This code does not work for cases like:
1149 // foreach (int a in ValueTypeVariable){
1152 // The code should emit an ldarga instruction
1153 // for the ValueTypeVariable rather than a ldarg
1155 if (e.Type.IsValueType){
1156 ig.Emit (OpCodes.Call, get_enum);
1159 ig.Emit (OpCodes.Callvirt, get_enum);
1161 ig.Emit (OpCodes.Stloc, enumerator);
1164 // Protect the code in a try/finalize block, so that
1165 // if the beast implement IDisposable, we get rid of it
1167 Label l = ig.BeginExceptionBlock ();
1168 ig.MarkLabel (ec.LoopBegin);
1169 ig.Emit (OpCodes.Ldloc, enumerator);
1170 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1171 ig.Emit (OpCodes.Brfalse, end_try);
1172 ig.Emit (OpCodes.Ldloc, enumerator);
1173 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1175 Variable.Store (ec);
1176 Statement.Emit (ec);
1177 ig.Emit (OpCodes.Br, ec.LoopBegin);
1178 ig.MarkLabel (end_try);
1180 // The runtime provides this for us.
1181 // ig.Emit (OpCodes.Leave, end);
1184 // Now the finally block
1186 Label end_finally = ig.DefineLabel ();
1188 ig.BeginFinallyBlock ();
1189 ig.Emit (OpCodes.Ldloc, enumerator);
1190 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1191 ig.Emit (OpCodes.Stloc, disposable);
1192 ig.Emit (OpCodes.Ldloc, disposable);
1193 ig.Emit (OpCodes.Brfalse, end_finally);
1194 ig.Emit (OpCodes.Ldloc, disposable);
1195 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1196 ig.MarkLabel (end_finally);
1198 // The runtime generates this anyways.
1199 // ig.Emit (OpCodes.Endfinally);
1201 ig.EndExceptionBlock ();
1203 ig.MarkLabel (ec.LoopEnd);
1205 ec.LoopBegin = old_begin;
1206 ec.LoopEnd = old_end;
1207 ec.InLoop = old_inloop;