2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@gnome.org)
7 // Anirban Bhattacharjee (banirban@novell.com)
9 // (C) 2001, 2002 Ximian, Inc.
14 using System
.Reflection
;
15 using System
.Reflection
.Emit
;
16 using System
.Diagnostics
;
18 namespace Mono
.MonoBASIC
{
20 using System
.Collections
;
22 public abstract class Statement
{
26 /// Resolves the statement, true means that all sub-statements
29 public virtual bool Resolve (EmitContext ec
)
35 /// Return value indicates whether all code paths emitted return.
37 protected abstract bool DoEmit (EmitContext ec
);
40 /// Return value indicates whether all code paths emitted return.
42 public virtual bool Emit (EmitContext ec
)
45 Report
.Debug (8, "MARK", this, loc
);
49 public static Expression
ResolveBoolean (EmitContext ec
, Expression e
, Location loc
)
55 if (e
.Type
!= TypeManager
.bool_type
){
56 e
= Expression
.ConvertImplicit (ec
, e
, TypeManager
.bool_type
, Location
.Null
);
61 31, loc
, "Can not convert the expression to a boolean");
70 /// Encapsulates the emission of a boolean test and jumping to a
73 /// This will emit the bool expression in `bool_expr' and if
74 /// `target_is_for_true' is true, then the code will generate a
75 /// brtrue to the target. Otherwise a brfalse.
77 public static void EmitBoolExpression (EmitContext ec
, Expression bool_expr
,
78 Label target
, bool target_is_for_true
)
80 ILGenerator ig
= ec
.ig
;
83 if (bool_expr
is Unary
){
84 Unary u
= (Unary
) bool_expr
;
86 if (u
.Oper
== Unary
.Operator
.LogicalNot
){
89 u
.EmitLogicalNot (ec
);
91 } else if (bool_expr
is Binary
){
92 Binary b
= (Binary
) bool_expr
;
94 if (b
.EmitBranchable (ec
, target
, target_is_for_true
))
101 if (target_is_for_true
){
103 ig
.Emit (OpCodes
.Brfalse
, target
);
105 ig
.Emit (OpCodes
.Brtrue
, target
);
108 ig
.Emit (OpCodes
.Brtrue
, target
);
110 ig
.Emit (OpCodes
.Brfalse
, target
);
114 public static void Warning_DeadCodeFound (Location loc
)
116 Report
.Warning (162, loc
, "Unreachable code detected");
120 public class EmptyStatement
: Statement
{
121 public override bool Resolve (EmitContext ec
)
126 protected override bool DoEmit (EmitContext ec
)
132 public class If
: Statement
{
134 public Statement TrueStatement
;
135 public Statement FalseStatement
;
137 public If (Expression expr
, Statement trueStatement
, Location l
)
140 TrueStatement
= trueStatement
;
144 public If (Expression expr
,
145 Statement trueStatement
,
146 Statement falseStatement
,
150 TrueStatement
= trueStatement
;
151 FalseStatement
= falseStatement
;
155 public override bool Resolve (EmitContext ec
)
157 Report
.Debug (1, "START IF BLOCK", loc
);
159 expr
= ResolveBoolean (ec
, expr
, loc
);
164 ec
.StartFlowBranching (FlowBranchingType
.BLOCK
, loc
);
166 if (!TrueStatement
.Resolve (ec
)) {
167 ec
.KillFlowBranching ();
171 ec
.CurrentBranching
.CreateSibling ();
173 if ((FalseStatement
!= null) && !FalseStatement
.Resolve (ec
)) {
174 ec
.KillFlowBranching ();
178 ec
.EndFlowBranching ();
180 Report
.Debug (1, "END IF BLOCK", loc
);
185 protected override bool DoEmit (EmitContext ec
)
187 ILGenerator ig
= ec
.ig
;
188 Label false_target
= ig
.DefineLabel ();
190 bool is_true_ret
, is_false_ret
;
193 // Dead code elimination
195 if (expr
is BoolConstant
){
196 bool take
= ((BoolConstant
) expr
).Value
;
199 if (FalseStatement
!= null){
200 Warning_DeadCodeFound (FalseStatement
.loc
);
202 return TrueStatement
.Emit (ec
);
204 Warning_DeadCodeFound (TrueStatement
.loc
);
205 if (FalseStatement
!= null)
206 return FalseStatement
.Emit (ec
);
210 EmitBoolExpression (ec
, expr
, false_target
, false);
212 is_true_ret
= TrueStatement
.Emit (ec
);
213 is_false_ret
= is_true_ret
;
215 if (FalseStatement
!= null){
216 bool branch_emitted
= false;
218 end
= ig
.DefineLabel ();
220 ig
.Emit (OpCodes
.Br
, end
);
221 branch_emitted
= true;
224 ig
.MarkLabel (false_target
);
225 is_false_ret
= FalseStatement
.Emit (ec
);
230 ig
.MarkLabel (false_target
);
231 is_false_ret
= false;
234 return is_true_ret
&& is_false_ret
;
238 public enum DoOptions
{
245 public class Do
: Statement
{
246 public Expression expr
;
247 public readonly Statement EmbeddedStatement
;
248 //public DoOptions type;
249 public DoOptions test
;
250 bool infinite
, may_return
;
253 public Do (Statement statement
, Expression boolExpr
, DoOptions do_test
, Location l
)
256 EmbeddedStatement
= statement
;
262 public override bool Resolve (EmitContext ec
)
266 ec
.StartFlowBranching (FlowBranchingType
.LOOP_BLOCK
, loc
);
268 if (!EmbeddedStatement
.Resolve (ec
))
271 expr
= ResolveBoolean (ec
, expr
, loc
);
274 else if (expr
is BoolConstant
){
275 bool res
= ((BoolConstant
) expr
).Value
;
281 ec
.CurrentBranching
.Infinite
= infinite
;
282 FlowReturns returns
= ec
.EndFlowBranching ();
283 may_return
= returns
!= FlowReturns
.NEVER
;
288 protected override bool DoEmit (EmitContext ec
)
290 ILGenerator ig
= ec
.ig
;
291 Label loop
= ig
.DefineLabel ();
292 Label old_begin
= ec
.LoopBegin
;
293 Label old_end
= ec
.LoopEnd
;
294 bool old_inloop
= ec
.InLoop
;
295 int old_loop_begin_try_catch_level
= ec
.LoopBeginTryCatchLevel
;
297 ec
.LoopBegin
= ig
.DefineLabel ();
298 ec
.LoopEnd
= ig
.DefineLabel ();
300 ec
.LoopBeginTryCatchLevel
= ec
.TryCatchLevel
;
302 if (test
== DoOptions
.TEST_AFTER
) {
304 EmbeddedStatement
.Emit (ec
);
305 ig
.MarkLabel (ec
.LoopBegin
);
308 // Dead code elimination
310 if (expr
is BoolConstant
){
311 bool res
= ((BoolConstant
) expr
).Value
;
314 ec
.ig
.Emit (OpCodes
.Br
, loop
);
316 EmitBoolExpression (ec
, expr
, loop
, true);
318 ig
.MarkLabel (ec
.LoopEnd
);
323 ig
.MarkLabel (ec
.LoopBegin
);
326 // Dead code elimination
328 if (expr
is BoolConstant
){
329 bool res
= ((BoolConstant
) expr
).Value
;
332 ec
.ig
.Emit (OpCodes
.Br
, ec
.LoopEnd
);
334 EmitBoolExpression (ec
, expr
, ec
.LoopEnd
, true);
336 EmbeddedStatement
.Emit (ec
);
337 ec
.ig
.Emit (OpCodes
.Br
, loop
);
338 ig
.MarkLabel (ec
.LoopEnd
);
340 ec
.LoopBeginTryCatchLevel
= old_loop_begin_try_catch_level
;
341 ec
.LoopBegin
= old_begin
;
342 ec
.LoopEnd
= old_end
;
343 ec
.InLoop
= old_inloop
;
346 return may_return
== false;
352 public class While
: Statement
{
353 public Expression expr
;
354 public readonly Statement Statement
;
355 bool may_return
, empty
, infinite
;
357 public While (Expression boolExpr
, Statement statement
, Location l
)
359 this.expr
= boolExpr
;
360 Statement
= statement
;
364 public override bool Resolve (EmitContext ec
)
368 expr
= ResolveBoolean (ec
, expr
, loc
);
372 ec
.StartFlowBranching (FlowBranchingType
.LOOP_BLOCK
, loc
);
375 // Inform whether we are infinite or not
377 if (expr
is BoolConstant
){
378 BoolConstant bc
= (BoolConstant
) expr
;
380 if (bc
.Value
== false){
381 Warning_DeadCodeFound (Statement
.loc
);
387 // We are not infinite, so the loop may or may not be executed.
389 ec
.CurrentBranching
.CreateSibling ();
392 if (!Statement
.Resolve (ec
))
396 ec
.KillFlowBranching ();
398 ec
.CurrentBranching
.Infinite
= infinite
;
399 FlowReturns returns
= ec
.EndFlowBranching ();
400 may_return
= returns
!= FlowReturns
.NEVER
;
406 protected override bool DoEmit (EmitContext ec
)
411 ILGenerator ig
= ec
.ig
;
412 Label old_begin
= ec
.LoopBegin
;
413 Label old_end
= ec
.LoopEnd
;
414 bool old_inloop
= ec
.InLoop
;
415 int old_loop_begin_try_catch_level
= ec
.LoopBeginTryCatchLevel
;
418 ec
.LoopBegin
= ig
.DefineLabel ();
419 ec
.LoopEnd
= ig
.DefineLabel ();
421 ec
.LoopBeginTryCatchLevel
= ec
.TryCatchLevel
;
424 // Inform whether we are infinite or not
426 if (expr
is BoolConstant
){
427 BoolConstant bc
= (BoolConstant
) expr
;
429 ig
.MarkLabel (ec
.LoopBegin
);
431 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
434 // Inform that we are infinite (ie, `we return'), only
435 // if we do not `break' inside the code.
437 ret
= may_return
== false;
438 ig
.MarkLabel (ec
.LoopEnd
);
440 Label while_loop
= ig
.DefineLabel ();
442 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
443 ig
.MarkLabel (while_loop
);
447 ig
.MarkLabel (ec
.LoopBegin
);
449 EmitBoolExpression (ec
, expr
, while_loop
, true);
450 ig
.MarkLabel (ec
.LoopEnd
);
455 ec
.LoopBegin
= old_begin
;
456 ec
.LoopEnd
= old_end
;
457 ec
.InLoop
= old_inloop
;
458 ec
.LoopBeginTryCatchLevel
= old_loop_begin_try_catch_level
;
464 public class For
: Statement
{
466 readonly Statement InitStatement
;
467 readonly Statement Increment
;
468 readonly Statement Statement
;
469 bool may_return
, infinite
, empty
;
471 public For (Statement initStatement
,
477 InitStatement
= initStatement
;
479 Increment
= increment
;
480 Statement
= statement
;
485 public override bool Resolve (EmitContext ec
)
489 if (InitStatement
!= null){
490 if (!InitStatement
.Resolve (ec
))
495 Test
= ResolveBoolean (ec
, Test
, loc
);
498 else if (Test
is BoolConstant
){
499 BoolConstant bc
= (BoolConstant
) Test
;
501 if (bc
.Value
== false){
502 Warning_DeadCodeFound (Statement
.loc
);
510 if (Increment
!= null){
511 if (!Increment
.Resolve (ec
))
515 ec
.StartFlowBranching (FlowBranchingType
.LOOP_BLOCK
, loc
);
517 ec
.CurrentBranching
.CreateSibling ();
519 if (!Statement
.Resolve (ec
))
523 ec
.KillFlowBranching ();
525 ec
.CurrentBranching
.Infinite
= infinite
;
526 FlowReturns returns
= ec
.EndFlowBranching ();
527 may_return
= returns
!= FlowReturns
.NEVER
;
533 protected override bool DoEmit (EmitContext ec
)
538 ILGenerator ig
= ec
.ig
;
539 Label old_begin
= ec
.LoopBegin
;
540 Label old_end
= ec
.LoopEnd
;
541 bool old_inloop
= ec
.InLoop
;
542 int old_loop_begin_try_catch_level
= ec
.LoopBeginTryCatchLevel
;
543 Label loop
= ig
.DefineLabel ();
544 Label test
= ig
.DefineLabel ();
546 if (InitStatement
!= null)
547 if (! (InitStatement
is EmptyStatement
))
548 InitStatement
.Emit (ec
);
550 ec
.LoopBegin
= ig
.DefineLabel ();
551 ec
.LoopEnd
= ig
.DefineLabel ();
553 ec
.LoopBeginTryCatchLevel
= ec
.TryCatchLevel
;
555 ig
.Emit (OpCodes
.Br
, test
);
559 ig
.MarkLabel (ec
.LoopBegin
);
560 if (!(Increment
is EmptyStatement
))
565 // If test is null, there is no test, and we are just
569 EmitBoolExpression (ec
, Test
, loop
, true);
571 ig
.Emit (OpCodes
.Br
, loop
);
572 ig
.MarkLabel (ec
.LoopEnd
);
574 ec
.LoopBegin
= old_begin
;
575 ec
.LoopEnd
= old_end
;
576 ec
.InLoop
= old_inloop
;
577 ec
.LoopBeginTryCatchLevel
= old_loop_begin_try_catch_level
;
580 // Inform whether we are infinite or not
583 if (Test
is BoolConstant
){
584 BoolConstant bc
= (BoolConstant
) Test
;
587 return may_return
== false;
591 return may_return
== false;
595 public class StatementExpression
: Statement
{
596 public Expression expr
;
598 public StatementExpression (ExpressionStatement expr
, Location l
)
604 public override bool Resolve (EmitContext ec
)
606 expr
= (Expression
) expr
.Resolve (ec
);
610 protected override bool DoEmit (EmitContext ec
)
612 ILGenerator ig
= ec
.ig
;
614 if (expr
is ExpressionStatement
)
615 ((ExpressionStatement
) expr
).EmitStatement (ec
);
618 ig
.Emit (OpCodes
.Pop
);
624 public override string ToString ()
626 return "StatementExpression (" + expr
+ ")";
631 /// Implements the return statement
633 public class Return
: Statement
{
634 public Expression Expr
;
636 public Return (Expression expr
, Location l
)
642 public override bool Resolve (EmitContext ec
)
645 Expr
= Expr
.Resolve (ec
);
650 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
652 if (ec
.CurrentBranching
.InTryBlock ())
653 ec
.CurrentBranching
.AddFinallyVector (vector
);
655 vector
.CheckOutParameters (ec
.CurrentBranching
);
657 vector
.Returns
= FlowReturns
.ALWAYS
;
658 vector
.Breaks
= FlowReturns
.ALWAYS
;
662 protected override bool DoEmit (EmitContext ec
)
665 Report
.Error (157,loc
,"Control can not leave the body of the finally block");
669 if (ec
.ReturnType
== null){
671 Report
.Error (127, loc
, "Return with a value not allowed here");
676 Report
.Error (126, loc
, "An object of type `" +
677 TypeManager
.MonoBASIC_Name (ec
.ReturnType
) + "' is " +
678 "expected for the return statement");
682 if (Expr
.Type
!= ec
.ReturnType
)
683 Expr
= Expression
.ConvertImplicitRequired (
684 ec
, Expr
, ec
.ReturnType
, loc
);
691 if (ec
.InTry
|| ec
.InCatch
)
692 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
695 if (ec
.InTry
|| ec
.InCatch
) {
696 if (!ec
.HasReturnLabel
) {
697 ec
.ReturnLabel
= ec
.ig
.DefineLabel ();
698 ec
.HasReturnLabel
= true;
700 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
702 ec
.ig
.Emit (OpCodes
.Ret
);
708 public class Goto
: Statement
{
711 LabeledStatement label
;
713 public override bool Resolve (EmitContext ec
)
715 label
= block
.LookupLabel (target
);
719 "No such label `" + target
+ "' in this scope");
723 // If this is a forward goto.
724 if (!label
.IsDefined
)
725 label
.AddUsageVector (ec
.CurrentBranching
.CurrentUsageVector
);
727 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.ALWAYS
;
732 public Goto (Block parent_block
, string label
, Location l
)
734 block
= parent_block
;
739 public string Target
{
745 protected override bool DoEmit (EmitContext ec
)
747 Label l
= label
.LabelTarget (ec
);
748 ec
.ig
.Emit (OpCodes
.Br
, l
);
754 public class LabeledStatement
: Statement
{
755 public readonly Location Location
;
763 public LabeledStatement (string label_name
, Location l
)
765 this.label_name
= label_name
;
769 public Label
LabelTarget (EmitContext ec
)
773 label
= ec
.ig
.DefineLabel ();
779 public bool IsDefined
{
785 public bool HasBeenReferenced
{
791 public void AddUsageVector (FlowBranching
.UsageVector vector
)
794 vectors
= new ArrayList ();
796 vectors
.Add (vector
.Clone ());
799 public override bool Resolve (EmitContext ec
)
802 ec
.CurrentBranching
.CurrentUsageVector
.MergeJumpOrigins (vectors
);
804 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.NEVER
;
805 ec
.CurrentBranching
.CurrentUsageVector
.Returns
= FlowReturns
.NEVER
;
813 protected override bool DoEmit (EmitContext ec
)
816 ec
.ig
.MarkLabel (label
);
824 /// `goto default' statement
826 public class GotoDefault
: Statement
{
828 public GotoDefault (Location l
)
833 public override bool Resolve (EmitContext ec
)
835 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.UNREACHABLE
;
839 protected override bool DoEmit (EmitContext ec
)
841 if (ec
.Switch
== null){
842 Report
.Error (153, loc
, "goto default is only valid in a switch statement");
846 if (!ec
.Switch
.GotDefault
){
847 Report
.Error (159, loc
, "No default target on switch statement");
850 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
856 /// `goto case' statement
858 public class GotoCase
: Statement
{
862 public GotoCase (Expression e
, Location l
)
868 public override bool Resolve (EmitContext ec
)
870 if (ec
.Switch
== null){
871 Report
.Error (153, loc
, "goto case is only valid in a switch statement");
875 expr
= expr
.Resolve (ec
);
879 if (!(expr
is Constant
)){
880 Report
.Error (159, loc
, "Target expression for goto case is not constant");
884 object val
= Expression
.ConvertIntLiteral (
885 (Constant
) expr
, ec
.Switch
.SwitchType
, loc
);
890 SwitchLabel sl
= (SwitchLabel
) ec
.Switch
.Elements
[val
];
895 "No such label 'case " + val
+ "': for the goto case");
898 label
= sl
.ILLabelCode
;
900 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.UNREACHABLE
;
904 protected override bool DoEmit (EmitContext ec
)
906 ec
.ig
.Emit (OpCodes
.Br
, label
);
911 public class Throw
: Statement
{
914 public Throw (Expression expr
, Location l
)
920 public override bool Resolve (EmitContext ec
)
923 expr
= expr
.Resolve (ec
);
927 ExprClass eclass
= expr
.eclass
;
929 if (!(eclass
== ExprClass
.Variable
|| eclass
== ExprClass
.PropertyAccess
||
930 eclass
== ExprClass
.Value
|| eclass
== ExprClass
.IndexerAccess
)) {
931 expr
.Error118 ("value, variable, property or indexer access ");
937 if ((t
!= TypeManager
.exception_type
) &&
938 !t
.IsSubclassOf (TypeManager
.exception_type
) &&
939 !(expr
is NullLiteral
)) {
940 Report
.Error (30665, loc
,
941 "The type caught or thrown must be derived " +
942 "from System.Exception");
947 ec
.CurrentBranching
.CurrentUsageVector
.Returns
= FlowReturns
.EXCEPTION
;
948 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.EXCEPTION
;
952 protected override bool DoEmit (EmitContext ec
)
956 ec
.ig
.Emit (OpCodes
.Rethrow
);
960 "A throw statement with no argument is only " +
961 "allowed in a catch clause");
968 ec
.ig
.Emit (OpCodes
.Throw
);
974 public class Break
: Statement
{
976 public Break (Location l
)
981 public override bool Resolve (EmitContext ec
)
983 ec
.CurrentBranching
.MayLeaveLoop
= true;
984 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.ALWAYS
;
988 protected override bool DoEmit (EmitContext ec
)
990 ILGenerator ig
= ec
.ig
;
992 if (ec
.InLoop
== false && ec
.Switch
== null){
993 Report
.Error (139, loc
, "No enclosing loop or switch to continue to");
997 if (ec
.InTry
|| ec
.InCatch
)
998 ig
.Emit (OpCodes
.Leave
, ec
.LoopEnd
);
1000 ig
.Emit (OpCodes
.Br
, ec
.LoopEnd
);
1006 public enum ExitType
{
1017 public class Exit
: Statement
{
1018 public readonly ExitType type
;
1019 public Exit (ExitType t
, Location l
)
1025 public override bool Resolve (EmitContext ec
)
1027 ec
.CurrentBranching
.MayLeaveLoop
= true;
1028 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.ALWAYS
;
1032 protected override bool DoEmit (EmitContext ec
)
1034 ILGenerator ig
= ec
.ig
;
1036 if (type
!= ExitType
.SUB
&& type
!= ExitType
.FUNCTION
&&
1037 type
!= ExitType
.PROPERTY
&& type
!= ExitType
.TRY
) {
1038 if (ec
.InLoop
== false && ec
.Switch
== null){
1039 if (type
== ExitType
.FOR
)
1040 Report
.Error (30096, loc
, "No enclosing FOR loop to exit from");
1041 if (type
== ExitType
.WHILE
)
1042 Report
.Error (30097, loc
, "No enclosing WHILE loop to exit from");
1043 if (type
== ExitType
.DO
)
1044 Report
.Error (30089, loc
, "No enclosing DO loop to exit from");
1045 if (type
== ExitType
.SELECT
)
1046 Report
.Error (30099, loc
, "No enclosing SELECT to exit from");
1051 if (ec
.InTry
|| ec
.InCatch
)
1052 ig
.Emit (OpCodes
.Leave
, ec
.LoopEnd
);
1054 ig
.Emit (OpCodes
.Br
, ec
.LoopEnd
);
1057 Report
.Error (30393, loc
,
1058 "Control can not leave the body of the finally block");
1062 if (ec
.InTry
|| ec
.InCatch
) {
1063 if (!ec
.HasReturnLabel
) {
1064 ec
.ReturnLabel
= ec
.ig
.DefineLabel ();
1065 ec
.HasReturnLabel
= true;
1067 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
1069 ec
.ig
.Emit (OpCodes
.Ret
);
1078 public class Continue
: Statement
{
1080 public Continue (Location l
)
1085 public override bool Resolve (EmitContext ec
)
1087 ec
.CurrentBranching
.CurrentUsageVector
.Breaks
= FlowReturns
.ALWAYS
;
1091 protected override bool DoEmit (EmitContext ec
)
1093 Label begin
= ec
.LoopBegin
;
1096 Report
.Error (139, loc
, "No enclosing loop to continue to");
1101 // UGH: Non trivial. This Br might cross a try/catch boundary
1105 // try { ... } catch { continue; }
1109 // try {} catch { while () { continue; }}
1111 if (ec
.TryCatchLevel
> ec
.LoopBeginTryCatchLevel
)
1112 ec
.ig
.Emit (OpCodes
.Leave
, begin
);
1113 else if (ec
.TryCatchLevel
< ec
.LoopBeginTryCatchLevel
)
1114 throw new Exception ("Should never happen");
1116 ec
.ig
.Emit (OpCodes
.Br
, begin
);
1122 // This is used in the control flow analysis code to specify whether the
1123 // current code block may return to its enclosing block before reaching
1126 public enum FlowReturns
{
1127 // It can never return.
1130 // This means that the block contains a conditional return statement
1134 // The code always returns, ie. there's an unconditional return / break
1138 // The code always throws an exception.
1141 // The current code block is unreachable. This happens if it's immediately
1142 // following a FlowReturns.ALWAYS block.
1147 // This is a special bit vector which can inherit from another bit vector doing a
1148 // copy-on-write strategy. The inherited vector may have a smaller size than the
1151 public class MyBitVector
{
1152 public readonly int Count
;
1153 public readonly MyBitVector InheritsFrom
;
1158 public MyBitVector (int Count
)
1159 : this (null, Count
)
1162 public MyBitVector (MyBitVector InheritsFrom
, int Count
)
1164 this.InheritsFrom
= InheritsFrom
;
1169 // Checks whether this bit vector has been modified. After setting this to true,
1170 // we won't use the inherited vector anymore, but our own copy of it.
1172 public bool IsDirty
{
1179 initialize_vector ();
1184 // Get/set bit `index' in the bit vector.
1186 public bool this [int index
]
1190 throw new ArgumentOutOfRangeException ();
1192 // We're doing a "copy-on-write" strategy here; as long
1193 // as nobody writes to the array, we can use our parent's
1194 // copy instead of duplicating the vector.
1197 return vector
[index
];
1198 else if (InheritsFrom
!= null) {
1199 BitArray inherited
= InheritsFrom
.Vector
;
1201 if (index
< inherited
.Count
)
1202 return inherited
[index
];
1211 throw new ArgumentOutOfRangeException ();
1213 // Only copy the vector if we're actually modifying it.
1215 if (this [index
] != value) {
1216 initialize_vector ();
1218 vector
[index
] = value;
1224 // If you explicitly convert the MyBitVector to a BitArray, you will get a deep
1225 // copy of the bit vector.
1227 public static explicit operator BitArray (MyBitVector vector
)
1229 vector
.initialize_vector ();
1230 return vector
.Vector
;
1234 // Performs an `or' operation on the bit vector. The `new_vector' may have a
1235 // different size than the current one.
1237 public void Or (MyBitVector new_vector
)
1239 BitArray new_array
= new_vector
.Vector
;
1241 initialize_vector ();
1244 if (vector
.Count
< new_array
.Count
)
1245 upper
= vector
.Count
;
1247 upper
= new_array
.Count
;
1249 for (int i
= 0; i
< upper
; i
++)
1250 vector
[i
] = vector
[i
] | new_array
[i
];
1254 // Perfonrms an `and' operation on the bit vector. The `new_vector' may have
1255 // a different size than the current one.
1257 public void And (MyBitVector new_vector
)
1259 BitArray new_array
= new_vector
.Vector
;
1261 initialize_vector ();
1264 if (vector
.Count
< new_array
.Count
)
1265 lower
= upper
= vector
.Count
;
1267 lower
= new_array
.Count
;
1268 upper
= vector
.Count
;
1271 for (int i
= 0; i
< lower
; i
++)
1272 vector
[i
] = vector
[i
] & new_array
[i
];
1274 for (int i
= lower
; i
< upper
; i
++)
1279 // This does a deep copy of the bit vector.
1281 public MyBitVector
Clone ()
1283 MyBitVector retval
= new MyBitVector (Count
);
1285 retval
.Vector
= Vector
;
1294 else if (!is_dirty
&& (InheritsFrom
!= null))
1295 return InheritsFrom
.Vector
;
1297 initialize_vector ();
1303 initialize_vector ();
1305 for (int i
= 0; i
< System
.Math
.Min (vector
.Count
, value.Count
); i
++)
1306 vector
[i
] = value [i
];
1310 void initialize_vector ()
1315 vector
= new BitArray (Count
, false);
1316 if (InheritsFrom
!= null)
1317 Vector
= InheritsFrom
.Vector
;
1322 public override string ToString ()
1324 StringBuilder sb
= new StringBuilder ("MyBitVector (");
1326 BitArray vector
= Vector
;
1330 sb
.Append ("INHERITED - ");
1331 for (int i
= 0; i
< vector
.Count
; i
++) {
1334 sb
.Append (vector
[i
]);
1338 return sb
.ToString ();
1343 // The type of a FlowBranching.
1345 public enum FlowBranchingType
{
1346 // Normal (conditional or toplevel) block.
1363 // A new instance of this class is created every time a new block is resolved
1364 // and if there's branching in the block's control flow.
1366 public class FlowBranching
{
1368 // The type of this flow branching.
1370 public readonly FlowBranchingType Type
;
1373 // The block this branching is contained in. This may be null if it's not
1374 // a top-level block and it doesn't declare any local variables.
1376 public readonly Block Block
;
1379 // The parent of this branching or null if this is the top-block.
1381 public readonly FlowBranching Parent
;
1384 // Start-Location of this flow branching.
1386 public readonly Location Location
;
1389 // A list of UsageVectors. A new vector is added each time control flow may
1390 // take a different path.
1392 public ArrayList Siblings
;
1395 // If this is an infinite loop.
1397 public bool Infinite
;
1400 // If we may leave the current loop.
1402 public bool MayLeaveLoop
;
1407 InternalParameters param_info
;
1409 MyStructInfo
[] struct_params
;
1411 ArrayList finally_vectors
;
1413 static int next_id
= 0;
1417 // Performs an `And' operation on the FlowReturns status
1418 // (for instance, a block only returns ALWAYS if all its siblings
1421 public static FlowReturns
AndFlowReturns (FlowReturns a
, FlowReturns b
)
1423 if (b
== FlowReturns
.UNREACHABLE
)
1427 case FlowReturns
.NEVER
:
1428 if (b
== FlowReturns
.NEVER
)
1429 return FlowReturns
.NEVER
;
1431 return FlowReturns
.SOMETIMES
;
1433 case FlowReturns
.SOMETIMES
:
1434 return FlowReturns
.SOMETIMES
;
1436 case FlowReturns
.ALWAYS
:
1437 if ((b
== FlowReturns
.ALWAYS
) || (b
== FlowReturns
.EXCEPTION
))
1438 return FlowReturns
.ALWAYS
;
1440 return FlowReturns
.SOMETIMES
;
1442 case FlowReturns
.EXCEPTION
:
1443 if (b
== FlowReturns
.EXCEPTION
)
1444 return FlowReturns
.EXCEPTION
;
1445 else if (b
== FlowReturns
.ALWAYS
)
1446 return FlowReturns
.ALWAYS
;
1448 return FlowReturns
.SOMETIMES
;
1455 // The vector contains a BitArray with information about which local variables
1456 // and parameters are already initialized at the current code position.
1458 public class UsageVector
{
1460 // If this is true, then the usage vector has been modified and must be
1461 // merged when we're done with this branching.
1463 public bool IsDirty
;
1466 // The number of parameters in this block.
1468 public readonly int CountParameters
;
1471 // The number of locals in this block.
1473 public readonly int CountLocals
;
1476 // If not null, then we inherit our state from this vector and do a
1477 // copy-on-write. If null, then we're the first sibling in a top-level
1478 // block and inherit from the empty vector.
1480 public readonly UsageVector InheritsFrom
;
1485 MyBitVector locals
, parameters
;
1486 FlowReturns real_returns
, real_breaks
;
1489 static int next_id
= 0;
1493 // Normally, you should not use any of these constructors.
1495 public UsageVector (UsageVector parent
, int num_params
, int num_locals
)
1497 this.InheritsFrom
= parent
;
1498 this.CountParameters
= num_params
;
1499 this.CountLocals
= num_locals
;
1500 this.real_returns
= FlowReturns
.NEVER
;
1501 this.real_breaks
= FlowReturns
.NEVER
;
1503 if (parent
!= null) {
1504 locals
= new MyBitVector (parent
.locals
, CountLocals
);
1506 parameters
= new MyBitVector (parent
.parameters
, num_params
);
1507 real_returns
= parent
.Returns
;
1508 real_breaks
= parent
.Breaks
;
1510 locals
= new MyBitVector (null, CountLocals
);
1512 parameters
= new MyBitVector (null, num_params
);
1518 public UsageVector (UsageVector parent
)
1519 : this (parent
, parent
.CountParameters
, parent
.CountLocals
)
1523 // This does a deep copy of the usage vector.
1525 public UsageVector
Clone ()
1527 UsageVector retval
= new UsageVector (null, CountParameters
, CountLocals
);
1529 retval
.locals
= locals
.Clone ();
1530 if (parameters
!= null)
1531 retval
.parameters
= parameters
.Clone ();
1532 retval
.real_returns
= real_returns
;
1533 retval
.real_breaks
= real_breaks
;
1539 // State of parameter `number'.
1541 public bool this [int number
]
1546 else if (number
== 0)
1547 throw new ArgumentException ();
1549 return parameters
[number
- 1];
1555 else if (number
== 0)
1556 throw new ArgumentException ();
1558 parameters
[number
- 1] = value;
1563 // State of the local variable `vi'.
1564 // If the local variable is a struct, use a non-zero `field_idx'
1565 // to check an individual field in it.
1567 public bool this [VariableInfo vi
, int field_idx
]
1570 if (vi
.Number
== -1)
1572 else if (vi
.Number
== 0)
1573 throw new ArgumentException ();
1575 return locals
[vi
.Number
+ field_idx
- 1];
1579 if (vi
.Number
== -1)
1581 else if (vi
.Number
== 0)
1582 throw new ArgumentException ();
1584 locals
[vi
.Number
+ field_idx
- 1] = value;
1589 // Specifies when the current block returns.
1590 // If this is FlowReturns.UNREACHABLE, then control can never reach the
1591 // end of the method (so that we don't need to emit a return statement).
1592 // The same applies for FlowReturns.EXCEPTION, but in this case the return
1593 // value will never be used.
1595 public FlowReturns Returns
{
1597 return real_returns
;
1601 real_returns
= value;
1606 // Specifies whether control may return to our containing block
1607 // before reaching the end of this block. This happens if there
1608 // is a break/continue/goto/return in it.
1609 // This can also be used to find out whether the statement immediately
1610 // following the current block may be reached or not.
1612 public FlowReturns Breaks
{
1618 real_breaks
= value;
1622 public bool AlwaysBreaks
{
1624 return (Breaks
== FlowReturns
.ALWAYS
) ||
1625 (Breaks
== FlowReturns
.EXCEPTION
) ||
1626 (Breaks
== FlowReturns
.UNREACHABLE
);
1630 public bool MayBreak
{
1632 return Breaks
!= FlowReturns
.NEVER
;
1636 public bool AlwaysReturns
{
1638 return (Returns
== FlowReturns
.ALWAYS
) ||
1639 (Returns
== FlowReturns
.EXCEPTION
);
1643 public bool MayReturn
{
1645 return (Returns
== FlowReturns
.SOMETIMES
) ||
1646 (Returns
== FlowReturns
.ALWAYS
);
1651 // Merge a child branching.
1653 public FlowReturns
MergeChildren (FlowBranching branching
, ICollection children
)
1655 MyBitVector new_locals
= null;
1656 MyBitVector new_params
= null;
1658 FlowReturns new_returns
= FlowReturns
.NEVER
;
1659 FlowReturns new_breaks
= FlowReturns
.NEVER
;
1660 bool new_returns_set
= false, new_breaks_set
= false;
1662 Report
.Debug (2, "MERGING CHILDREN", branching
, branching
.Type
,
1663 this, children
.Count
);
1665 foreach (UsageVector child
in children
) {
1666 Report
.Debug (2, " MERGING CHILD", child
, child
.is_finally
);
1668 if (!child
.is_finally
) {
1669 if (child
.Breaks
!= FlowReturns
.UNREACHABLE
) {
1670 // If Returns is already set, perform an
1671 // `And' operation on it, otherwise just set just.
1672 if (!new_returns_set
) {
1673 new_returns
= child
.Returns
;
1674 new_returns_set
= true;
1676 new_returns
= AndFlowReturns (
1677 new_returns
, child
.Returns
);
1680 // If Breaks is already set, perform an
1681 // `And' operation on it, otherwise just set just.
1682 if (!new_breaks_set
) {
1683 new_breaks
= child
.Breaks
;
1684 new_breaks_set
= true;
1686 new_breaks
= AndFlowReturns (
1687 new_breaks
, child
.Breaks
);
1690 // Ignore unreachable children.
1691 if (child
.Returns
== FlowReturns
.UNREACHABLE
)
1694 // A local variable is initialized after a flow branching if it
1695 // has been initialized in all its branches which do neither
1696 // always return or always throw an exception.
1698 // If a branch may return, but does not always return, then we
1699 // can treat it like a never-returning branch here: control will
1700 // only reach the code position after the branching if we did not
1703 // It's important to distinguish between always and sometimes
1704 // returning branches here:
1707 // 2 if (something) {
1711 // 6 Console.WriteLine (a);
1713 // The if block in lines 3-4 always returns, so we must not look
1714 // at the initialization of `a' in line 4 - thus it'll still be
1715 // uninitialized in line 6.
1717 // On the other hand, the following is allowed:
1724 // 6 Console.WriteLine (a);
1726 // Here, `a' is initialized in line 3 and we must not look at
1727 // line 5 since it always returns.
1729 if (child
.is_finally
) {
1730 if (new_locals
== null)
1731 new_locals
= locals
.Clone ();
1732 new_locals
.Or (child
.locals
);
1734 if (parameters
!= null) {
1735 if (new_params
== null)
1736 new_params
= parameters
.Clone ();
1737 new_params
.Or (child
.parameters
);
1741 if (!child
.AlwaysReturns
&& !child
.AlwaysBreaks
) {
1742 if (new_locals
!= null)
1743 new_locals
.And (child
.locals
);
1745 new_locals
= locals
.Clone ();
1746 new_locals
.Or (child
.locals
);
1748 } else if (children
.Count
== 1) {
1749 new_locals
= locals
.Clone ();
1750 new_locals
.Or (child
.locals
);
1753 // An `out' parameter must be assigned in all branches which do
1754 // not always throw an exception.
1755 if (parameters
!= null) {
1756 if (child
.Breaks
!= FlowReturns
.EXCEPTION
) {
1757 if (new_params
!= null)
1758 new_params
.And (child
.parameters
);
1760 new_params
= parameters
.Clone ();
1761 new_params
.Or (child
.parameters
);
1763 } else if (children
.Count
== 1) {
1764 new_params
= parameters
.Clone ();
1765 new_params
.Or (child
.parameters
);
1771 Returns
= new_returns
;
1772 if ((branching
.Type
== FlowBranchingType
.BLOCK
) ||
1773 (branching
.Type
== FlowBranchingType
.EXCEPTION
) ||
1774 (new_breaks
== FlowReturns
.UNREACHABLE
) ||
1775 (new_breaks
== FlowReturns
.EXCEPTION
))
1776 Breaks
= new_breaks
;
1777 else if (branching
.Type
== FlowBranchingType
.SWITCH_SECTION
)
1778 Breaks
= new_returns
;
1779 else if (branching
.Type
== FlowBranchingType
.SWITCH
){
1780 if (new_breaks
== FlowReturns
.ALWAYS
)
1781 Breaks
= FlowReturns
.ALWAYS
;
1785 // We've now either reached the point after the branching or we will
1786 // never get there since we always return or always throw an exception.
1788 // If we can reach the point after the branching, mark all locals and
1789 // parameters as initialized which have been initialized in all branches
1790 // we need to look at (see above).
1793 if (((new_breaks
!= FlowReturns
.ALWAYS
) &&
1794 (new_breaks
!= FlowReturns
.EXCEPTION
) &&
1795 (new_breaks
!= FlowReturns
.UNREACHABLE
)) ||
1796 (children
.Count
== 1)) {
1797 if (new_locals
!= null)
1798 locals
.Or (new_locals
);
1800 if (new_params
!= null)
1801 parameters
.Or (new_params
);
1804 Report
.Debug (2, "MERGING CHILDREN DONE", branching
.Type
,
1805 new_params
, new_locals
, new_returns
, new_breaks
,
1806 branching
.Infinite
, branching
.MayLeaveLoop
, this);
1808 if (branching
.Type
== FlowBranchingType
.SWITCH_SECTION
) {
1809 if ((new_breaks
!= FlowReturns
.ALWAYS
) &&
1810 (new_breaks
!= FlowReturns
.EXCEPTION
) &&
1811 (new_breaks
!= FlowReturns
.UNREACHABLE
))
1812 Report
.Error (163, branching
.Location
,
1813 "Control cannot fall through from one " +
1814 "case label to another");
1817 if (branching
.Infinite
&& !branching
.MayLeaveLoop
) {
1818 Report
.Debug (1, "INFINITE", new_returns
, new_breaks
,
1819 Returns
, Breaks
, this);
1821 // We're actually infinite.
1822 if (new_returns
== FlowReturns
.NEVER
) {
1823 Breaks
= FlowReturns
.UNREACHABLE
;
1824 return FlowReturns
.UNREACHABLE
;
1827 // If we're an infinite loop and do not break, the code after
1828 // the loop can never be reached. However, if we may return
1829 // from the loop, then we do always return (or stay in the loop
1831 if ((new_returns
== FlowReturns
.SOMETIMES
) ||
1832 (new_returns
== FlowReturns
.ALWAYS
)) {
1833 Returns
= FlowReturns
.ALWAYS
;
1834 return FlowReturns
.ALWAYS
;
1842 // Tells control flow analysis that the current code position may be reached with
1843 // a forward jump from any of the origins listed in `origin_vectors' which is a
1844 // list of UsageVectors.
1846 // This is used when resolving forward gotos - in the following example, the
1847 // variable `a' is uninitialized in line 8 becase this line may be reached via
1848 // the goto in line 4:
1858 // 8 Console.WriteLine (a);
1861 public void MergeJumpOrigins (ICollection origin_vectors
)
1863 Report
.Debug (1, "MERGING JUMP ORIGIN", this);
1865 real_breaks
= FlowReturns
.NEVER
;
1866 real_returns
= FlowReturns
.NEVER
;
1868 foreach (UsageVector vector
in origin_vectors
) {
1869 Report
.Debug (1, " MERGING JUMP ORIGIN", vector
);
1871 locals
.And (vector
.locals
);
1872 if (parameters
!= null)
1873 parameters
.And (vector
.parameters
);
1874 Breaks
= AndFlowReturns (Breaks
, vector
.Breaks
);
1875 Returns
= AndFlowReturns (Returns
, vector
.Returns
);
1878 Report
.Debug (1, "MERGING JUMP ORIGIN DONE", this);
1882 // This is used at the beginning of a finally block if there were
1883 // any return statements in the try block or one of the catch blocks.
1885 public void MergeFinallyOrigins (ICollection finally_vectors
)
1887 Report
.Debug (1, "MERGING FINALLY ORIGIN", this);
1889 real_breaks
= FlowReturns
.NEVER
;
1891 foreach (UsageVector vector
in finally_vectors
) {
1892 Report
.Debug (1, " MERGING FINALLY ORIGIN", vector
);
1894 if (parameters
!= null)
1895 parameters
.And (vector
.parameters
);
1896 Breaks
= AndFlowReturns (Breaks
, vector
.Breaks
);
1901 Report
.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
1904 public void CheckOutParameters (FlowBranching branching
)
1906 if (parameters
!= null)
1907 branching
.CheckOutParameters (parameters
, branching
.Location
);
1911 // Performs an `or' operation on the locals and the parameters.
1913 public void Or (UsageVector new_vector
)
1915 locals
.Or (new_vector
.locals
);
1916 if (parameters
!= null)
1917 parameters
.Or (new_vector
.parameters
);
1921 // Performs an `and' operation on the locals.
1923 public void AndLocals (UsageVector new_vector
)
1925 locals
.And (new_vector
.locals
);
1929 // Returns a deep copy of the parameters.
1931 public MyBitVector Parameters
{
1933 if (parameters
!= null)
1934 return parameters
.Clone ();
1941 // Returns a deep copy of the locals.
1943 public MyBitVector Locals
{
1945 return locals
.Clone ();
1953 public override string ToString ()
1955 StringBuilder sb
= new StringBuilder ();
1957 sb
.Append ("Vector (");
1960 sb
.Append (Returns
);
1963 if (parameters
!= null) {
1965 sb
.Append (parameters
);
1971 return sb
.ToString ();
1975 FlowBranching (FlowBranchingType type
, Location loc
)
1977 this.Siblings
= new ArrayList ();
1979 this.Location
= loc
;
1985 // Creates a new flow branching for `block'.
1986 // This is used from Block.Resolve to create the top-level branching of
1989 public FlowBranching (Block block
, InternalParameters ip
, Location loc
)
1990 : this (FlowBranchingType
.BLOCK
, loc
)
1995 int count
= (ip
!= null) ? ip
.Count
: 0;
1998 param_map
= new int [count
];
1999 struct_params
= new MyStructInfo
[count
];
2002 for (int i
= 0; i
< count
; i
++) {
2003 Parameter
.Modifier mod
= param_info
.ParameterModifier (i
);
2005 if ((mod
& Parameter
.Modifier
.OUT
) == 0)
2008 param_map
[i
] = ++num_params
;
2010 Type param_type
= param_info
.ParameterType (i
);
2012 struct_params
[i
] = MyStructInfo
.GetStructInfo (param_type
);
2013 if (struct_params
[i
] != null)
2014 num_params
+= struct_params
[i
].Count
;
2017 Siblings
= new ArrayList ();
2018 Siblings
.Add (new UsageVector (null, num_params
, block
.CountVariables
));
2022 // Creates a new flow branching which is contained in `parent'.
2023 // You should only pass non-null for the `block' argument if this block
2024 // introduces any new variables - in this case, we need to create a new
2025 // usage vector with a different size than our parent's one.
2027 public FlowBranching (FlowBranching parent
, FlowBranchingType type
,
2028 Block block
, Location loc
)
2034 if (parent
!= null) {
2035 param_info
= parent
.param_info
;
2036 param_map
= parent
.param_map
;
2037 struct_params
= parent
.struct_params
;
2038 num_params
= parent
.num_params
;
2043 vector
= new UsageVector (parent
.CurrentUsageVector
, num_params
,
2044 Block
.CountVariables
);
2046 vector
= new UsageVector (Parent
.CurrentUsageVector
);
2048 Siblings
.Add (vector
);
2051 case FlowBranchingType
.EXCEPTION
:
2052 finally_vectors
= new ArrayList ();
2061 // Returns the branching's current usage vector.
2063 public UsageVector CurrentUsageVector
2066 return (UsageVector
) Siblings
[Siblings
.Count
- 1];
2071 // Creates a sibling of the current usage vector.
2073 public void CreateSibling ()
2075 Siblings
.Add (new UsageVector (Parent
.CurrentUsageVector
));
2077 Report
.Debug (1, "CREATED SIBLING", CurrentUsageVector
);
2081 // Creates a sibling for a `finally' block.
2083 public void CreateSiblingForFinally ()
2085 if (Type
!= FlowBranchingType
.EXCEPTION
)
2086 throw new NotSupportedException ();
2090 CurrentUsageVector
.MergeFinallyOrigins (finally_vectors
);
2094 // Check whether all `out' parameters have been assigned.
2096 public void CheckOutParameters (MyBitVector parameters
, Location loc
)
2101 for (int i
= 0; i
< param_map
.Length
; i
++) {
2102 int index
= param_map
[i
];
2107 if (parameters
[index
- 1])
2110 // If it's a struct, we must ensure that all its fields have
2111 // been assigned. If the struct has any non-public fields, this
2112 // can only be done by assigning the whole struct.
2114 MyStructInfo struct_info
= struct_params
[index
- 1];
2115 if ((struct_info
== null) || struct_info
.HasNonPublicFields
) {
2117 177, loc
, "The out parameter `" +
2118 param_info
.ParameterName (i
) + "' must be " +
2119 "assigned before control leave the current method.");
2125 for (int j
= 0; j
< struct_info
.Count
; j
++) {
2126 if (!parameters
[index
+ j
]) {
2128 177, loc
, "The out parameter `" +
2129 param_info
.ParameterName (i
) + "' must be " +
2130 "assigned before control leave the current method.");
2139 // Merge a child branching.
2141 public FlowReturns
MergeChild (FlowBranching child
)
2143 FlowReturns returns
= CurrentUsageVector
.MergeChildren (child
, child
.Siblings
);
2145 if (child
.Type
!= FlowBranchingType
.LOOP_BLOCK
)
2146 MayLeaveLoop
|= child
.MayLeaveLoop
;
2148 MayLeaveLoop
= false;
2154 // Does the toplevel merging.
2156 public FlowReturns
MergeTopBlock ()
2158 if ((Type
!= FlowBranchingType
.BLOCK
) || (Block
== null))
2159 throw new NotSupportedException ();
2161 UsageVector vector
= new UsageVector (null, num_params
, Block
.CountVariables
);
2163 Report
.Debug (1, "MERGING TOP BLOCK", Location
, vector
);
2165 vector
.MergeChildren (this, Siblings
);
2168 Siblings
.Add (vector
);
2170 Report
.Debug (1, "MERGING TOP BLOCK DONE", Location
, vector
);
2172 if (vector
.Breaks
!= FlowReturns
.EXCEPTION
) {
2173 if (!vector
.AlwaysBreaks
)
2174 CheckOutParameters (CurrentUsageVector
.Parameters
, Location
);
2175 return vector
.AlwaysBreaks
? FlowReturns
.ALWAYS
: vector
.Returns
;
2177 return FlowReturns
.EXCEPTION
;
2180 public bool InTryBlock ()
2182 if (finally_vectors
!= null)
2184 else if (Parent
!= null)
2185 return Parent
.InTryBlock ();
2190 public void AddFinallyVector (UsageVector vector
)
2192 if (finally_vectors
!= null) {
2193 finally_vectors
.Add (vector
.Clone ());
2198 Parent
.AddFinallyVector (vector
);
2200 throw new NotSupportedException ();
2203 public bool IsVariableAssigned (VariableInfo vi
)
2205 if (CurrentUsageVector
.AlwaysBreaks
)
2208 return CurrentUsageVector
[vi
, 0];
2211 public bool IsVariableAssigned (VariableInfo vi
, int field_idx
)
2213 if (CurrentUsageVector
.AlwaysBreaks
)
2216 return CurrentUsageVector
[vi
, field_idx
];
2219 public void SetVariableAssigned (VariableInfo vi
)
2221 if (CurrentUsageVector
.AlwaysBreaks
)
2224 CurrentUsageVector
[vi
, 0] = true;
2227 public void SetVariableAssigned (VariableInfo vi
, int field_idx
)
2229 if (CurrentUsageVector
.AlwaysBreaks
)
2232 CurrentUsageVector
[vi
, field_idx
] = true;
2235 public bool IsParameterAssigned (int number
)
2237 int index
= param_map
[number
];
2242 if (CurrentUsageVector
[index
])
2245 // Parameter is not assigned, so check whether it's a struct.
2246 // If it's either not a struct or a struct which non-public
2247 // fields, return false.
2248 MyStructInfo struct_info
= struct_params
[number
];
2249 if ((struct_info
== null) || struct_info
.HasNonPublicFields
)
2252 // Ok, so each field must be assigned.
2253 for (int i
= 0; i
< struct_info
.Count
; i
++)
2254 if (!CurrentUsageVector
[index
+ i
])
2260 public bool IsParameterAssigned (int number
, string field_name
)
2262 int index
= param_map
[number
];
2267 MyStructInfo info
= (MyStructInfo
) struct_params
[number
];
2271 int field_idx
= info
[field_name
];
2273 return CurrentUsageVector
[index
+ field_idx
];
2276 public void SetParameterAssigned (int number
)
2278 if (param_map
[number
] == 0)
2281 if (!CurrentUsageVector
.AlwaysBreaks
)
2282 CurrentUsageVector
[param_map
[number
]] = true;
2285 public void SetParameterAssigned (int number
, string field_name
)
2287 int index
= param_map
[number
];
2292 MyStructInfo info
= (MyStructInfo
) struct_params
[number
];
2296 int field_idx
= info
[field_name
];
2298 if (!CurrentUsageVector
.AlwaysBreaks
)
2299 CurrentUsageVector
[index
+ field_idx
] = true;
2302 public bool IsReachable ()
2307 case FlowBranchingType
.SWITCH_SECTION
:
2308 // The code following a switch block is reachable unless the switch
2309 // block always returns.
2310 reachable
= !CurrentUsageVector
.AlwaysReturns
;
2313 case FlowBranchingType
.LOOP_BLOCK
:
2314 // The code following a loop is reachable unless the loop always
2315 // returns or it's an infinite loop without any `break's in it.
2316 reachable
= !CurrentUsageVector
.AlwaysReturns
&&
2317 (CurrentUsageVector
.Breaks
!= FlowReturns
.UNREACHABLE
);
2321 // The code following a block or exception is reachable unless the
2322 // block either always returns or always breaks.
2323 reachable
= !CurrentUsageVector
.AlwaysBreaks
&&
2324 !CurrentUsageVector
.AlwaysReturns
;
2328 Report
.Debug (1, "REACHABLE", Type
, CurrentUsageVector
.Returns
,
2329 CurrentUsageVector
.Breaks
, CurrentUsageVector
, reachable
);
2334 public override string ToString ()
2336 StringBuilder sb
= new StringBuilder ("FlowBranching (");
2341 if (Block
!= null) {
2343 sb
.Append (Block
.ID
);
2345 sb
.Append (Block
.StartLocation
);
2348 sb
.Append (Siblings
.Count
);
2350 sb
.Append (CurrentUsageVector
);
2352 return sb
.ToString ();
2356 public class MyStructInfo
{
2357 public readonly Type Type
;
2358 public readonly FieldInfo
[] Fields
;
2359 public readonly FieldInfo
[] NonPublicFields
;
2360 public readonly int Count
;
2361 public readonly int CountNonPublic
;
2362 public readonly bool HasNonPublicFields
;
2364 private static Hashtable field_type_hash
= new Hashtable ();
2365 private Hashtable field_hash
;
2367 // Private constructor. To save memory usage, we only need to create one instance
2368 // of this class per struct type.
2369 private MyStructInfo (Type type
)
2373 if (type
is TypeBuilder
) {
2374 TypeContainer tc
= TypeManager
.LookupTypeContainer (type
);
2376 ArrayList fields
= tc
.Fields
;
2377 if (fields
!= null) {
2378 foreach (Field field
in fields
) {
2379 if ((field
.ModFlags
& Modifiers
.STATIC
) != 0)
2381 if ((field
.ModFlags
& Modifiers
.PUBLIC
) != 0)
2388 Fields
= new FieldInfo
[Count
];
2389 NonPublicFields
= new FieldInfo
[CountNonPublic
];
2391 Count
= CountNonPublic
= 0;
2392 if (fields
!= null) {
2393 foreach (Field field
in fields
) {
2394 if ((field
.ModFlags
& Modifiers
.STATIC
) != 0)
2396 if ((field
.ModFlags
& Modifiers
.PUBLIC
) != 0)
2397 Fields
[Count
++] = field
.FieldBuilder
;
2399 NonPublicFields
[CountNonPublic
++] =
2405 Fields
= type
.GetFields (BindingFlags
.Instance
|BindingFlags
.Public
);
2406 Count
= Fields
.Length
;
2408 NonPublicFields
= type
.GetFields (BindingFlags
.Instance
|BindingFlags
.NonPublic
);
2409 CountNonPublic
= NonPublicFields
.Length
;
2412 Count
+= NonPublicFields
.Length
;
2415 field_hash
= new Hashtable ();
2416 foreach (FieldInfo field
in Fields
)
2417 field_hash
.Add (field
.Name
, ++number
);
2419 if (NonPublicFields
.Length
!= 0)
2420 HasNonPublicFields
= true;
2422 foreach (FieldInfo field
in NonPublicFields
)
2423 field_hash
.Add (field
.Name
, ++number
);
2426 public int this [string name
] {
2428 if (field_hash
.Contains (name
))
2429 return (int) field_hash
[name
];
2435 public FieldInfo
this [int index
] {
2437 if (index
>= Fields
.Length
)
2438 return NonPublicFields
[index
- Fields
.Length
];
2440 return Fields
[index
];
2444 public static MyStructInfo
GetStructInfo (Type type
)
2446 if (!TypeManager
.IsValueType (type
) || TypeManager
.IsEnumType (type
))
2449 if (!(type
is TypeBuilder
) && TypeManager
.IsBuiltinType (type
))
2452 MyStructInfo info
= (MyStructInfo
) field_type_hash
[type
];
2456 info
= new MyStructInfo (type
);
2457 field_type_hash
.Add (type
, info
);
2461 public static MyStructInfo
GetStructInfo (TypeContainer tc
)
2463 MyStructInfo info
= (MyStructInfo
) field_type_hash
[tc
.TypeBuilder
];
2467 info
= new MyStructInfo (tc
.TypeBuilder
);
2468 field_type_hash
.Add (tc
.TypeBuilder
, info
);
2473 public class VariableInfo
: IVariable
{
2474 public Expression Type
;
2475 public LocalBuilder LocalBuilder
;
2476 public Type VariableType
;
2477 public readonly string Name
;
2478 public readonly Location Location
;
2479 public readonly int Block
;
2484 public bool Assigned
;
2485 public bool ReadOnly
;
2487 public VariableInfo (Expression type
, string name
, int block
, Location l
)
2492 LocalBuilder
= null;
2496 public VariableInfo (TypeContainer tc
, int block
, Location l
)
2498 VariableType
= tc
.TypeBuilder
;
2499 struct_info
= MyStructInfo
.GetStructInfo (tc
);
2501 LocalBuilder
= null;
2505 MyStructInfo struct_info
;
2506 public MyStructInfo StructInfo
{
2512 public bool IsAssigned (EmitContext ec
, Location loc
)
2513 {/* FIXME: we shouldn't just skip this!!!
2514 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
2517 MyStructInfo struct_info = StructInfo;
2518 if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
2519 Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
2520 ec.CurrentBranching.SetVariableAssigned (this);
2524 int count = struct_info.Count;
2526 for (int i = 0; i < count; i++) {
2527 if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
2529 Report.Error (165, loc,
2530 "Use of unassigned local variable `" +
2532 ec.CurrentBranching.SetVariableAssigned (this);
2536 FieldInfo field = struct_info [i];
2537 Report.Error (171, loc,
2538 "Field `" + TypeManager.MonoBASIC_Name (VariableType) +
2539 "." + field.Name + "' must be fully initialized " +
2540 "before control leaves the constructor");
2548 public bool IsFieldAssigned (EmitContext ec
, string name
, Location loc
)
2550 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsVariableAssigned (this) ||
2551 (struct_info
== null))
2554 int field_idx
= StructInfo
[name
];
2558 if (!ec
.CurrentBranching
.IsVariableAssigned (this, field_idx
)) {
2559 Report
.Error (170, loc
,
2560 "Use of possibly unassigned field `" + name
+ "'");
2561 ec
.CurrentBranching
.SetVariableAssigned (this, field_idx
);
2568 public void SetAssigned (EmitContext ec
)
2570 if (ec
.DoFlowAnalysis
)
2571 ec
.CurrentBranching
.SetVariableAssigned (this);
2574 public void SetFieldAssigned (EmitContext ec
, string name
)
2576 if (ec
.DoFlowAnalysis
&& (struct_info
!= null))
2577 ec
.CurrentBranching
.SetVariableAssigned (this, StructInfo
[name
]);
2580 public bool Resolve (DeclSpace decl
)
2582 if (struct_info
!= null)
2585 if (VariableType
== null)
2586 VariableType
= decl
.ResolveType (Type
, false, Location
);
2588 if (VariableType
== null)
2591 struct_info
= MyStructInfo
.GetStructInfo (VariableType
);
2596 public void MakePinned ()
2598 TypeManager
.MakePinned (LocalBuilder
);
2601 public override string ToString ()
2603 return "VariableInfo (" + Number
+ "," + Type
+ "," + Location
+ ")";
2608 /// Block represents a C# block.
2612 /// This class is used in a number of places: either to represent
2613 /// explicit blocks that the programmer places or implicit blocks.
2615 /// Implicit blocks are used as labels or to introduce variable
2618 public class Block
: Statement
{
2619 public readonly Block Parent
;
2620 public readonly bool Implicit
;
2621 public readonly Location StartLocation
;
2622 public Location EndLocation
;
2625 // The statements in this block
2627 public ArrayList statements
;
2630 // An array of Blocks. We keep track of children just
2631 // to generate the local variable declarations.
2633 // Statements and child statements are handled through the
2639 // Labels. (label, block) pairs.
2641 CaseInsensitiveHashtable labels
;
2644 // Keeps track of (name, type) pairs
2646 CaseInsensitiveHashtable variables
;
2649 // Keeps track of constants
2650 CaseInsensitiveHashtable constants
;
2653 // Maps variable names to ILGenerator.LocalBuilders
2655 CaseInsensitiveHashtable local_builders
;
2663 public Block (Block parent
)
2664 : this (parent
, false, Location
.Null
, Location
.Null
)
2667 public Block (Block parent
, bool implicit_block
)
2668 : this (parent
, implicit_block
, Location
.Null
, Location
.Null
)
2671 public Block (Block parent
, bool implicit_block
, Parameters parameters
)
2672 : this (parent
, implicit_block
, parameters
, Location
.Null
, Location
.Null
)
2675 public Block (Block parent
, Location start
, Location end
)
2676 : this (parent
, false, start
, end
)
2679 public Block (Block parent
, Parameters parameters
, Location start
, Location end
)
2680 : this (parent
, false, parameters
, start
, end
)
2683 public Block (Block parent
, bool implicit_block
, Location start
, Location end
)
2684 : this (parent
, implicit_block
, Parameters
.EmptyReadOnlyParameters
,
2688 public Block (Block parent
, bool implicit_block
, Parameters parameters
,
2689 Location start
, Location end
)
2692 parent
.AddChild (this);
2694 this.Parent
= parent
;
2695 this.Implicit
= implicit_block
;
2696 this.parameters
= parameters
;
2697 this.StartLocation
= start
;
2698 this.EndLocation
= end
;
2701 statements
= new ArrayList ();
2710 void AddChild (Block b
)
2712 if (children
== null)
2713 children
= new ArrayList ();
2718 public void SetEndLocation (Location loc
)
2724 /// Adds a label to the current block.
2728 /// false if the name already exists in this block. true
2732 public bool AddLabel (string name
, LabeledStatement target
)
2735 labels
= new CaseInsensitiveHashtable ();
2736 if (labels
.Contains (name
))
2739 labels
.Add (name
, target
);
2743 public LabeledStatement
LookupLabel (string name
)
2745 if (labels
!= null){
2746 if (labels
.Contains (name
))
2747 return ((LabeledStatement
) labels
[name
]);
2751 return Parent
.LookupLabel (name
);
2756 VariableInfo this_variable
= null;
2759 // Returns the "this" instance variable of this block.
2760 // See AddThisVariable() for more information.
2762 public VariableInfo ThisVariable
{
2764 if (this_variable
!= null)
2765 return this_variable
;
2766 else if (Parent
!= null)
2767 return Parent
.ThisVariable
;
2773 Hashtable child_variable_names
;
2776 // Marks a variable with name @name as being used in a child block.
2777 // If a variable name has been used in a child block, it's illegal to
2778 // declare a variable with the same name in the current block.
2780 public void AddChildVariableName (string name
)
2782 if (child_variable_names
== null)
2783 child_variable_names
= new CaseInsensitiveHashtable ();
2785 if (!child_variable_names
.Contains (name
))
2786 child_variable_names
.Add (name
, true);
2790 // Marks all variables from block @block and all its children as being
2791 // used in a child block.
2793 public void AddChildVariableNames (Block block
)
2795 if (block
.Variables
!= null) {
2796 foreach (string name
in block
.Variables
.Keys
)
2797 AddChildVariableName (name
);
2800 foreach (Block child
in block
.children
) {
2801 if (child
.Variables
!= null) {
2802 foreach (string name
in child
.Variables
.Keys
)
2803 AddChildVariableName (name
);
2809 // Checks whether a variable name has already been used in a child block.
2811 public bool IsVariableNameUsedInChildBlock (string name
)
2813 if (child_variable_names
== null)
2816 return child_variable_names
.Contains (name
);
2820 // This is used by non-static `struct' constructors which do not have an
2821 // initializer - in this case, the constructor must initialize all of the
2822 // struct's fields. To do this, we add a "this" variable and use the flow
2823 // analysis code to ensure that it's been fully initialized before control
2824 // leaves the constructor.
2826 public VariableInfo
AddThisVariable (TypeContainer tc
, Location l
)
2828 if (this_variable
!= null)
2829 return this_variable
;
2831 this_variable
= new VariableInfo (tc
, ID
, l
);
2833 if (variables
== null)
2834 variables
= new CaseInsensitiveHashtable ();
2835 variables
.Add ("this", this_variable
);
2837 return this_variable
;
2840 public VariableInfo
AddVariable (Expression type
, string name
, Parameters pars
, Location l
)
2842 if (variables
== null)
2843 variables
= new CaseInsensitiveHashtable ();
2845 VariableInfo vi
= GetVariableInfo (name
);
2848 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
2849 "cannot be declared in this scope since it would " +
2850 "give a different meaning to `" + name
+ "', which " +
2851 "is already used in a `parent or current' scope to " +
2852 "denote something else");
2854 Report
.Error (128, l
, "A local variable `" + name
+ "' is already " +
2855 "defined in this scope");
2859 if (IsVariableNameUsedInChildBlock (name
)) {
2860 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
2861 "cannot be declared in this scope since it would " +
2862 "give a different meaning to `" + name
+ "', which " +
2863 "is already used in a `child' scope to denote something " +
2870 Parameter p
= pars
.GetParameterByName (name
, out idx
);
2872 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
2873 "cannot be declared in this scope since it would " +
2874 "give a different meaning to `" + name
+ "', which " +
2875 "is already used in a `parent or current' scope to " +
2876 "denote something else");
2881 vi
= new VariableInfo (type
, name
, ID
, l
);
2883 variables
.Add (name
, vi
);
2885 if (variables_initialized
)
2886 throw new Exception ();
2888 // Console.WriteLine ("Adding {0} to {1}", name, ID);
2892 public bool AddConstant (Expression type
, string name
, Expression
value, Parameters pars
, Location l
)
2894 if (AddVariable (type
, name
, pars
, l
) == null)
2897 if (constants
== null)
2898 constants
= new CaseInsensitiveHashtable ();
2900 constants
.Add (name
, value);
2904 public Hashtable Variables
{
2910 public VariableInfo
GetVariableInfo (string name
)
2912 if (variables
!= null) {
2914 temp
= variables
[name
];
2917 return (VariableInfo
) temp
;
2922 return Parent
.GetVariableInfo (name
);
2927 public Expression
GetVariableType (string name
)
2929 VariableInfo vi
= GetVariableInfo (name
);
2937 public Expression
GetConstantExpression (string name
)
2939 if (constants
!= null) {
2941 temp
= constants
[name
];
2944 return (Expression
) temp
;
2948 return Parent
.GetConstantExpression (name
);
2954 /// True if the variable named @name has been defined
2957 public bool IsVariableDefined (string name
)
2959 // Console.WriteLine ("Looking up {0} in {1}", name, ID);
2960 if (variables
!= null) {
2961 if (variables
.Contains (name
))
2966 return Parent
.IsVariableDefined (name
);
2972 /// True if the variable named @name is a constant
2974 public bool IsConstant (string name
)
2976 Expression e
= null;
2978 e
= GetConstantExpression (name
);
2984 /// Use to fetch the statement associated with this label
2986 public Statement
this [string name
] {
2988 return (Statement
) labels
[name
];
2992 Parameters parameters
= null;
2993 public Parameters Parameters
{
2996 return Parent
.Parameters
;
3003 /// A list of labels that were not used within this block
3005 public string [] GetUnreferenced ()
3007 // FIXME: Implement me
3011 public void AddStatement (Statement s
)
3028 bool variables_initialized
= false;
3029 int count_variables
= 0, first_variable
= 0;
3031 void UpdateVariableInfo (EmitContext ec
)
3033 DeclSpace ds
= ec
.DeclSpace
;
3038 first_variable
+= Parent
.CountVariables
;
3040 count_variables
= first_variable
;
3041 if (variables
!= null) {
3042 foreach (VariableInfo vi
in variables
.Values
) {
3043 if (!vi
.Resolve (ds
)) {
3048 vi
.Number
= ++count_variables
;
3050 if (vi
.StructInfo
!= null)
3051 count_variables
+= vi
.StructInfo
.Count
;
3055 variables_initialized
= true;
3060 // The number of local variables in this block
3062 public int CountVariables
3065 if (!variables_initialized
)
3066 throw new Exception ();
3068 return count_variables
;
3073 /// Emits the variable declarations and labels.
3076 /// tc: is our typecontainer (to resolve type references)
3077 /// ig: is the code generator:
3078 /// toplevel: the toplevel block. This is used for checking
3079 /// that no two labels with the same name are used.
3081 public void EmitMeta (EmitContext ec
, Block toplevel
)
3083 DeclSpace ds
= ec
.DeclSpace
;
3084 ILGenerator ig
= ec
.ig
;
3086 if (!variables_initialized
)
3087 UpdateVariableInfo (ec
);
3090 // Process this block variables
3092 if (variables
!= null){
3093 local_builders
= new CaseInsensitiveHashtable ();
3095 foreach (DictionaryEntry de
in variables
){
3096 string name
= (string) de
.Key
;
3097 VariableInfo vi
= (VariableInfo
) de
.Value
;
3099 if (vi
.VariableType
== null)
3102 vi
.LocalBuilder
= ig
.DeclareLocal (vi
.VariableType
);
3104 if (CodeGen
.SymbolWriter
!= null)
3105 vi
.LocalBuilder
.SetLocalSymInfo (name
);
3107 if (constants
== null)
3110 Expression cv
= (Expression
) constants
[name
];
3114 Expression e
= cv
.Resolve (ec
);
3118 if (!(e
is Constant
)){
3119 Report
.Error (133, vi
.Location
,
3120 "The expression being assigned to `" +
3121 name
+ "' must be constant (" + e
+ ")");
3125 constants
.Remove (name
);
3126 constants
.Add (name
, e
);
3131 // Now, handle the children
3133 if (children
!= null){
3134 foreach (Block b
in children
)
3135 b
.EmitMeta (ec
, toplevel
);
3139 public void UsageWarning ()
3143 if (variables
!= null){
3144 foreach (DictionaryEntry de
in variables
){
3145 VariableInfo vi
= (VariableInfo
) de
.Value
;
3150 name
= (string) de
.Key
;
3154 219, vi
.Location
, "The variable `" + name
+
3155 "' is assigned but its value is never used");
3158 168, vi
.Location
, "The variable `" +
3160 "' is declared but never used");
3165 if (children
!= null)
3166 foreach (Block b
in children
)
3170 bool has_ret
= false;
3172 public override bool Resolve (EmitContext ec
)
3174 Block prev_block
= ec
.CurrentBlock
;
3177 ec
.CurrentBlock
= this;
3178 ec
.StartFlowBranching (this);
3180 Report
.Debug (1, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
3182 if (!variables_initialized
)
3183 UpdateVariableInfo (ec
);
3185 ArrayList new_statements
= new ArrayList ();
3186 bool unreachable
= false, warning_shown
= false;
3188 foreach (Statement s
in statements
){
3189 if (unreachable
&& !(s
is LabeledStatement
)) {
3190 if (!warning_shown
&& !(s
is EmptyStatement
)) {
3191 warning_shown
= true;
3192 Warning_DeadCodeFound (s
.loc
);
3198 if (s
.Resolve (ec
) == false) {
3203 if (s
is LabeledStatement
)
3204 unreachable
= false;
3206 unreachable
= ! ec
.CurrentBranching
.IsReachable ();
3208 new_statements
.Add (s
);
3211 statements
= new_statements
;
3213 Report
.Debug (1, "RESOLVE BLOCK DONE", StartLocation
, ec
.CurrentBranching
);
3215 FlowReturns returns
= ec
.EndFlowBranching ();
3216 ec
.CurrentBlock
= prev_block
;
3218 // If we're a non-static `struct' constructor which doesn't have an
3219 // initializer, then we must initialize all of the struct's fields.
3220 if ((this_variable
!= null) && (returns
!= FlowReturns
.EXCEPTION
) &&
3221 !this_variable
.IsAssigned (ec
, loc
))
3224 if ((labels
!= null) && (RootContext
.WarningLevel
>= 2)) {
3225 foreach (LabeledStatement label
in labels
.Values
)
3226 if (!label
.HasBeenReferenced
)
3227 Report
.Warning (164, label
.Location
,
3228 "This label has not been referenced");
3231 if ((returns
== FlowReturns
.ALWAYS
) ||
3232 (returns
== FlowReturns
.EXCEPTION
) ||
3233 (returns
== FlowReturns
.UNREACHABLE
))
3239 protected override bool DoEmit (EmitContext ec
)
3241 Block prev_block
= ec
.CurrentBlock
;
3243 ec
.CurrentBlock
= this;
3245 ec
.Mark (StartLocation
);
3246 foreach (Statement s
in statements
)
3249 ec
.Mark (EndLocation
);
3251 ec
.CurrentBlock
= prev_block
;
3256 public class SwitchLabel
{
3259 public Location loc
;
3260 public Label ILLabel
;
3261 public Label ILLabelCode
;
3264 // if expr == null, then it is the default case.
3266 public SwitchLabel (Expression expr
, Location l
)
3272 public Expression Label
{
3278 public object Converted
{
3285 // Resolves the expression, reduces it to a literal if possible
3286 // and then converts it to the requested type.
3288 public bool ResolveAndReduce (EmitContext ec
, Type required_type
)
3290 ILLabel
= ec
.ig
.DefineLabel ();
3291 ILLabelCode
= ec
.ig
.DefineLabel ();
3296 Expression e
= label
.Resolve (ec
);
3301 if (!(e
is Constant
)){
3302 Console
.WriteLine ("Value is: " + label
);
3303 Report
.Error (150, loc
, "A constant value is expected");
3307 if (e
is StringConstant
|| e
is NullLiteral
){
3308 if (required_type
== TypeManager
.string_type
){
3310 ILLabel
= ec
.ig
.DefineLabel ();
3315 converted
= Expression
.ConvertIntLiteral ((Constant
) e
, required_type
, loc
);
3316 if (converted
== null)
3323 public class SwitchSection
{
3324 // An array of SwitchLabels.
3325 public readonly ArrayList Labels
;
3326 public readonly Block Block
;
3328 public SwitchSection (ArrayList labels
, Block block
)
3335 public class Switch
: Statement
{
3336 public readonly ArrayList Sections
;
3337 public Expression Expr
;
3340 /// Maps constants whose type type SwitchType to their SwitchLabels.
3342 public Hashtable Elements
;
3345 /// The governing switch type
3347 public Type SwitchType
;
3353 Label default_target
;
3354 Expression new_expr
;
3357 // The types allowed to be implicitly cast from
3358 // on the governing type
3360 static Type
[] allowed_types
;
3362 public Switch (Expression e
, ArrayList sects
, Location l
)
3369 public bool GotDefault
{
3375 public Label DefaultTarget
{
3377 return default_target
;
3382 // Determines the governing type for a switch. The returned
3383 // expression might be the expression from the switch, or an
3384 // expression that includes any potential conversions to the
3385 // integral types or to string.
3387 Expression
SwitchGoverningType (EmitContext ec
, Type t
)
3389 if (t
== TypeManager
.int32_type
||
3390 t
== TypeManager
.uint32_type
||
3391 t
== TypeManager
.char_type
||
3392 t
== TypeManager
.byte_type
||
3393 t
== TypeManager
.sbyte_type
||
3394 t
== TypeManager
.ushort_type
||
3395 t
== TypeManager
.short_type
||
3396 t
== TypeManager
.uint64_type
||
3397 t
== TypeManager
.int64_type
||
3398 t
== TypeManager
.string_type
||
3399 t
== TypeManager
.bool_type
||
3400 t
.IsSubclassOf (TypeManager
.enum_type
))
3403 if (allowed_types
== null){
3404 allowed_types
= new Type
[] {
3405 TypeManager
.sbyte_type
,
3406 TypeManager
.byte_type
,
3407 TypeManager
.short_type
,
3408 TypeManager
.ushort_type
,
3409 TypeManager
.int32_type
,
3410 TypeManager
.uint32_type
,
3411 TypeManager
.int64_type
,
3412 TypeManager
.uint64_type
,
3413 TypeManager
.char_type
,
3414 TypeManager
.bool_type
,
3415 TypeManager
.string_type
3420 // Try to find a *user* defined implicit conversion.
3422 // If there is no implicit conversion, or if there are multiple
3423 // conversions, we have to report an error
3425 Expression converted
= null;
3426 foreach (Type tt
in allowed_types
){
3429 e
= Expression
.ImplicitUserConversion (ec
, Expr
, tt
, loc
);
3433 if (converted
!= null){
3434 Report
.Error (-12, loc
, "More than one conversion to an integral " +
3435 " type exists for type `" +
3436 TypeManager
.MonoBASIC_Name (Expr
.Type
)+"'");
3444 void error152 (string n
)
3447 152, "The label `" + n
+ ":' " +
3448 "is already present on this switch statement");
3452 // Performs the basic sanity checks on the switch statement
3453 // (looks for duplicate keys and non-constant expressions).
3455 // It also returns a hashtable with the keys that we will later
3456 // use to compute the switch tables
3458 bool CheckSwitch (EmitContext ec
)
3462 Elements
= new CaseInsensitiveHashtable ();
3464 got_default
= false;
3466 if (TypeManager
.IsEnumType (SwitchType
)){
3467 compare_type
= TypeManager
.EnumToUnderlying (SwitchType
);
3469 compare_type
= SwitchType
;
3471 foreach (SwitchSection ss
in Sections
){
3472 foreach (SwitchLabel sl
in ss
.Labels
){
3473 if (!sl
.ResolveAndReduce (ec
, SwitchType
)){
3478 if (sl
.Label
== null){
3480 error152 ("default");
3487 object key
= sl
.Converted
;
3489 if (key
is Constant
)
3490 key
= ((Constant
) key
).GetValue ();
3493 key
= NullLiteral
.Null
;
3495 string lname
= null;
3496 if (compare_type
== TypeManager
.uint64_type
){
3497 ulong v
= (ulong) key
;
3499 if (Elements
.Contains (v
))
3500 lname
= v
.ToString ();
3502 Elements
.Add (v
, sl
);
3503 } else if (compare_type
== TypeManager
.int64_type
){
3504 long v
= (long) key
;
3506 if (Elements
.Contains (v
))
3507 lname
= v
.ToString ();
3509 Elements
.Add (v
, sl
);
3510 } else if (compare_type
== TypeManager
.uint32_type
){
3511 uint v
= (uint) key
;
3513 if (Elements
.Contains (v
))
3514 lname
= v
.ToString ();
3516 Elements
.Add (v
, sl
);
3517 } else if (compare_type
== TypeManager
.char_type
){
3518 char v
= (char) key
;
3520 if (Elements
.Contains (v
))
3521 lname
= v
.ToString ();
3523 Elements
.Add (v
, sl
);
3524 } else if (compare_type
== TypeManager
.byte_type
){
3525 byte v
= (byte) key
;
3527 if (Elements
.Contains (v
))
3528 lname
= v
.ToString ();
3530 Elements
.Add (v
, sl
);
3531 } else if (compare_type
== TypeManager
.sbyte_type
){
3532 sbyte v
= (sbyte) key
;
3534 if (Elements
.Contains (v
))
3535 lname
= v
.ToString ();
3537 Elements
.Add (v
, sl
);
3538 } else if (compare_type
== TypeManager
.short_type
){
3539 short v
= (short) key
;
3541 if (Elements
.Contains (v
))
3542 lname
= v
.ToString ();
3544 Elements
.Add (v
, sl
);
3545 } else if (compare_type
== TypeManager
.ushort_type
){
3546 ushort v
= (ushort) key
;
3548 if (Elements
.Contains (v
))
3549 lname
= v
.ToString ();
3551 Elements
.Add (v
, sl
);
3552 } else if (compare_type
== TypeManager
.string_type
){
3553 if (key
is NullLiteral
){
3554 if (Elements
.Contains (NullLiteral
.Null
))
3557 Elements
.Add (NullLiteral
.Null
, null);
3559 string s
= (string) key
;
3561 if (Elements
.Contains (s
))
3564 Elements
.Add (s
, sl
);
3566 } else if (compare_type
== TypeManager
.int32_type
) {
3569 if (Elements
.Contains (v
))
3570 lname
= v
.ToString ();
3572 Elements
.Add (v
, sl
);
3573 } else if (compare_type
== TypeManager
.bool_type
) {
3574 bool v
= (bool) key
;
3576 if (Elements
.Contains (v
))
3577 lname
= v
.ToString ();
3579 Elements
.Add (v
, sl
);
3583 throw new Exception ("Unknown switch type!" +
3584 SwitchType
+ " " + compare_type
);
3588 error152 ("case + " + lname
);
3599 void EmitObjectInteger (ILGenerator ig
, object k
)
3602 IntConstant
.EmitInt (ig
, (int) k
);
3603 else if (k
is Constant
) {
3604 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
3607 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
3610 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
3612 IntConstant
.EmitInt (ig
, (int) (long) k
);
3613 ig
.Emit (OpCodes
.Conv_I8
);
3616 LongConstant
.EmitLong (ig
, (long) k
);
3618 else if (k
is ulong)
3620 if ((ulong) k
< (1L<<32))
3622 IntConstant
.EmitInt (ig
, (int) (long) k
);
3623 ig
.Emit (OpCodes
.Conv_U8
);
3627 LongConstant
.EmitLong (ig
, unchecked ((long) (ulong) k
));
3631 IntConstant
.EmitInt (ig
, (int) ((char) k
));
3632 else if (k
is sbyte)
3633 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
3635 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
3636 else if (k
is short)
3637 IntConstant
.EmitInt (ig
, (int) ((short) k
));
3638 else if (k
is ushort)
3639 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
3641 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
3643 throw new Exception ("Unhandled case");
3646 // structure used to hold blocks of keys while calculating table switch
3647 class KeyBlock
: IComparable
3649 public KeyBlock (long _nFirst
)
3651 nFirst
= nLast
= _nFirst
;
3655 public ArrayList rgKeys
= null;
3658 get { return (int) (nLast - nFirst + 1); }
3660 public static long TotalLength (KeyBlock kbFirst
, KeyBlock kbLast
)
3662 return kbLast
.nLast
- kbFirst
.nFirst
+ 1;
3664 public int CompareTo (object obj
)
3666 KeyBlock kb
= (KeyBlock
) obj
;
3667 int nLength
= Length
;
3668 int nLengthOther
= kb
.Length
;
3669 if (nLengthOther
== nLength
)
3670 return (int) (kb
.nFirst
- nFirst
);
3671 return nLength
- nLengthOther
;
3676 /// This method emits code for a lookup-based switch statement (non-string)
3677 /// Basically it groups the cases into blocks that are at least half full,
3678 /// and then spits out individual lookup opcodes for each block.
3679 /// It emits the longest blocks first, and short blocks are just
3680 /// handled with direct compares.
3682 /// <param name="ec"></param>
3683 /// <param name="val"></param>
3684 /// <returns></returns>
3685 bool TableSwitchEmit (EmitContext ec
, LocalBuilder val
)
3687 int cElements
= Elements
.Count
;
3688 object [] rgKeys
= new object [cElements
];
3689 Elements
.Keys
.CopyTo (rgKeys
, 0);
3690 Array
.Sort (rgKeys
);
3692 // initialize the block list with one element per key
3693 ArrayList rgKeyBlocks
= new ArrayList ();
3694 foreach (object key
in rgKeys
)
3695 rgKeyBlocks
.Add (new KeyBlock (Convert
.ToInt64 (key
)));
3698 // iteratively merge the blocks while they are at least half full
3699 // there's probably a really cool way to do this with a tree...
3700 while (rgKeyBlocks
.Count
> 1)
3702 ArrayList rgKeyBlocksNew
= new ArrayList ();
3703 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
3704 for (int ikb
= 1; ikb
< rgKeyBlocks
.Count
; ikb
++)
3706 KeyBlock kb
= (KeyBlock
) rgKeyBlocks
[ikb
];
3707 if ((kbCurr
.Length
+ kb
.Length
) * 2 >= KeyBlock
.TotalLength (kbCurr
, kb
))
3710 kbCurr
.nLast
= kb
.nLast
;
3714 // start a new block
3715 rgKeyBlocksNew
.Add (kbCurr
);
3719 rgKeyBlocksNew
.Add (kbCurr
);
3720 if (rgKeyBlocks
.Count
== rgKeyBlocksNew
.Count
)
3722 rgKeyBlocks
= rgKeyBlocksNew
;
3725 // initialize the key lists
3726 foreach (KeyBlock kb
in rgKeyBlocks
)
3727 kb
.rgKeys
= new ArrayList ();
3729 // fill the key lists
3731 if (rgKeyBlocks
.Count
> 0) {
3732 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
3733 foreach (object key
in rgKeys
)
3735 bool fNextBlock
= (key
is UInt64
) ? (ulong) key
> (ulong) kbCurr
.nLast
: Convert
.ToInt64 (key
) > kbCurr
.nLast
;
3737 kbCurr
= (KeyBlock
) rgKeyBlocks
[++iBlockCurr
];
3738 kbCurr
.rgKeys
.Add (key
);
3742 // sort the blocks so we can tackle the largest ones first
3743 rgKeyBlocks
.Sort ();
3745 // okay now we can start...
3746 ILGenerator ig
= ec
.ig
;
3747 Label lblEnd
= ig
.DefineLabel (); // at the end ;-)
3748 Label lblDefault
= ig
.DefineLabel ();
3750 Type typeKeys
= null;
3751 if (rgKeys
.Length
> 0)
3752 typeKeys
= rgKeys
[0].GetType (); // used for conversions
3754 for (int iBlock
= rgKeyBlocks
.Count
- 1; iBlock
>= 0; --iBlock
)
3756 KeyBlock kb
= ((KeyBlock
) rgKeyBlocks
[iBlock
]);
3757 lblDefault
= (iBlock
== 0) ? DefaultTarget
: ig
.DefineLabel ();
3760 foreach (object key
in kb
.rgKeys
)
3762 ig
.Emit (OpCodes
.Ldloc
, val
);
3763 EmitObjectInteger (ig
, key
);
3764 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3765 ig
.Emit (OpCodes
.Beq
, sl
.ILLabel
);
3770 // TODO: if all the keys in the block are the same and there are
3771 // no gaps/defaults then just use a range-check.
3772 if (SwitchType
== TypeManager
.int64_type
||
3773 SwitchType
== TypeManager
.uint64_type
)
3775 // TODO: optimize constant/I4 cases
3777 // check block range (could be > 2^31)
3778 ig
.Emit (OpCodes
.Ldloc
, val
);
3779 EmitObjectInteger (ig
, Convert
.ChangeType (kb
.nFirst
, typeKeys
));
3780 ig
.Emit (OpCodes
.Blt
, lblDefault
);
3781 ig
.Emit (OpCodes
.Ldloc
, val
);
3782 EmitObjectInteger (ig
, Convert
.ChangeType (kb
.nFirst
, typeKeys
));
3783 ig
.Emit (OpCodes
.Bgt
, lblDefault
);
3786 ig
.Emit (OpCodes
.Ldloc
, val
);
3789 EmitObjectInteger (ig
, Convert
.ChangeType (kb
.nFirst
, typeKeys
));
3790 ig
.Emit (OpCodes
.Sub
);
3792 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
3797 ig
.Emit (OpCodes
.Ldloc
, val
);
3798 int nFirst
= (int) kb
.nFirst
;
3801 IntConstant
.EmitInt (ig
, nFirst
);
3802 ig
.Emit (OpCodes
.Sub
);
3804 else if (nFirst
< 0)
3806 IntConstant
.EmitInt (ig
, -nFirst
);
3807 ig
.Emit (OpCodes
.Add
);
3811 // first, build the list of labels for the switch
3813 int cJumps
= kb
.Length
;
3814 Label
[] rgLabels
= new Label
[cJumps
];
3815 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
3817 object key
= kb
.rgKeys
[iKey
];
3818 if (Convert
.ToInt64 (key
) == kb
.nFirst
+ iJump
)
3820 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
3821 rgLabels
[iJump
] = sl
.ILLabel
;
3825 rgLabels
[iJump
] = lblDefault
;
3827 // emit the switch opcode
3828 ig
.Emit (OpCodes
.Switch
, rgLabels
);
3831 // mark the default for this block
3833 ig
.MarkLabel (lblDefault
);
3836 // TODO: find the default case and emit it here,
3837 // to prevent having to do the following jump.
3838 // make sure to mark other labels in the default section
3840 // the last default just goes to the end
3841 ig
.Emit (OpCodes
.Br
, lblDefault
);
3843 // now emit the code for the sections
3844 bool fFoundDefault
= false;
3845 bool fAllReturn
= true;
3846 foreach (SwitchSection ss
in Sections
)
3848 foreach (SwitchLabel sl
in ss
.Labels
)
3850 ig
.MarkLabel (sl
.ILLabel
);
3851 ig
.MarkLabel (sl
.ILLabelCode
);
3852 if (sl
.Label
== null)
3854 ig
.MarkLabel (lblDefault
);
3855 fFoundDefault
= true;
3858 bool returns
= ss
.Block
.Emit (ec
);
3859 fAllReturn
&= returns
;
3860 //ig.Emit (OpCodes.Br, lblEnd);
3863 if (!fFoundDefault
) {
3864 ig
.MarkLabel (lblDefault
);
3867 ig
.MarkLabel (lblEnd
);
3872 // This simple emit switch works, but does not take advantage of the
3874 // TODO: remove non-string logic from here
3875 // TODO: binary search strings?
3877 bool SimpleSwitchEmit (EmitContext ec
, LocalBuilder val
)
3879 ILGenerator ig
= ec
.ig
;
3880 Label end_of_switch
= ig
.DefineLabel ();
3881 Label next_test
= ig
.DefineLabel ();
3882 Label null_target
= ig
.DefineLabel ();
3883 bool default_found
= false;
3884 bool first_test
= true;
3885 bool pending_goto_end
= false;
3886 bool all_return
= true;
3887 bool is_string
= false;
3891 // Special processing for strings: we cant compare
3894 if (SwitchType
== TypeManager
.string_type
){
3895 ig
.Emit (OpCodes
.Ldloc
, val
);
3898 if (Elements
.Contains (NullLiteral
.Null
)){
3899 ig
.Emit (OpCodes
.Brfalse
, null_target
);
3901 ig
.Emit (OpCodes
.Brfalse
, default_target
);
3903 ig
.Emit (OpCodes
.Ldloc
, val
);
3904 ig
.Emit (OpCodes
.Call
, TypeManager
.string_isinterneted_string
);
3905 ig
.Emit (OpCodes
.Stloc
, val
);
3908 foreach (SwitchSection ss
in Sections
){
3909 Label sec_begin
= ig
.DefineLabel ();
3911 if (pending_goto_end
)
3912 ig
.Emit (OpCodes
.Br
, end_of_switch
);
3914 int label_count
= ss
.Labels
.Count
;
3916 foreach (SwitchLabel sl
in ss
.Labels
){
3917 ig
.MarkLabel (sl
.ILLabel
);
3920 ig
.MarkLabel (next_test
);
3921 next_test
= ig
.DefineLabel ();
3924 // If we are the default target
3926 if (sl
.Label
== null){
3927 ig
.MarkLabel (default_target
);
3928 default_found
= true;
3930 object lit
= sl
.Converted
;
3932 if (lit
is NullLiteral
){
3934 if (label_count
== 1)
3935 ig
.Emit (OpCodes
.Br
, next_test
);
3940 StringConstant str
= (StringConstant
) lit
;
3942 ig
.Emit (OpCodes
.Ldloc
, val
);
3943 ig
.Emit (OpCodes
.Ldstr
, str
.Value
);
3944 if (label_count
== 1)
3945 ig
.Emit (OpCodes
.Bne_Un
, next_test
);
3947 ig
.Emit (OpCodes
.Beq
, sec_begin
);
3949 ig
.Emit (OpCodes
.Ldloc
, val
);
3950 EmitObjectInteger (ig
, lit
);
3951 ig
.Emit (OpCodes
.Ceq
);
3952 if (label_count
== 1)
3953 ig
.Emit (OpCodes
.Brfalse
, next_test
);
3955 ig
.Emit (OpCodes
.Brtrue
, sec_begin
);
3959 if (label_count
!= 1)
3960 ig
.Emit (OpCodes
.Br
, next_test
);
3963 ig
.MarkLabel (null_target
);
3964 ig
.MarkLabel (sec_begin
);
3965 foreach (SwitchLabel sl
in ss
.Labels
)
3966 ig
.MarkLabel (sl
.ILLabelCode
);
3968 bool returns
= ss
.Block
.Emit (ec
);
3970 pending_goto_end
= false;
3973 pending_goto_end
= true;
3977 if (!default_found
){
3978 ig
.MarkLabel (default_target
);
3981 ig
.MarkLabel (next_test
);
3982 ig
.MarkLabel (end_of_switch
);
3987 public override bool Resolve (EmitContext ec
)
3989 Expr
= Expr
.Resolve (ec
);
3993 new_expr
= SwitchGoverningType (ec
, Expr
.Type
);
3994 if (new_expr
== null){
3995 Report
.Error (151, loc
, "An integer type or string was expected for switch");
4000 SwitchType
= new_expr
.Type
;
4002 if (!CheckSwitch (ec
))
4005 Switch old_switch
= ec
.Switch
;
4007 ec
.Switch
.SwitchType
= SwitchType
;
4009 ec
.StartFlowBranching (FlowBranchingType
.SWITCH
, loc
);
4012 foreach (SwitchSection ss
in Sections
){
4014 ec
.CurrentBranching
.CreateSibling ();
4018 if (ss
.Block
.Resolve (ec
) != true)
4024 ec
.CurrentBranching
.CreateSibling ();
4026 ec
.EndFlowBranching ();
4027 ec
.Switch
= old_switch
;
4032 protected override bool DoEmit (EmitContext ec
)
4034 // Store variable for comparission purposes
4035 LocalBuilder
value = ec
.ig
.DeclareLocal (SwitchType
);
4037 ec
.ig
.Emit (OpCodes
.Stloc
, value);
4039 ILGenerator ig
= ec
.ig
;
4041 default_target
= ig
.DefineLabel ();
4044 // Setup the codegen context
4046 Label old_end
= ec
.LoopEnd
;
4047 Switch old_switch
= ec
.Switch
;
4049 ec
.LoopEnd
= ig
.DefineLabel ();
4054 if (SwitchType
== TypeManager
.string_type
)
4055 all_return
= SimpleSwitchEmit (ec
, value);
4057 all_return
= TableSwitchEmit (ec
, value);
4059 // Restore context state.
4060 ig
.MarkLabel (ec
.LoopEnd
);
4063 // Restore the previous context
4065 ec
.LoopEnd
= old_end
;
4066 ec
.Switch
= old_switch
;
4072 public class Lock
: Statement
{
4074 Statement Statement
;
4076 public Lock (Expression expr
, Statement stmt
, Location l
)
4083 public override bool Resolve (EmitContext ec
)
4085 expr
= expr
.Resolve (ec
);
4086 return Statement
.Resolve (ec
) && expr
!= null;
4089 protected override bool DoEmit (EmitContext ec
)
4091 Type type
= expr
.Type
;
4094 if (type
.IsValueType
){
4095 Report
.Error (185, loc
, "lock statement requires the expression to be " +
4096 " a reference type (type is: `" +
4097 TypeManager
.MonoBASIC_Name (type
) + "'");
4101 ILGenerator ig
= ec
.ig
;
4102 LocalBuilder temp
= ig
.DeclareLocal (type
);
4105 ig
.Emit (OpCodes
.Dup
);
4106 ig
.Emit (OpCodes
.Stloc
, temp
);
4107 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
4110 Label end
= ig
.BeginExceptionBlock ();
4111 bool old_in_try
= ec
.InTry
;
4113 Label finish
= ig
.DefineLabel ();
4114 val
= Statement
.Emit (ec
);
4115 ec
.InTry
= old_in_try
;
4116 // ig.Emit (OpCodes.Leave, finish);
4118 ig
.MarkLabel (finish
);
4121 ig
.BeginFinallyBlock ();
4122 ig
.Emit (OpCodes
.Ldloc
, temp
);
4123 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
4124 ig
.EndExceptionBlock ();
4130 public class Unchecked
: Statement
{
4131 public readonly Block Block
;
4133 public Unchecked (Block b
)
4138 public override bool Resolve (EmitContext ec
)
4140 return Block
.Resolve (ec
);
4143 protected override bool DoEmit (EmitContext ec
)
4145 bool previous_state
= ec
.CheckState
;
4146 bool previous_state_const
= ec
.ConstantCheckState
;
4149 ec
.CheckState
= false;
4150 ec
.ConstantCheckState
= false;
4151 val
= Block
.Emit (ec
);
4152 ec
.CheckState
= previous_state
;
4153 ec
.ConstantCheckState
= previous_state_const
;
4159 public class Checked
: Statement
{
4160 public readonly Block Block
;
4162 public Checked (Block b
)
4167 public override bool Resolve (EmitContext ec
)
4169 bool previous_state
= ec
.CheckState
;
4170 bool previous_state_const
= ec
.ConstantCheckState
;
4172 ec
.CheckState
= true;
4173 ec
.ConstantCheckState
= true;
4174 bool ret
= Block
.Resolve (ec
);
4175 ec
.CheckState
= previous_state
;
4176 ec
.ConstantCheckState
= previous_state_const
;
4181 protected override bool DoEmit (EmitContext ec
)
4183 bool previous_state
= ec
.CheckState
;
4184 bool previous_state_const
= ec
.ConstantCheckState
;
4187 ec
.CheckState
= true;
4188 ec
.ConstantCheckState
= true;
4189 val
= Block
.Emit (ec
);
4190 ec
.CheckState
= previous_state
;
4191 ec
.ConstantCheckState
= previous_state_const
;
4197 public class Unsafe
: Statement
{
4198 public readonly Block Block
;
4200 public Unsafe (Block b
)
4205 public override bool Resolve (EmitContext ec
)
4207 bool previous_state
= ec
.InUnsafe
;
4211 val
= Block
.Resolve (ec
);
4212 ec
.InUnsafe
= previous_state
;
4217 protected override bool DoEmit (EmitContext ec
)
4219 bool previous_state
= ec
.InUnsafe
;
4223 val
= Block
.Emit (ec
);
4224 ec
.InUnsafe
= previous_state
;
4233 public class Fixed
: Statement
{
4235 ArrayList declarators
;
4236 Statement statement
;
4241 public bool is_object
;
4242 public VariableInfo vi
;
4243 public Expression expr
;
4244 public Expression converted
;
4247 public Fixed (Expression type
, ArrayList decls
, Statement stmt
, Location l
)
4250 declarators
= decls
;
4255 public override bool Resolve (EmitContext ec
)
4257 expr_type
= ec
.DeclSpace
.ResolveType (type
, false, loc
);
4258 if (expr_type
== null)
4261 data
= new FixedData
[declarators
.Count
];
4264 foreach (Pair p
in declarators
){
4265 VariableInfo vi
= (VariableInfo
) p
.First
;
4266 Expression e
= (Expression
) p
.Second
;
4271 // The rules for the possible declarators are pretty wise,
4272 // but the production on the grammar is more concise.
4274 // So we have to enforce these rules here.
4276 // We do not resolve before doing the case 1 test,
4277 // because the grammar is explicit in that the token &
4278 // is present, so we need to test for this particular case.
4282 // Case 1: & object.
4284 if (e
is Unary
&& ((Unary
) e
).Oper
== Unary
.Operator
.AddressOf
){
4285 Expression child
= ((Unary
) e
).Expr
;
4288 if (child
is ParameterReference
|| child
is LocalVariableReference
){
4291 "No need to use fixed statement for parameters or " +
4292 "local variable declarations (address is already " +
4301 child
= ((Unary
) e
).Expr
;
4303 if (!TypeManager
.VerifyUnManaged (child
.Type
, loc
))
4306 data
[i
].is_object
= true;
4308 data
[i
].converted
= null;
4322 if (e
.Type
.IsArray
){
4323 Type array_type
= e
.Type
.GetElementType ();
4327 // Provided that array_type is unmanaged,
4329 if (!TypeManager
.VerifyUnManaged (array_type
, loc
))
4333 // and T* is implicitly convertible to the
4334 // pointer type given in the fixed statement.
4336 ArrayPtr array_ptr
= new ArrayPtr (e
, loc
);
4338 Expression converted
= Expression
.ConvertImplicitRequired (
4339 ec
, array_ptr
, vi
.VariableType
, loc
);
4340 if (converted
== null)
4343 data
[i
].is_object
= false;
4345 data
[i
].converted
= converted
;
4355 if (e
.Type
== TypeManager
.string_type
){
4356 data
[i
].is_object
= false;
4358 data
[i
].converted
= null;
4364 return statement
.Resolve (ec
);
4367 protected override bool DoEmit (EmitContext ec
)
4369 ILGenerator ig
= ec
.ig
;
4371 bool is_ret
= false;
4373 for (int i
= 0; i
< data
.Length
; i
++) {
4374 VariableInfo vi
= data
[i
].vi
;
4377 // Case 1: & object.
4379 if (data
[i
].is_object
) {
4381 // Store pointer in pinned location
4383 data
[i
].expr
.Emit (ec
);
4384 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4386 is_ret
= statement
.Emit (ec
);
4388 // Clear the pinned variable.
4389 ig
.Emit (OpCodes
.Ldc_I4_0
);
4390 ig
.Emit (OpCodes
.Conv_U
);
4391 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4399 if (data
[i
].expr
.Type
.IsArray
){
4401 // Store pointer in pinned location
4403 data
[i
].converted
.Emit (ec
);
4405 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4407 is_ret
= statement
.Emit (ec
);
4409 // Clear the pinned variable.
4410 ig
.Emit (OpCodes
.Ldc_I4_0
);
4411 ig
.Emit (OpCodes
.Conv_U
);
4412 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4420 if (data
[i
].expr
.Type
== TypeManager
.string_type
){
4421 LocalBuilder pinned_string
= ig
.DeclareLocal (TypeManager
.string_type
);
4422 TypeManager
.MakePinned (pinned_string
);
4424 data
[i
].expr
.Emit (ec
);
4425 ig
.Emit (OpCodes
.Stloc
, pinned_string
);
4427 Expression sptr
= new StringPtr (pinned_string
, loc
);
4428 Expression converted
= Expression
.ConvertImplicitRequired (
4429 ec
, sptr
, vi
.VariableType
, loc
);
4431 if (converted
== null)
4434 converted
.Emit (ec
);
4435 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4437 is_ret
= statement
.Emit (ec
);
4439 // Clear the pinned variable
4440 ig
.Emit (OpCodes
.Ldnull
);
4441 ig
.Emit (OpCodes
.Stloc
, pinned_string
);
4449 public class Catch
{
4450 public readonly string Name
;
4451 public readonly Block Block
;
4452 public Expression Clause
;
4453 public readonly Location Location
;
4455 Expression type_expr
;
4456 //Expression clus_expr;
4459 public Catch (Expression type
, string name
, Block block
, Expression clause
, Location l
)
4468 public Type CatchType
{
4474 public bool IsGeneral
{
4476 return type_expr
== null;
4480 public bool Resolve (EmitContext ec
)
4482 if (type_expr
!= null) {
4483 type
= ec
.DeclSpace
.ResolveType (type_expr
, false, Location
);
4487 if (type
!= TypeManager
.exception_type
&& !type
.IsSubclassOf (TypeManager
.exception_type
)){
4488 Report
.Error (30665, Location
,
4489 "The type caught or thrown must be derived " +
4490 "from System.Exception");
4496 if (Clause
!= null) {
4497 Clause
= Statement
.ResolveBoolean (ec
, Clause
, Location
);
4498 if (Clause
== null) {
4503 if (!Block
.Resolve (ec
))
4510 public class Try
: Statement
{
4511 public readonly Block Fini
, Block
;
4512 public readonly ArrayList Specific
;
4513 public readonly Catch General
;
4516 // specific, general and fini might all be null.
4518 public Try (Block block
, ArrayList specific
, Catch general
, Block fini
, Location l
)
4520 if (specific
== null && general
== null){
4521 Console
.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4525 this.Specific
= specific
;
4526 this.General
= general
;
4531 public override bool Resolve (EmitContext ec
)
4535 ec
.StartFlowBranching (FlowBranchingType
.EXCEPTION
, Block
.StartLocation
);
4537 Report
.Debug (1, "START OF TRY BLOCK", Block
.StartLocation
);
4539 bool old_in_try
= ec
.InTry
;
4542 if (!Block
.Resolve (ec
))
4545 ec
.InTry
= old_in_try
;
4547 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
4549 Report
.Debug (1, "START OF CATCH BLOCKS", vector
);
4551 foreach (Catch c
in Specific
){
4552 ec
.CurrentBranching
.CreateSibling ();
4553 Report
.Debug (1, "STARTED SIBLING FOR CATCH", ec
.CurrentBranching
);
4555 if (c
.Name
!= null) {
4556 VariableInfo vi
= c
.Block
.GetVariableInfo (c
.Name
);
4558 throw new Exception ();
4563 bool old_in_catch
= ec
.InCatch
;
4566 if (!c
.Resolve (ec
))
4569 ec
.InCatch
= old_in_catch
;
4571 FlowBranching
.UsageVector current
= ec
.CurrentBranching
.CurrentUsageVector
;
4573 if (!current
.AlwaysReturns
&& !current
.AlwaysBreaks
)
4574 vector
.AndLocals (current
);
4577 Report
.Debug (1, "END OF CATCH BLOCKS", ec
.CurrentBranching
);
4579 if (General
!= null){
4580 ec
.CurrentBranching
.CreateSibling ();
4581 Report
.Debug (1, "STARTED SIBLING FOR GENERAL", ec
.CurrentBranching
);
4583 bool old_in_catch
= ec
.InCatch
;
4586 if (!General
.Resolve (ec
))
4589 ec
.InCatch
= old_in_catch
;
4591 FlowBranching
.UsageVector current
= ec
.CurrentBranching
.CurrentUsageVector
;
4593 if (!current
.AlwaysReturns
&& !current
.AlwaysBreaks
)
4594 vector
.AndLocals (current
);
4597 Report
.Debug (1, "END OF GENERAL CATCH BLOCKS", ec
.CurrentBranching
);
4600 ec
.CurrentBranching
.CreateSiblingForFinally ();
4601 Report
.Debug (1, "STARTED SIBLING FOR FINALLY", ec
.CurrentBranching
, vector
);
4603 bool old_in_finally
= ec
.InFinally
;
4604 ec
.InFinally
= true;
4606 if (!Fini
.Resolve (ec
))
4609 ec
.InFinally
= old_in_finally
;
4612 FlowReturns returns
= ec
.EndFlowBranching ();
4614 FlowBranching
.UsageVector f_vector
= ec
.CurrentBranching
.CurrentUsageVector
;
4616 Report
.Debug (1, "END OF FINALLY", ec
.CurrentBranching
, returns
, vector
, f_vector
);
4618 if ((returns
== FlowReturns
.SOMETIMES
) || (returns
== FlowReturns
.ALWAYS
)) {
4619 ec
.CurrentBranching
.CheckOutParameters (f_vector
.Parameters
, loc
);
4622 ec
.CurrentBranching
.CurrentUsageVector
.Or (vector
);
4624 Report
.Debug (1, "END OF TRY", ec
.CurrentBranching
);
4629 protected override bool DoEmit (EmitContext ec
)
4631 ILGenerator ig
= ec
.ig
;
4633 Label finish
= ig
.DefineLabel ();;
4637 end
= ig
.BeginExceptionBlock ();
4638 bool old_in_try
= ec
.InTry
;
4640 returns
= Block
.Emit (ec
);
4641 ec
.InTry
= old_in_try
;
4644 // System.Reflection.Emit provides this automatically:
4645 // ig.Emit (OpCodes.Leave, finish);
4647 bool old_in_catch
= ec
.InCatch
;
4649 DeclSpace ds
= ec
.DeclSpace
;
4651 foreach (Catch c
in Specific
){
4654 ig
.BeginCatchBlock (c
.CatchType
);
4656 if (c
.Name
!= null){
4657 vi
= c
.Block
.GetVariableInfo (c
.Name
);
4659 throw new Exception ("Variable does not exist in this block");
4661 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
4663 ig
.Emit (OpCodes
.Pop
);
4666 // if when clause is there
4668 if (c
.Clause
!= null) {
4669 if (c
.Clause
is BoolConstant
) {
4670 bool take
= ((BoolConstant
) c
.Clause
).Value
;
4673 if (!c
.Block
.Emit (ec
))
4676 EmitBoolExpression (ec
, c
.Clause
, finish
, false);
4677 if (!c
.Block
.Emit (ec
))
4681 if (!c
.Block
.Emit (ec
))
4685 if (General
!= null){
4686 ig
.BeginCatchBlock (TypeManager
.object_type
);
4687 ig
.Emit (OpCodes
.Pop
);
4689 if (General
.Clause
!= null) {
4690 if (General
.Clause
is BoolConstant
) {
4691 bool take
= ((BoolConstant
) General
.Clause
).Value
;
4693 if (!General
.Block
.Emit (ec
))
4696 EmitBoolExpression (ec
, General
.Clause
, finish
, false);
4697 if (!General
.Block
.Emit (ec
))
4701 if (!General
.Block
.Emit (ec
))
4705 ec
.InCatch
= old_in_catch
;
4707 ig
.MarkLabel (finish
);
4709 ig
.BeginFinallyBlock ();
4710 bool old_in_finally
= ec
.InFinally
;
4711 ec
.InFinally
= true;
4713 ec
.InFinally
= old_in_finally
;
4716 ig
.EndExceptionBlock ();
4719 if (!returns
|| ec
.InTry
|| ec
.InCatch
)
4722 // Unfortunately, System.Reflection.Emit automatically emits a leave
4723 // to the end of the finally block. This is a problem if `returns'
4724 // is true since we may jump to a point after the end of the method.
4725 // As a workaround, emit an explicit ret here.
4727 if (ec
.ReturnType
!= null)
4728 ec
.ig
.Emit (OpCodes
.Ldloc
, ec
.TemporaryReturn ());
4729 ec
.ig
.Emit (OpCodes
.Ret
);
4735 public class Using
: Statement
{
4736 object expression_or_block
;
4737 Statement Statement
;
4742 Expression
[] converted_vars
;
4743 ExpressionStatement
[] assign
;
4745 public Using (object expression_or_block
, Statement stmt
, Location l
)
4747 this.expression_or_block
= expression_or_block
;
4753 // Resolves for the case of using using a local variable declaration.
4755 bool ResolveLocalVariableDecls (EmitContext ec
)
4757 bool need_conv
= false;
4758 expr_type
= ec
.DeclSpace
.ResolveType (expr
, false, loc
);
4761 if (expr_type
== null)
4765 // The type must be an IDisposable or an implicit conversion
4768 converted_vars
= new Expression
[var_list
.Count
];
4769 assign
= new ExpressionStatement
[var_list
.Count
];
4770 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
)){
4771 foreach (DictionaryEntry e
in var_list
){
4772 Expression
var = (Expression
) e
.Key
;
4774 var = var.ResolveLValue (ec
, new EmptyExpression ());
4778 converted_vars
[i
] = Expression
.ConvertImplicitRequired (
4779 ec
, var, TypeManager
.idisposable_type
, loc
);
4781 if (converted_vars
[i
] == null)
4789 foreach (DictionaryEntry e
in var_list
){
4790 LocalVariableReference
var = (LocalVariableReference
) e
.Key
;
4791 Expression new_expr
= (Expression
) e
.Value
;
4794 a
= new Assign (var, new_expr
, loc
);
4800 converted_vars
[i
] = var;
4801 assign
[i
] = (ExpressionStatement
) a
;
4808 bool ResolveExpression (EmitContext ec
)
4810 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
)){
4811 conv
= Expression
.ConvertImplicitRequired (
4812 ec
, expr
, TypeManager
.idisposable_type
, loc
);
4822 // Emits the code for the case of using using a local variable declaration.
4824 bool EmitLocalVariableDecls (EmitContext ec
)
4826 ILGenerator ig
= ec
.ig
;
4829 bool old_in_try
= ec
.InTry
;
4831 for (i
= 0; i
< assign
.Length
; i
++) {
4832 assign
[i
].EmitStatement (ec
);
4834 ig
.BeginExceptionBlock ();
4836 Statement
.Emit (ec
);
4837 ec
.InTry
= old_in_try
;
4839 bool old_in_finally
= ec
.InFinally
;
4840 ec
.InFinally
= true;
4841 var_list
.Reverse ();
4842 foreach (DictionaryEntry e
in var_list
){
4843 LocalVariableReference
var = (LocalVariableReference
) e
.Key
;
4844 Label skip
= ig
.DefineLabel ();
4847 ig
.BeginFinallyBlock ();
4850 ig
.Emit (OpCodes
.Brfalse
, skip
);
4851 converted_vars
[i
].Emit (ec
);
4852 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4853 ig
.MarkLabel (skip
);
4854 ig
.EndExceptionBlock ();
4856 ec
.InFinally
= old_in_finally
;
4861 bool EmitExpression (EmitContext ec
)
4864 // Make a copy of the expression and operate on that.
4866 ILGenerator ig
= ec
.ig
;
4867 LocalBuilder local_copy
= ig
.DeclareLocal (expr_type
);
4872 ig
.Emit (OpCodes
.Stloc
, local_copy
);
4874 bool old_in_try
= ec
.InTry
;
4876 ig
.BeginExceptionBlock ();
4877 Statement
.Emit (ec
);
4878 ec
.InTry
= old_in_try
;
4880 Label skip
= ig
.DefineLabel ();
4881 bool old_in_finally
= ec
.InFinally
;
4882 ig
.BeginFinallyBlock ();
4883 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
4884 ig
.Emit (OpCodes
.Brfalse
, skip
);
4885 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
4886 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4887 ig
.MarkLabel (skip
);
4888 ec
.InFinally
= old_in_finally
;
4889 ig
.EndExceptionBlock ();
4894 public override bool Resolve (EmitContext ec
)
4896 if (expression_or_block
is DictionaryEntry
){
4897 expr
= (Expression
) ((DictionaryEntry
) expression_or_block
).Key
;
4898 var_list
= (ArrayList
)((DictionaryEntry
)expression_or_block
).Value
;
4900 if (!ResolveLocalVariableDecls (ec
))
4903 } else if (expression_or_block
is Expression
){
4904 expr
= (Expression
) expression_or_block
;
4906 expr
= expr
.Resolve (ec
);
4910 expr_type
= expr
.Type
;
4912 if (!ResolveExpression (ec
))
4916 return Statement
.Resolve (ec
);
4919 protected override bool DoEmit (EmitContext ec
)
4921 if (expression_or_block
is DictionaryEntry
)
4922 return EmitLocalVariableDecls (ec
);
4923 else if (expression_or_block
is Expression
)
4924 return EmitExpression (ec
);
4931 /// Implementation of the foreach C# statement
4933 public class Foreach
: Statement
{
4935 LocalVariableReference variable
;
4937 Statement statement
;
4938 ForeachHelperMethods hm
;
4939 Expression empty
, conv
;
4940 Type array_type
, element_type
;
4943 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
4944 Statement stmt
, Location l
)
4951 VariableInfo vi
= var.VariableInfo
;
4952 this.type
= vi
.Type
;
4954 this.variable
= var;
4960 public override bool Resolve (EmitContext ec
)
4962 expr
= expr
.Resolve (ec
);
4966 var_type
= ec
.DeclSpace
.ResolveType (type
, false, loc
);
4967 if (var_type
== null)
4971 // We need an instance variable. Not sure this is the best
4972 // way of doing this.
4974 // FIXME: When we implement propertyaccess, will those turn
4975 // out to return values in ExprClass? I think they should.
4977 if (!(expr
.eclass
== ExprClass
.Variable
|| expr
.eclass
== ExprClass
.Value
||
4978 expr
.eclass
== ExprClass
.PropertyAccess
|| expr
.eclass
== ExprClass
.IndexerAccess
)){
4979 error1579 (expr
.Type
);
4983 if (expr
.Type
.IsArray
) {
4984 array_type
= expr
.Type
;
4985 element_type
= array_type
.GetElementType ();
4987 empty
= new EmptyExpression (element_type
);
4989 hm
= ProbeCollectionType (ec
, expr
.Type
);
4991 error1579 (expr
.Type
);
4995 array_type
= expr
.Type
;
4996 element_type
= hm
.element_type
;
4998 empty
= new EmptyExpression (hm
.element_type
);
5001 ec
.StartFlowBranching (FlowBranchingType
.LOOP_BLOCK
, loc
);
5002 ec
.CurrentBranching
.CreateSibling ();
5006 // FIXME: maybe we can apply the same trick we do in the
5007 // array handling to avoid creating empty and conv in some cases.
5009 // Although it is not as important in this case, as the type
5010 // will not likely be object (what the enumerator will return).
5012 conv
= Expression
.ConvertExplicit (ec
, empty
, var_type
, false, loc
);
5016 if (variable
.ResolveLValue (ec
, empty
) == null)
5019 if (!statement
.Resolve (ec
))
5022 FlowReturns returns
= ec
.EndFlowBranching ();
5028 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5030 static MethodInfo
FetchMethodMoveNext (Type t
)
5032 MemberList move_next_list
;
5034 move_next_list
= TypeContainer
.FindMembers (
5035 t
, MemberTypes
.Method
,
5036 BindingFlags
.Public
| BindingFlags
.Instance
,
5037 Type
.FilterName
, "MoveNext");
5038 if (move_next_list
.Count
== 0)
5041 foreach (MemberInfo m
in move_next_list
){
5042 MethodInfo mi
= (MethodInfo
) m
;
5045 args
= TypeManager
.GetArgumentTypes (mi
);
5046 if (args
!= null && args
.Length
== 0){
5047 if (mi
.ReturnType
== TypeManager
.bool_type
)
5055 // Retrieves a `public T get_Current ()' method from the Type `t'
5057 static MethodInfo
FetchMethodGetCurrent (Type t
)
5059 MemberList move_next_list
;
5061 move_next_list
= TypeContainer
.FindMembers (
5062 t
, MemberTypes
.Method
,
5063 BindingFlags
.Public
| BindingFlags
.Instance
,
5064 Type
.FilterName
, "get_Current");
5065 if (move_next_list
.Count
== 0)
5068 foreach (MemberInfo m
in move_next_list
){
5069 MethodInfo mi
= (MethodInfo
) m
;
5072 args
= TypeManager
.GetArgumentTypes (mi
);
5073 if (args
!= null && args
.Length
== 0)
5080 // This struct records the helper methods used by the Foreach construct
5082 class ForeachHelperMethods
{
5083 public EmitContext ec
;
5084 public MethodInfo get_enumerator
;
5085 public MethodInfo move_next
;
5086 public MethodInfo get_current
;
5087 public Type element_type
;
5088 public Type enumerator_type
;
5089 public bool is_disposable
;
5091 public ForeachHelperMethods (EmitContext ec
)
5094 this.element_type
= TypeManager
.object_type
;
5095 this.enumerator_type
= TypeManager
.ienumerator_type
;
5096 this.is_disposable
= true;
5100 static bool GetEnumeratorFilter (MemberInfo m
, object criteria
)
5105 if (!(m
is MethodInfo
))
5108 if (m
.Name
!= "GetEnumerator")
5111 MethodInfo mi
= (MethodInfo
) m
;
5112 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
5114 if (args
.Length
!= 0)
5117 ForeachHelperMethods hm
= (ForeachHelperMethods
) criteria
;
5118 EmitContext ec
= hm
.ec
;
5121 // Check whether GetEnumerator is accessible to us
5123 MethodAttributes prot
= mi
.Attributes
& MethodAttributes
.MemberAccessMask
;
5125 Type declaring
= mi
.DeclaringType
;
5126 if (prot
== MethodAttributes
.Private
){
5127 if (declaring
!= ec
.ContainerType
)
5129 } else if (prot
== MethodAttributes
.FamANDAssem
){
5130 // If from a different assembly, false
5131 if (!(mi
is MethodBuilder
))
5134 // Are we being invoked from the same class, or from a derived method?
5136 if (ec
.ContainerType
!= declaring
){
5137 if (!ec
.ContainerType
.IsSubclassOf (declaring
))
5140 } else if (prot
== MethodAttributes
.FamORAssem
){
5141 if (!(mi
is MethodBuilder
||
5142 ec
.ContainerType
== declaring
||
5143 ec
.ContainerType
.IsSubclassOf (declaring
)))
5145 } if (prot
== MethodAttributes
.Family
){
5146 if (!(ec
.ContainerType
== declaring
||
5147 ec
.ContainerType
.IsSubclassOf (declaring
)))
5152 // Ok, we can access it, now make sure that we can do something
5153 // with this `GetEnumerator'
5156 if (mi
.ReturnType
== TypeManager
.ienumerator_type
||
5157 TypeManager
.ienumerator_type
.IsAssignableFrom (mi
.ReturnType
) ||
5158 (!RootContext
.StdLib
&& TypeManager
.ImplementsInterface (mi
.ReturnType
, TypeManager
.ienumerator_type
))) {
5159 hm
.move_next
= TypeManager
.bool_movenext_void
;
5160 hm
.get_current
= TypeManager
.object_getcurrent_void
;
5165 // Ok, so they dont return an IEnumerable, we will have to
5166 // find if they support the GetEnumerator pattern.
5168 Type return_type
= mi
.ReturnType
;
5170 hm
.move_next
= FetchMethodMoveNext (return_type
);
5171 if (hm
.move_next
== null)
5173 hm
.get_current
= FetchMethodGetCurrent (return_type
);
5174 if (hm
.get_current
== null)
5177 hm
.element_type
= hm
.get_current
.ReturnType
;
5178 hm
.enumerator_type
= return_type
;
5179 hm
.is_disposable
= TypeManager
.ImplementsInterface (
5180 hm
.enumerator_type
, TypeManager
.idisposable_type
);
5186 /// This filter is used to find the GetEnumerator method
5187 /// on which IEnumerator operates
5189 static MemberFilter FilterEnumerator
;
5193 FilterEnumerator
= new MemberFilter (GetEnumeratorFilter
);
5196 void error1579 (Type t
)
5198 Report
.Error (1579, loc
,
5199 "foreach statement cannot operate on variables of type `" +
5200 t
.FullName
+ "' because that class does not provide a " +
5201 " GetEnumerator method or it is inaccessible");
5204 static bool TryType (Type t
, ForeachHelperMethods hm
)
5208 mi
= TypeContainer
.FindMembers (t
, MemberTypes
.Method
,
5209 BindingFlags
.Public
| BindingFlags
.NonPublic
|
5210 BindingFlags
.Instance
,
5211 FilterEnumerator
, hm
);
5216 hm
.get_enumerator
= (MethodInfo
) mi
[0];
5221 // Looks for a usable GetEnumerator in the Type, and if found returns
5222 // the three methods that participate: GetEnumerator, MoveNext and get_Current
5224 ForeachHelperMethods
ProbeCollectionType (EmitContext ec
, Type t
)
5226 ForeachHelperMethods hm
= new ForeachHelperMethods (ec
);
5228 if (TryType (t
, hm
))
5232 // Now try to find the method in the interfaces
5235 Type
[] ifaces
= t
.GetInterfaces ();
5237 foreach (Type i
in ifaces
){
5238 if (TryType (i
, hm
))
5243 // Since TypeBuilder.GetInterfaces only returns the interface
5244 // types for this type, we have to keep looping, but once
5245 // we hit a non-TypeBuilder (ie, a Type), then we know we are
5246 // done, because it returns all the types
5248 if ((t
is TypeBuilder
))
5258 // FIXME: possible optimization.
5259 // We might be able to avoid creating `empty' if the type is the sam
5261 bool EmitCollectionForeach (EmitContext ec
)
5263 ILGenerator ig
= ec
.ig
;
5264 LocalBuilder enumerator
, disposable
;
5266 enumerator
= ig
.DeclareLocal (hm
.enumerator_type
);
5267 if (hm
.is_disposable
)
5268 disposable
= ig
.DeclareLocal (TypeManager
.idisposable_type
);
5273 // Instantiate the enumerator
5275 if (expr
.Type
.IsValueType
){
5276 if (expr
is IMemoryLocation
){
5277 IMemoryLocation ml
= (IMemoryLocation
) expr
;
5279 ml
.AddressOf (ec
, AddressOp
.Load
);
5281 throw new Exception ("Expr " + expr
+ " of type " + expr
.Type
+
5282 " does not implement IMemoryLocation");
5283 ig
.Emit (OpCodes
.Call
, hm
.get_enumerator
);
5286 ig
.Emit (OpCodes
.Callvirt
, hm
.get_enumerator
);
5288 ig
.Emit (OpCodes
.Stloc
, enumerator
);
5291 // Protect the code in a try/finalize block, so that
5292 // if the beast implement IDisposable, we get rid of it
5295 bool old_in_try
= ec
.InTry
;
5297 if (hm
.is_disposable
) {
5298 l
= ig
.BeginExceptionBlock ();
5302 Label end_try
= ig
.DefineLabel ();
5304 ig
.MarkLabel (ec
.LoopBegin
);
5305 ig
.Emit (OpCodes
.Ldloc
, enumerator
);
5306 ig
.Emit (OpCodes
.Callvirt
, hm
.move_next
);
5307 ig
.Emit (OpCodes
.Brfalse
, end_try
);
5308 ig
.Emit (OpCodes
.Ldloc
, enumerator
);
5309 ig
.Emit (OpCodes
.Callvirt
, hm
.get_current
);
5310 variable
.EmitAssign (ec
, conv
);
5311 statement
.Emit (ec
);
5312 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
5313 ig
.MarkLabel (end_try
);
5314 ec
.InTry
= old_in_try
;
5316 // The runtime provides this for us.
5317 // ig.Emit (OpCodes.Leave, end);
5320 // Now the finally block
5322 if (hm
.is_disposable
) {
5323 Label end_finally
= ig
.DefineLabel ();
5324 bool old_in_finally
= ec
.InFinally
;
5325 ec
.InFinally
= true;
5326 ig
.BeginFinallyBlock ();
5328 ig
.Emit (OpCodes
.Ldloc
, enumerator
);
5329 ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
5330 ig
.Emit (OpCodes
.Stloc
, disposable
);
5331 ig
.Emit (OpCodes
.Ldloc
, disposable
);
5332 ig
.Emit (OpCodes
.Brfalse
, end_finally
);
5333 ig
.Emit (OpCodes
.Ldloc
, disposable
);
5334 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
5335 ig
.MarkLabel (end_finally
);
5336 ec
.InFinally
= old_in_finally
;
5338 // The runtime generates this anyways.
5339 // ig.Emit (OpCodes.Endfinally);
5341 ig
.EndExceptionBlock ();
5344 ig
.MarkLabel (ec
.LoopEnd
);
5349 // FIXME: possible optimization.
5350 // We might be able to avoid creating `empty' if the type is the sam
5352 bool EmitArrayForeach (EmitContext ec
)
5354 int rank
= array_type
.GetArrayRank ();
5355 ILGenerator ig
= ec
.ig
;
5357 LocalBuilder copy
= ig
.DeclareLocal (array_type
);
5360 // Make our copy of the array
5363 ig
.Emit (OpCodes
.Stloc
, copy
);
5366 LocalBuilder counter
= ig
.DeclareLocal (TypeManager
.int32_type
);
5370 ig
.Emit (OpCodes
.Ldc_I4_0
);
5371 ig
.Emit (OpCodes
.Stloc
, counter
);
5372 test
= ig
.DefineLabel ();
5373 ig
.Emit (OpCodes
.Br
, test
);
5375 loop
= ig
.DefineLabel ();
5376 ig
.MarkLabel (loop
);
5378 ig
.Emit (OpCodes
.Ldloc
, copy
);
5379 ig
.Emit (OpCodes
.Ldloc
, counter
);
5380 ArrayAccess
.EmitLoadOpcode (ig
, var_type
);
5382 variable
.EmitAssign (ec
, conv
);
5384 statement
.Emit (ec
);
5386 ig
.MarkLabel (ec
.LoopBegin
);
5387 ig
.Emit (OpCodes
.Ldloc
, counter
);
5388 ig
.Emit (OpCodes
.Ldc_I4_1
);
5389 ig
.Emit (OpCodes
.Add
);
5390 ig
.Emit (OpCodes
.Stloc
, counter
);
5392 ig
.MarkLabel (test
);
5393 ig
.Emit (OpCodes
.Ldloc
, counter
);
5394 ig
.Emit (OpCodes
.Ldloc
, copy
);
5395 ig
.Emit (OpCodes
.Ldlen
);
5396 ig
.Emit (OpCodes
.Conv_I4
);
5397 ig
.Emit (OpCodes
.Blt
, loop
);
5399 LocalBuilder
[] dim_len
= new LocalBuilder
[rank
];
5400 LocalBuilder
[] dim_count
= new LocalBuilder
[rank
];
5401 Label
[] loop
= new Label
[rank
];
5402 Label
[] test
= new Label
[rank
];
5405 for (dim
= 0; dim
< rank
; dim
++){
5406 dim_len
[dim
] = ig
.DeclareLocal (TypeManager
.int32_type
);
5407 dim_count
[dim
] = ig
.DeclareLocal (TypeManager
.int32_type
);
5408 test
[dim
] = ig
.DefineLabel ();
5409 loop
[dim
] = ig
.DefineLabel ();
5412 for (dim
= 0; dim
< rank
; dim
++){
5413 ig
.Emit (OpCodes
.Ldloc
, copy
);
5414 IntLiteral
.EmitInt (ig
, dim
);
5415 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.int_getlength_int
);
5416 ig
.Emit (OpCodes
.Stloc
, dim_len
[dim
]);
5419 for (dim
= 0; dim
< rank
; dim
++){
5420 ig
.Emit (OpCodes
.Ldc_I4_0
);
5421 ig
.Emit (OpCodes
.Stloc
, dim_count
[dim
]);
5422 ig
.Emit (OpCodes
.Br
, test
[dim
]);
5423 ig
.MarkLabel (loop
[dim
]);
5426 ig
.Emit (OpCodes
.Ldloc
, copy
);
5427 for (dim
= 0; dim
< rank
; dim
++)
5428 ig
.Emit (OpCodes
.Ldloc
, dim_count
[dim
]);
5431 // FIXME: Maybe we can cache the computation of `get'?
5433 Type
[] args
= new Type
[rank
];
5436 for (int i
= 0; i
< rank
; i
++)
5437 args
[i
] = TypeManager
.int32_type
;
5439 ModuleBuilder mb
= CodeGen
.ModuleBuilder
;
5440 get = mb
.GetArrayMethod (
5442 CallingConventions
.HasThis
| CallingConventions
.Standard
,
5444 ig
.Emit (OpCodes
.Call
, get);
5445 variable
.EmitAssign (ec
, conv
);
5446 statement
.Emit (ec
);
5447 ig
.MarkLabel (ec
.LoopBegin
);
5448 for (dim
= rank
- 1; dim
>= 0; dim
--){
5449 ig
.Emit (OpCodes
.Ldloc
, dim_count
[dim
]);
5450 ig
.Emit (OpCodes
.Ldc_I4_1
);
5451 ig
.Emit (OpCodes
.Add
);
5452 ig
.Emit (OpCodes
.Stloc
, dim_count
[dim
]);
5454 ig
.MarkLabel (test
[dim
]);
5455 ig
.Emit (OpCodes
.Ldloc
, dim_count
[dim
]);
5456 ig
.Emit (OpCodes
.Ldloc
, dim_len
[dim
]);
5457 ig
.Emit (OpCodes
.Blt
, loop
[dim
]);
5460 ig
.MarkLabel (ec
.LoopEnd
);
5465 protected override bool DoEmit (EmitContext ec
)
5469 ILGenerator ig
= ec
.ig
;
5471 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
5472 bool old_inloop
= ec
.InLoop
;
5473 int old_loop_begin_try_catch_level
= ec
.LoopBeginTryCatchLevel
;
5474 ec
.LoopBegin
= ig
.DefineLabel ();
5475 ec
.LoopEnd
= ig
.DefineLabel ();
5477 ec
.LoopBeginTryCatchLevel
= ec
.TryCatchLevel
;
5480 ret_val
= EmitCollectionForeach (ec
);
5482 ret_val
= EmitArrayForeach (ec
);
5484 ec
.LoopBegin
= old_begin
;
5485 ec
.LoopEnd
= old_end
;
5486 ec
.InLoop
= old_inloop
;
5487 ec
.LoopBeginTryCatchLevel
= old_loop_begin_try_catch_level
;
5494 /// AddHandler statement
5496 public class AddHandler
: Statement
{
5498 Expression EvtHandler
;
5501 // keeps track whether EvtId is already resolved
5505 public AddHandler (Expression evt_id
, Expression evt_handler
, Location l
)
5508 EvtHandler
= evt_handler
;
5511 //Console.WriteLine ("Adding handler '" + evt_handler + "' for Event '" + evt_id +"'");
5514 public override bool Resolve (EmitContext ec
)
5517 // if EvetId is of EventExpr type that means
5518 // this is already resolved
5520 if (EvtId
is EventExpr
) {
5525 EvtId
= EvtId
.Resolve(ec
);
5526 EvtHandler
= EvtHandler
.Resolve(ec
,ResolveFlags
.MethodGroup
);
5527 if (EvtId
== null || (!(EvtId
is EventExpr
))) {
5528 Report
.Error (30676, "Need an event designator.");
5532 if (EvtHandler
== null)
5534 Report
.Error (999, "'AddHandler' statement needs an event handler.");
5541 protected override bool DoEmit (EmitContext ec
)
5544 // Already resolved and emitted don't do anything
5550 ArrayList args
= new ArrayList();
5551 Argument arg
= new Argument (EvtHandler
, Argument
.AType
.Expression
);
5556 // The even type was already resolved to a delegate, so
5557 // we must un-resolve its name to generate a type expression
5558 string ts
= (EvtId
.Type
.ToString()).Replace ('+','.');
5559 Expression dtype
= Mono
.MonoBASIC
.Parser
.DecomposeQI (ts
, Location
.Null
);
5561 // which we can use to declare a new event handler
5563 d
= new New (dtype
, args
, Location
.Null
);
5565 e
= new CompoundAssign(Binary
.Operator
.Addition
, EvtId
, d
, Location
.Null
);
5567 // we resolve it all and emit the code
5580 /// RemoveHandler statement
5582 public class RemoveHandler
: Statement
5585 Expression EvtHandler
;
5587 public RemoveHandler (Expression evt_id
, Expression evt_handler
, Location l
)
5590 EvtHandler
= evt_handler
;
5594 public override bool Resolve (EmitContext ec
)
5596 EvtId
= EvtId
.Resolve(ec
);
5597 EvtHandler
= EvtHandler
.Resolve(ec
,ResolveFlags
.MethodGroup
);
5598 if (EvtId
== null || (!(EvtId
is EventExpr
)))
5600 Report
.Error (30676, "Need an event designator.");
5604 if (EvtHandler
== null)
5606 Report
.Error (999, "'AddHandler' statement needs an event handler.");
5612 protected override bool DoEmit (EmitContext ec
)
5615 ArrayList args
= new ArrayList();
5616 Argument arg
= new Argument (EvtHandler
, Argument
.AType
.Expression
);
5619 // The even type was already resolved to a delegate, so
5620 // we must un-resolve its name to generate a type expression
5621 string ts
= (EvtId
.Type
.ToString()).Replace ('+','.');
5622 Expression dtype
= Mono
.MonoBASIC
.Parser
.DecomposeQI (ts
, Location
.Null
);
5624 // which we can use to declare a new event handler
5626 d
= new New (dtype
, args
, Location
.Null
);
5629 e
= new CompoundAssign(Binary
.Operator
.Subtraction
, EvtId
, d
, Location
.Null
);
5631 // we resolve it all and emit the code
5643 public class RedimClause
{
5644 public Expression Expr
;
5645 public ArrayList NewIndexes
;
5647 public RedimClause (Expression e
, ArrayList args
)
5654 public class ReDim
: Statement
{
5655 ArrayList RedimTargets
;
5659 private StatementExpression ReDimExpr
;
5661 public ReDim (ArrayList targets
, bool opt_preserve
, Location l
)
5664 RedimTargets
= targets
;
5665 Preserve
= opt_preserve
;
5668 public override bool Resolve (EmitContext ec
)
5670 Expression RedimTarget
;
5671 ArrayList NewIndexes
;
5673 foreach (RedimClause rc
in RedimTargets
) {
5674 RedimTarget
= rc
.Expr
;
5675 NewIndexes
= rc
.NewIndexes
;
5677 RedimTarget
= RedimTarget
.Resolve (ec
);
5678 if (!RedimTarget
.Type
.IsArray
)
5679 Report
.Error (49, "'ReDim' statement requires an array");
5681 ArrayList args
= new ArrayList();
5682 foreach (Argument a
in NewIndexes
) {
5683 if (a
.Resolve(ec
, loc
))
5687 for (int x
= 0; x
< args
.Count
; x
++) {
5688 args
[x
] = new Binary (Binary
.Operator
.Addition
,
5689 (Expression
) args
[x
], new IntLiteral (1), Location
.Null
);
5693 if (RedimTarget
.Type
.GetArrayRank() != args
.Count
)
5694 Report
.Error (415, "'ReDim' cannot change the number of dimensions of an array.");
5696 BaseType
= RedimTarget
.Type
.GetElementType();
5697 Expression BaseTypeExpr
= MonoBASIC
.Parser
.DecomposeQI(BaseType
.FullName
.ToString(), Location
.Null
);
5698 ArrayCreation acExpr
= new ArrayCreation (BaseTypeExpr
, NewIndexes
, "", null, Location
.Null
);
5699 // TODO: we are in a foreach we probably can't reuse ReDimExpr, must turn it into an array(list)
5702 // TODO: Generate call to copying code, which has to make lots of verifications
5703 //PreserveExpr = (ExpressionStatement) new Preserve(RedimTarget, acExpr, loc);
5704 //ReDimExpr = (StatementExpression) new StatementExpression ((ExpressionStatement) new Assign (RedimTarget, PreserveExpr, loc), loc);
5705 ReDimExpr
= (StatementExpression
) new StatementExpression ((ExpressionStatement
) new Assign (RedimTarget
, acExpr
, loc
), loc
);
5708 ReDimExpr
= (StatementExpression
) new StatementExpression ((ExpressionStatement
) new Assign (RedimTarget
, acExpr
, loc
), loc
);
5709 ReDimExpr
.Resolve(ec
);
5714 protected override bool DoEmit (EmitContext ec
)
5722 public class Erase
: Statement
{
5723 Expression EraseTarget
;
5725 private StatementExpression EraseExpr
;
5727 public Erase (Expression expr
, Location l
)
5733 public override bool Resolve (EmitContext ec
)
5735 EraseTarget
= EraseTarget
.Resolve (ec
);
5736 if (!EraseTarget
.Type
.IsArray
)
5737 Report
.Error (49, "'Erase' statement requires an array");
5739 EraseExpr
= (StatementExpression
) new StatementExpression ((ExpressionStatement
) new Assign (EraseTarget
, NullLiteral
.Null
, loc
), loc
);
5740 EraseExpr
.Resolve(ec
);
5745 protected override bool DoEmit (EmitContext ec
)