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 ();
127 EmbeddedStatement.Emit (ec);
128 EmitBoolExpression (ec, Expr);
129 ig.Emit (OpCodes.Brtrue, loop);
135 public class While : Statement {
136 public readonly Expression Expr;
137 public readonly Statement Statement;
139 public While (Expression boolExpr, Statement statement)
142 Statement = statement;
145 public override bool Emit (EmitContext ec)
147 ILGenerator ig = ec.ig;
148 Label while_eval = ig.DefineLabel ();
149 Label exit = ig.DefineLabel ();
151 ig.MarkLabel (while_eval);
152 EmitBoolExpression (ec, Expr);
153 ig.Emit (OpCodes.Brfalse, exit);
155 ig.Emit (OpCodes.Br, while_eval);
162 public class For : Statement {
163 public readonly Statement InitStatement;
164 public readonly Expression Test;
165 public readonly Statement Increment;
166 public readonly Statement Statement;
168 public For (Statement initStatement,
173 InitStatement = initStatement;
175 Increment = increment;
176 Statement = statement;
179 public override bool Emit (EmitContext ec)
181 ILGenerator ig = ec.ig;
182 Label loop = ig.DefineLabel ();
183 Label exit = ig.DefineLabel ();
185 if (! (InitStatement is EmptyStatement))
186 InitStatement.Emit (ec);
189 EmitBoolExpression (ec, Test);
190 ig.Emit (OpCodes.Brfalse, exit);
192 if (!(Increment is EmptyStatement))
194 ig.Emit (OpCodes.Br, loop);
201 public class StatementExpression : Statement {
202 public readonly ExpressionStatement Expr;
204 public StatementExpression (ExpressionStatement expr)
209 public override bool Emit (EmitContext ec)
211 ILGenerator ig = ec.ig;
214 ne = Expr.Resolve (ec);
216 if (ne is ExpressionStatement)
217 ((ExpressionStatement) ne).EmitStatement (ec);
220 ig.Emit (OpCodes.Pop);
228 public class Return : Statement {
229 public Expression Expr;
230 public readonly Location loc;
232 public Return (Expression expr, Location l)
237 public override bool Emit (EmitContext ec)
239 if (ec.ReturnType == null){
241 Report.Error (127, loc, "Return with a value not allowed here");
246 Report.Error (126, loc, "An object of type `" +
247 TypeManager.CSharpName (ec.ReturnType) + "' is " +
248 "expected for the return statement");
252 Expr = Expr.Resolve (ec);
256 if (Expr.Type != ec.ReturnType)
257 Expr = Expression.ConvertImplicitRequired (
258 ec, Expr, ec.ReturnType, loc);
266 ec.ig.Emit (OpCodes.Ret);
272 public class Goto : Statement {
275 public Goto (string label)
280 public string Target {
286 public override bool Emit (EmitContext ec)
288 throw new Exception ("Unimplemented");
292 public class Throw : Statement {
293 public readonly Expression Expr;
295 public Throw (Expression expr)
300 public override bool Emit (EmitContext ec)
302 Expression e = Expr.Resolve (ec);
308 ec.ig.Emit (OpCodes.Throw);
314 public class Break : Statement {
319 public override bool Emit (EmitContext ec)
321 throw new Exception ("Unimplemented");
325 public class Continue : Statement {
330 public override bool Emit (EmitContext ec)
332 throw new Exception ("Unimplemented");
336 public class VariableInfo {
337 public readonly string Type;
338 public LocalBuilder LocalBuilder;
339 public Type VariableType;
340 public readonly Location Location;
344 public bool Assigned;
346 public VariableInfo (string type, Location l)
357 throw new Exception ("Unassigned idx for variable");
370 // Used for Label management
373 public class Block : Statement {
374 public readonly Block Parent;
375 public readonly bool Implicit;
376 public readonly string Label;
379 // The statements in this block
381 StatementCollection statements;
384 // An array of Blocks
389 // Labels. (label, block) pairs.
394 // Keeps track of (name, type) pairs
399 // Maps variable names to ILGenerator.LocalBuilders
401 Hashtable local_builders;
405 public Block (Block parent)
408 parent.AddChild (this);
410 this.Parent = parent;
411 this.Implicit = false;
414 public Block (Block parent, bool implicit_block)
417 parent.AddChild (this);
419 this.Parent = parent;
420 this.Implicit = true;
423 public Block (Block parent, string labeled)
426 parent.AddChild (this);
428 this.Parent = parent;
429 this.Implicit = true;
433 public void AddChild (Block b)
435 if (children == null)
436 children = new ArrayList ();
442 // Adds a label to the current block.
446 // false if the name already exists in this block. true
450 public bool AddLabel (string name, Block block)
453 labels = new Hashtable ();
454 if (labels.Contains (name))
457 labels.Add (name, block);
461 public bool AddVariable (string type, string name, Location l)
463 if (variables == null)
464 variables = new Hashtable ();
466 if (GetVariableType (name) != null)
469 VariableInfo vi = new VariableInfo (type, l);
471 variables.Add (name, vi);
475 public Hashtable Variables {
481 public VariableInfo GetVariableInfo (string name)
483 if (variables != null) {
485 temp = variables [name];
488 return (VariableInfo) temp;
492 return Parent.GetVariableInfo (name);
498 public string GetVariableType (string name)
500 VariableInfo vi = GetVariableInfo (name);
509 // True if the variable named @name has been defined
512 public bool IsVariableDefined (string name)
514 return GetVariableType (name) != null;
518 // Use to fetch the statement associated with this label
520 public Statement this [string name] {
522 return (Statement) labels [name];
527 // A list of labels that were not used within this block
529 public string [] GetUnreferenced ()
531 // FIXME: Implement me
535 public StatementCollection Statements {
537 if (statements == null)
538 statements = new StatementCollection ();
544 public void AddStatement (Statement s)
546 if (statements == null)
547 statements = new StatementCollection ();
565 // Creates a compiler-internal identifier, this is
566 // used to create temporary variables that should not
567 // be seen by the application
569 int internal_id_serial;
570 public string MakeInternalID () {
571 string ret = internal_id_serial.ToString ();
573 internal_id_serial++;
578 // Emits the variable declarations and labels.
581 // tc: is our typecontainer (to resolve type references)
582 // ig: is the code generator:
583 // toplevel: the toplevel block. This is used for checking
584 // that no two labels with the same name are used.
586 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
589 // Process this block variables
591 if (variables != null){
592 local_builders = new Hashtable ();
594 foreach (DictionaryEntry de in variables){
595 string name = (string) de.Key;
596 VariableInfo vi = (VariableInfo) de.Value;
599 t = tc.LookupType (vi.Type, false);
604 vi.LocalBuilder = ig.DeclareLocal (t);
610 // Now, handle the children
612 if (children != null){
613 foreach (Block b in children)
614 b.EmitMeta (tc, ig, toplevel, count);
618 public void UsageWarning ()
622 if (variables != null){
623 foreach (DictionaryEntry de in variables){
624 VariableInfo vi = (VariableInfo) de.Value;
629 name = (string) de.Key;
633 219, vi.Location, "The variable `" + name +
634 "' is assigned but its value is never used");
637 168, vi.Location, "The variable `" +
639 "' is declared but never used");
644 if (children != null)
645 foreach (Block b in children)
649 public override bool Emit (EmitContext ec)
653 foreach (Statement s in Statements)
654 is_ret = s.Emit (ec);
660 public class SwitchLabel {
664 // if expr == null, then it is the default case.
666 public SwitchLabel (Expression expr)
671 public Expression Label {
678 public class SwitchSection {
679 // An array of SwitchLabels.
683 public SwitchSection (ArrayList labels, Block block)
685 this.labels = labels;
695 public ArrayList Labels {
702 public class Switch : Statement {
706 public Switch (Expression expr, ArrayList sections)
709 this.sections = sections;
712 public Expression Expr {
718 public ArrayList Sections {
724 public override bool Emit (EmitContext ec)
726 throw new Exception ("Unimplemented");
730 public class Lock : Statement {
731 public readonly Expression Expr;
732 public readonly Statement Statement;
734 public Lock (Expression expr, Statement stmt)
740 public override bool Emit (EmitContext ec)
742 throw new Exception ("Unimplemented");
747 public class Unchecked : Statement {
748 public readonly Block Block;
750 public Unchecked (Block b)
755 public override bool Emit (EmitContext ec)
757 bool previous_state = ec.CheckState;
760 ec.CheckState = false;
761 val = Block.Emit (ec);
762 ec.CheckState = previous_state;
768 public class Checked : Statement {
769 public readonly Block Block;
771 public Checked (Block b)
776 public override bool Emit (EmitContext ec)
778 bool previous_state = ec.CheckState;
781 ec.CheckState = true;
782 val = Block.Emit (ec);
783 ec.CheckState = previous_state;
790 public readonly string Type;
791 public readonly string Name;
792 public readonly Block Block;
794 public Catch (string type, string name, Block block)
802 public class Try : Statement {
803 public readonly Block Fini, Block;
804 public readonly ArrayList Specific;
805 public readonly Catch General;
808 // specific, general and fini might all be null.
810 public Try (Block block, ArrayList specific, Catch general, Block fini)
812 if (specific == null && general == null){
813 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
817 this.Specific = specific;
818 this.General = general;
822 public override bool Emit (EmitContext ec)
824 ILGenerator ig = ec.ig;
826 Label finish = ig.DefineLabel ();;
828 end = ig.BeginExceptionBlock ();
830 ig.Emit (OpCodes.Leave, finish);
832 foreach (Catch c in Specific){
833 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
836 if (catch_type == null)
839 ig.BeginCatchBlock (catch_type);
842 vi = c.Block.GetVariableInfo (c.Name);
844 Console.WriteLine ("This should not happen! variable does not exist in this block");
845 Environment.Exit (0);
848 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
850 ig.Emit (OpCodes.Pop);
855 if (General != null){
856 ig.BeginCatchBlock (TypeManager.object_type);
857 ig.Emit (OpCodes.Pop);
860 ig.MarkLabel (finish);
862 ig.BeginFinallyBlock ();
866 ig.EndExceptionBlock ();
872 public class Foreach : Statement {
873 public readonly string Type;
874 public readonly LocalVariableReference Variable;
875 public readonly Expression Expr;
876 public readonly Statement Statement;
877 public readonly Location Location;
879 public Foreach (string type, LocalVariableReference var, Expression expr,
880 Statement stmt, Location l)
889 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
894 if (!(m is MethodInfo))
897 if (m.Name != "GetEnumerator")
900 MethodInfo mi = (MethodInfo) m;
902 if (mi.ReturnType != TypeManager.ienumerator_type)
905 Type [] args = TypeManager.GetArgumentTypes (mi);
909 if (args.Length == 0)
916 // This filter is used to find the GetEnumerator method
917 // on which IEnumerator operates
919 static MemberFilter FilterEnumerator;
923 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
926 void error1579 (Type t)
928 Report.Error (1579, Location,
929 "foreach statement cannot operate on variables of type `" +
930 t.FullName + "' because that class does not provide a " +
931 " GetEnumerator method or it is inaccessible");
934 MethodInfo ProbeCollectionType (Type t)
938 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
940 FilterEnumerator, null);
952 return (MethodInfo) mi [0];
955 public override bool Emit (EmitContext ec)
957 ILGenerator ig = ec.ig;
960 LocalBuilder enumerator, disposable;
967 var_type = ec.TypeContainer.LookupType (Type, false);
968 if (var_type == null)
972 // We need an instance variable. Not sure this is the best
973 // way of doing this.
975 // FIXME: When we implement propertyaccess, will those turn
976 // out to return values in ExprClass? I think they should.
978 if (!(e.ExprClass == ExprClass.Variable || e.ExprClass == ExprClass.Value)){
983 if ((get_enum = ProbeCollectionType (e.Type)) == null)
986 Expression empty = new EmptyExpression ();
989 conv = Expression.ConvertExplicit (ec, empty, var_type, Location);
993 enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
994 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
997 // Instantiate the enumerator
999 Label end = ig.DefineLabel ();
1000 Label end_try = ig.DefineLabel ();
1001 Label loop = ig.DefineLabel ();
1004 // FIXME: This code does not work for cases like:
1005 // foreach (int a in ValueTypeVariable){
1008 // The code should emit an ldarga instruction
1009 // for the ValueTypeVariable rather than a ldarg
1011 if (e.Type.IsValueType){
1012 ig.Emit (OpCodes.Call, get_enum);
1015 ig.Emit (OpCodes.Callvirt, get_enum);
1017 ig.Emit (OpCodes.Stloc, enumerator);
1020 // Protect the code in a try/finalize block, so that
1021 // if the beast implement IDisposable, we get rid of it
1023 Label l = ig.BeginExceptionBlock ();
1024 ig.MarkLabel (loop);
1025 ig.Emit (OpCodes.Ldloc, enumerator);
1026 ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1027 ig.Emit (OpCodes.Brfalse, end_try);
1028 ig.Emit (OpCodes.Ldloc, enumerator);
1029 ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1031 Variable.Store (ec);
1032 Statement.Emit (ec);
1033 ig.Emit (OpCodes.Br, loop);
1034 ig.MarkLabel (end_try);
1036 // The runtime provides this for us.
1037 // ig.Emit (OpCodes.Leave, end);
1040 // Now the finally block
1042 Label end_finally = ig.DefineLabel ();
1044 ig.BeginFinallyBlock ();
1045 ig.Emit (OpCodes.Ldloc, enumerator);
1046 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1047 ig.Emit (OpCodes.Stloc, disposable);
1048 ig.Emit (OpCodes.Ldloc, disposable);
1049 ig.Emit (OpCodes.Brfalse, end_finally);
1050 ig.Emit (OpCodes.Ldloc, disposable);
1051 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1052 ig.MarkLabel (end_finally);
1054 // The runtime generates this anyways.
1055 // ig.Emit (OpCodes.Endfinally);
1057 ig.EndExceptionBlock ();