2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
14 using System
.Reflection
;
15 using System
.Reflection
.Emit
;
16 using System
.Diagnostics
;
18 namespace Mono
.CSharp
{
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 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
38 public virtual bool ResolveUnreachable (EmitContext ec
, bool warn
)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
49 if (warn
&& (RootContext
.WarningLevel
>= 2))
50 Report
.Warning (162, loc
, "Unreachable code detected");
52 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
53 bool ok
= Resolve (ec
);
54 ec
.KillFlowBranching ();
59 protected void CheckObsolete (Type type
)
61 ObsoleteAttribute obsolete_attr
= AttributeTester
.GetObsoleteAttribute (type
);
62 if (obsolete_attr
== null)
65 AttributeTester
.Report_ObsoleteMessage (obsolete_attr
, type
.FullName
, loc
);
69 /// Return value indicates whether all code paths emitted return.
71 protected abstract void DoEmit (EmitContext ec
);
74 /// Utility wrapper routine for Error, just to beautify the code
76 public void Error (int error
, string format
, params object[] args
)
78 Error (error
, String
.Format (format
, args
));
81 public void Error (int error
, string s
)
83 if (!Location
.IsNull (loc
))
84 Report
.Error (error
, loc
, s
);
86 Report
.Error (error
, s
);
90 /// Return value indicates whether all code paths emitted return.
92 public virtual void Emit (EmitContext ec
)
99 public sealed class EmptyStatement
: Statement
{
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value
= new EmptyStatement ();
105 public override bool Resolve (EmitContext ec
)
110 protected override void DoEmit (EmitContext ec
)
115 public class If
: Statement
{
117 public Statement TrueStatement
;
118 public Statement FalseStatement
;
122 public If (Expression expr
, Statement trueStatement
, Location l
)
125 TrueStatement
= trueStatement
;
129 public If (Expression expr
,
130 Statement trueStatement
,
131 Statement falseStatement
,
135 TrueStatement
= trueStatement
;
136 FalseStatement
= falseStatement
;
140 public override bool Resolve (EmitContext ec
)
142 Report
.Debug (1, "START IF BLOCK", loc
);
144 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
149 Assign ass
= expr
as Assign
;
150 if (ass
!= null && ass
.Source
is Constant
) {
151 Report
.Warning (665, 3, loc
, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
155 // Dead code elimination
157 if (expr
is BoolConstant
){
158 bool take
= ((BoolConstant
) expr
).Value
;
161 if (!TrueStatement
.Resolve (ec
))
164 if ((FalseStatement
!= null) &&
165 !FalseStatement
.ResolveUnreachable (ec
, true))
167 FalseStatement
= null;
169 if (!TrueStatement
.ResolveUnreachable (ec
, true))
171 TrueStatement
= null;
173 if ((FalseStatement
!= null) &&
174 !FalseStatement
.Resolve (ec
))
181 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
183 bool ok
= TrueStatement
.Resolve (ec
);
185 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.Reachability
.IsUnreachable
;
187 ec
.CurrentBranching
.CreateSibling ();
189 if ((FalseStatement
!= null) && !FalseStatement
.Resolve (ec
))
192 ec
.EndFlowBranching ();
194 Report
.Debug (1, "END IF BLOCK", loc
);
199 protected override void DoEmit (EmitContext ec
)
201 ILGenerator ig
= ec
.ig
;
202 Label false_target
= ig
.DefineLabel ();
206 // If we're a boolean expression, Resolve() already
207 // eliminated dead code for us.
209 if (expr
is BoolConstant
){
210 bool take
= ((BoolConstant
) expr
).Value
;
213 TrueStatement
.Emit (ec
);
214 else if (FalseStatement
!= null)
215 FalseStatement
.Emit (ec
);
220 expr
.EmitBranchable (ec
, false_target
, false);
222 TrueStatement
.Emit (ec
);
224 if (FalseStatement
!= null){
225 bool branch_emitted
= false;
227 end
= ig
.DefineLabel ();
229 ig
.Emit (OpCodes
.Br
, end
);
230 branch_emitted
= true;
233 ig
.MarkLabel (false_target
);
234 FalseStatement
.Emit (ec
);
239 ig
.MarkLabel (false_target
);
244 public class Do
: Statement
{
245 public Expression expr
;
246 public readonly Statement EmbeddedStatement
;
249 public Do (Statement statement
, Expression boolExpr
, Location l
)
252 EmbeddedStatement
= statement
;
256 public override bool Resolve (EmitContext ec
)
260 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
262 if (!EmbeddedStatement
.Resolve (ec
))
265 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
268 else if (expr
is BoolConstant
){
269 bool res
= ((BoolConstant
) expr
).Value
;
275 ec
.CurrentBranching
.Infinite
= infinite
;
276 ec
.EndFlowBranching ();
281 protected override void DoEmit (EmitContext ec
)
283 ILGenerator ig
= ec
.ig
;
284 Label loop
= ig
.DefineLabel ();
285 Label old_begin
= ec
.LoopBegin
;
286 Label old_end
= ec
.LoopEnd
;
288 ec
.LoopBegin
= ig
.DefineLabel ();
289 ec
.LoopEnd
= ig
.DefineLabel ();
292 EmbeddedStatement
.Emit (ec
);
293 ig
.MarkLabel (ec
.LoopBegin
);
296 // Dead code elimination
298 if (expr
is BoolConstant
){
299 bool res
= ((BoolConstant
) expr
).Value
;
302 ec
.ig
.Emit (OpCodes
.Br
, loop
);
304 expr
.EmitBranchable (ec
, loop
, true);
306 ig
.MarkLabel (ec
.LoopEnd
);
308 ec
.LoopBegin
= old_begin
;
309 ec
.LoopEnd
= old_end
;
313 public class While
: Statement
{
314 public Expression expr
;
315 public readonly Statement Statement
;
316 bool infinite
, empty
;
318 public While (Expression boolExpr
, Statement statement
, Location l
)
320 this.expr
= boolExpr
;
321 Statement
= statement
;
325 public override bool Resolve (EmitContext ec
)
329 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
334 // Inform whether we are infinite or not
336 if (expr
is BoolConstant
){
337 BoolConstant bc
= (BoolConstant
) expr
;
339 if (bc
.Value
== false){
340 if (!Statement
.ResolveUnreachable (ec
, true))
348 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
350 if (!Statement
.Resolve (ec
))
353 ec
.CurrentBranching
.Infinite
= infinite
;
354 ec
.EndFlowBranching ();
359 protected override void DoEmit (EmitContext ec
)
364 ILGenerator ig
= ec
.ig
;
365 Label old_begin
= ec
.LoopBegin
;
366 Label old_end
= ec
.LoopEnd
;
368 ec
.LoopBegin
= ig
.DefineLabel ();
369 ec
.LoopEnd
= ig
.DefineLabel ();
372 // Inform whether we are infinite or not
374 if (expr
is BoolConstant
){
375 ig
.MarkLabel (ec
.LoopBegin
);
377 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
380 // Inform that we are infinite (ie, `we return'), only
381 // if we do not `break' inside the code.
383 ig
.MarkLabel (ec
.LoopEnd
);
385 Label while_loop
= ig
.DefineLabel ();
387 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
388 ig
.MarkLabel (while_loop
);
392 ig
.MarkLabel (ec
.LoopBegin
);
394 expr
.EmitBranchable (ec
, while_loop
, true);
396 ig
.MarkLabel (ec
.LoopEnd
);
399 ec
.LoopBegin
= old_begin
;
400 ec
.LoopEnd
= old_end
;
404 public class For
: Statement
{
406 readonly Statement InitStatement
;
407 readonly Statement Increment
;
408 readonly Statement Statement
;
409 bool infinite
, empty
;
411 public For (Statement initStatement
,
417 InitStatement
= initStatement
;
419 Increment
= increment
;
420 Statement
= statement
;
424 public override bool Resolve (EmitContext ec
)
428 if (InitStatement
!= null){
429 if (!InitStatement
.Resolve (ec
))
434 Test
= Expression
.ResolveBoolean (ec
, Test
, loc
);
437 else if (Test
is BoolConstant
){
438 BoolConstant bc
= (BoolConstant
) Test
;
440 if (bc
.Value
== false){
441 if (!Statement
.ResolveUnreachable (ec
, true))
443 if ((Increment
!= null) &&
444 !Increment
.ResolveUnreachable (ec
, false))
454 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
456 ec
.CurrentBranching
.CreateSibling ();
458 if (!Statement
.Resolve (ec
))
461 if (Increment
!= null){
462 if (!Increment
.Resolve (ec
))
466 ec
.CurrentBranching
.Infinite
= infinite
;
467 ec
.EndFlowBranching ();
472 protected override void DoEmit (EmitContext ec
)
477 ILGenerator ig
= ec
.ig
;
478 Label old_begin
= ec
.LoopBegin
;
479 Label old_end
= ec
.LoopEnd
;
480 Label loop
= ig
.DefineLabel ();
481 Label test
= ig
.DefineLabel ();
483 if (InitStatement
!= null && InitStatement
!= EmptyStatement
.Value
)
484 InitStatement
.Emit (ec
);
486 ec
.LoopBegin
= ig
.DefineLabel ();
487 ec
.LoopEnd
= ig
.DefineLabel ();
489 ig
.Emit (OpCodes
.Br
, test
);
493 ig
.MarkLabel (ec
.LoopBegin
);
494 if (Increment
!= EmptyStatement
.Value
)
499 // If test is null, there is no test, and we are just
504 // The Resolve code already catches the case for
505 // Test == BoolConstant (false) so we know that
508 if (Test
is BoolConstant
)
509 ig
.Emit (OpCodes
.Br
, loop
);
511 Test
.EmitBranchable (ec
, loop
, true);
514 ig
.Emit (OpCodes
.Br
, loop
);
515 ig
.MarkLabel (ec
.LoopEnd
);
517 ec
.LoopBegin
= old_begin
;
518 ec
.LoopEnd
= old_end
;
522 public class StatementExpression
: Statement
{
523 public ExpressionStatement expr
;
525 public StatementExpression (ExpressionStatement expr
, Location l
)
531 public override bool Resolve (EmitContext ec
)
533 expr
= expr
.ResolveStatement (ec
);
537 protected override void DoEmit (EmitContext ec
)
539 expr
.EmitStatement (ec
);
542 public override string ToString ()
544 return "StatementExpression (" + expr
+ ")";
549 /// Implements the return statement
551 public class Return
: Statement
{
552 public Expression Expr
;
554 public Return (Expression expr
, Location l
)
562 public override bool Resolve (EmitContext ec
)
564 if (ec
.ReturnType
== null){
566 if (ec
.CurrentAnonymousMethod
!= null){
567 Report
.Error (1662, loc
, String
.Format (
568 "Anonymous method could not be converted to delegate " +
569 "since the return value does not match the delegate value"));
571 Error (127, "Return with a value not allowed here");
576 Error (126, "An object of type `{0}' is expected " +
577 "for the return statement",
578 TypeManager
.CSharpName (ec
.ReturnType
));
582 Expr
= Expr
.Resolve (ec
);
586 if (Expr
.Type
!= ec
.ReturnType
) {
587 Expr
= Convert
.ImplicitConversionRequired (
588 ec
, Expr
, ec
.ReturnType
, loc
);
595 Error (-206, "Return statement not allowed inside iterators");
599 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
601 if (ec
.CurrentBranching
.InTryOrCatch (true)) {
602 ec
.CurrentBranching
.AddFinallyVector (vector
);
604 } else if (ec
.CurrentBranching
.InFinally (true)) {
605 Error (157, "Control can not leave the body of the finally block");
608 vector
.CheckOutParameters (ec
.CurrentBranching
);
611 ec
.NeedReturnLabel ();
613 ec
.CurrentBranching
.CurrentUsageVector
.Return ();
617 protected override void DoEmit (EmitContext ec
)
623 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
627 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
629 ec
.ig
.Emit (OpCodes
.Ret
);
633 public class Goto
: Statement
{
636 LabeledStatement label
;
638 public override bool Resolve (EmitContext ec
)
640 label
= ec
.CurrentBranching
.LookupLabel (target
, loc
);
644 // If this is a forward goto.
645 if (!label
.IsDefined
)
646 label
.AddUsageVector (ec
.CurrentBranching
.CurrentUsageVector
);
648 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
653 public Goto (Block parent_block
, string label
, Location l
)
655 block
= parent_block
;
660 public string Target
{
666 protected override void DoEmit (EmitContext ec
)
668 Label l
= label
.LabelTarget (ec
);
669 ec
.ig
.Emit (OpCodes
.Br
, l
);
673 public class LabeledStatement
: Statement
{
674 public readonly Location Location
;
680 FlowBranching
.UsageVector vectors
;
682 public LabeledStatement (string label_name
, Location l
)
687 public Label
LabelTarget (EmitContext ec
)
692 label
= ec
.ig
.DefineLabel ();
698 public bool IsDefined
{
704 public bool HasBeenReferenced
{
710 public void AddUsageVector (FlowBranching
.UsageVector vector
)
712 vector
= vector
.Clone ();
713 vector
.Next
= vectors
;
717 public override bool Resolve (EmitContext ec
)
719 ec
.CurrentBranching
.Label (vectors
);
726 protected override void DoEmit (EmitContext ec
)
728 if (ig
!= null && ig
!= ec
.ig
) {
729 Report
.Error (1632, "Control cannot leave body of anonymous method");
733 ec
.ig
.MarkLabel (label
);
739 /// `goto default' statement
741 public class GotoDefault
: Statement
{
743 public GotoDefault (Location l
)
748 public override bool Resolve (EmitContext ec
)
750 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
754 protected override void DoEmit (EmitContext ec
)
756 if (ec
.Switch
== null){
757 Report
.Error (153, loc
, "goto default is only valid in a switch statement");
761 if (!ec
.Switch
.GotDefault
){
762 Report
.Error (159, loc
, "No default target on switch statement");
765 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
770 /// `goto case' statement
772 public class GotoCase
: Statement
{
776 public GotoCase (Expression e
, Location l
)
782 public override bool Resolve (EmitContext ec
)
784 if (ec
.Switch
== null){
785 Report
.Error (153, loc
, "goto case is only valid in a switch statement");
789 expr
= expr
.Resolve (ec
);
793 if (!(expr
is Constant
)){
794 Report
.Error (159, loc
, "Target expression for goto case is not constant");
798 object val
= Expression
.ConvertIntLiteral (
799 (Constant
) expr
, ec
.Switch
.SwitchType
, loc
);
804 sl
= (SwitchLabel
) ec
.Switch
.Elements
[val
];
809 "No such label 'case " + val
+ "': for the goto case");
813 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
817 protected override void DoEmit (EmitContext ec
)
819 ec
.ig
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
823 public class Throw
: Statement
{
826 public Throw (Expression expr
, Location l
)
832 public override bool Resolve (EmitContext ec
)
834 ec
.CurrentBranching
.CurrentUsageVector
.Throw ();
837 expr
= expr
.Resolve (ec
);
841 ExprClass eclass
= expr
.eclass
;
843 if (!(eclass
== ExprClass
.Variable
|| eclass
== ExprClass
.PropertyAccess
||
844 eclass
== ExprClass
.Value
|| eclass
== ExprClass
.IndexerAccess
)) {
845 expr
.Error_UnexpectedKind ("value, variable, property or indexer access ", loc
);
851 if ((t
!= TypeManager
.exception_type
) &&
852 !t
.IsSubclassOf (TypeManager
.exception_type
) &&
853 !(expr
is NullLiteral
)) {
855 "The type caught or thrown must be derived " +
856 "from System.Exception");
862 if (ec
.CurrentBranching
.InFinally (true)) {
863 Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
867 if (!ec
.CurrentBranching
.InCatch ()) {
868 Error (156, "A throw statement with no argument is only allowed in a catch clause");
874 protected override void DoEmit (EmitContext ec
)
877 ec
.ig
.Emit (OpCodes
.Rethrow
);
881 ec
.ig
.Emit (OpCodes
.Throw
);
886 public class Break
: Statement
{
888 public Break (Location l
)
895 public override bool Resolve (EmitContext ec
)
897 if (!ec
.CurrentBranching
.InLoop () && !ec
.CurrentBranching
.InSwitch ()){
898 Error (139, "No enclosing loop or switch to continue to");
900 } else if (ec
.CurrentBranching
.InFinally (false)) {
901 Error (157, "Control can not leave the body of the finally block");
903 } else if (ec
.CurrentBranching
.InTryOrCatch (false))
904 ec
.CurrentBranching
.AddFinallyVector (
905 ec
.CurrentBranching
.CurrentUsageVector
);
906 else if (ec
.CurrentBranching
.InLoop ())
907 ec
.CurrentBranching
.AddBreakVector (
908 ec
.CurrentBranching
.CurrentUsageVector
);
910 crossing_exc
= ec
.CurrentBranching
.BreakCrossesTryCatchBoundary ();
913 ec
.NeedReturnLabel ();
915 ec
.CurrentBranching
.CurrentUsageVector
.Break ();
919 protected override void DoEmit (EmitContext ec
)
921 ILGenerator ig
= ec
.ig
;
924 ig
.Emit (OpCodes
.Leave
, ec
.LoopEnd
);
926 ig
.Emit (OpCodes
.Br
, ec
.LoopEnd
);
931 public class Continue
: Statement
{
933 public Continue (Location l
)
940 public override bool Resolve (EmitContext ec
)
942 if (!ec
.CurrentBranching
.InLoop () && !ec
.CurrentBranching
.InSwitch ()){
943 Error (139, "No enclosing loop to continue to");
945 } else if (ec
.CurrentBranching
.InFinally (false)) {
946 Error (157, "Control can not leave the body of the finally block");
948 } else if (ec
.CurrentBranching
.InTryOrCatch (false))
949 ec
.CurrentBranching
.AddFinallyVector (ec
.CurrentBranching
.CurrentUsageVector
);
951 crossing_exc
= ec
.CurrentBranching
.BreakCrossesTryCatchBoundary ();
953 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
957 protected override void DoEmit (EmitContext ec
)
959 Label begin
= ec
.LoopBegin
;
962 ec
.ig
.Emit (OpCodes
.Leave
, begin
);
964 ec
.ig
.Emit (OpCodes
.Br
, begin
);
969 // The information about a user-perceived local variable
971 public class LocalInfo
{
972 public Expression Type
;
975 // Most of the time a variable will be stored in a LocalBuilder
977 // But sometimes, it will be stored in a field (variables that have been
978 // hoisted by iterators or by anonymous methods). The context of the field will
979 // be stored in the EmitContext
982 public LocalBuilder LocalBuilder
;
983 public FieldBuilder FieldBuilder
;
985 public Type VariableType
;
986 public readonly string Name
;
987 public readonly Location Location
;
988 public readonly Block Block
;
990 public VariableInfo VariableInfo
;
1003 public LocalInfo (Expression type
, string name
, Block block
, Location l
)
1011 public LocalInfo (TypeContainer tc
, Block block
, Location l
)
1013 VariableType
= tc
.TypeBuilder
;
1018 public bool IsThisAssigned (EmitContext ec
, Location loc
)
1020 if (VariableInfo
== null)
1021 throw new Exception ();
1023 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1026 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
.CurrentBranching
, VariableInfo
, loc
);
1029 public bool IsAssigned (EmitContext ec
)
1031 if (VariableInfo
== null)
1032 throw new Exception ();
1034 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1037 public bool Resolve (EmitContext ec
)
1039 if (VariableType
== null) {
1040 TypeExpr texpr
= Type
.ResolveAsTypeTerminal (ec
);
1044 VariableType
= texpr
.Type
;
1047 if (VariableType
== TypeManager
.void_type
) {
1048 Report
.Error (1547, Location
,
1049 "Keyword 'void' cannot be used in this context");
1053 if (VariableType
.IsAbstract
&& VariableType
.IsSealed
) {
1054 Report
.Error (723, Location
, "Cannot declare variable of static type '{0}'", TypeManager
.CSharpName (VariableType
));
1057 // TODO: breaks the build
1058 // if (VariableType.IsPointer && !ec.InUnsafe)
1059 // Expression.UnsafeError (Location);
1065 // Whether the variable is Fixed (because its Pinned or its a value type)
1067 public bool IsFixed
{
1069 if (((flags
& Flags
.Pinned
) != 0) || TypeManager
.IsValueType (VariableType
))
1076 public bool IsCaptured
{
1078 return (flags
& Flags
.Captured
) != 0;
1082 flags
|= Flags
.Captured
;
1086 public bool AddressTaken
{
1088 return (flags
& Flags
.AddressTaken
) != 0;
1092 flags
|= Flags
.AddressTaken
;
1096 public override string ToString ()
1098 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1099 Name
, Type
, VariableInfo
, Location
);
1104 return (flags
& Flags
.Used
) != 0;
1107 flags
= value ? (flags
| Flags
.Used
) : (unchecked (flags
& ~Flags
.Used
));
1111 public bool ReadOnly
{
1113 return (flags
& Flags
.ReadOnly
) != 0;
1116 flags
= value ? (flags
| Flags
.ReadOnly
) : (unchecked (flags
& ~Flags
.ReadOnly
));
1121 // Whether the variable is pinned, if Pinned the variable has been
1122 // allocated in a pinned slot with DeclareLocal.
1124 public bool Pinned
{
1126 return (flags
& Flags
.Pinned
) != 0;
1129 flags
= value ? (flags
| Flags
.Pinned
) : (flags
& ~Flags
.Pinned
);
1133 public bool IsThis
{
1135 return (flags
& Flags
.IsThis
) != 0;
1138 flags
= value ? (flags
| Flags
.IsThis
) : (flags
& ~Flags
.IsThis
);
1144 /// Block represents a C# block.
1148 /// This class is used in a number of places: either to represent
1149 /// explicit blocks that the programmer places or implicit blocks.
1151 /// Implicit blocks are used as labels or to introduce variable
1154 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1155 /// they contain extra information that is not necessary on normal blocks.
1157 public class Block
: Statement
{
1158 public Block Parent
;
1159 public readonly Location StartLocation
;
1160 public Location EndLocation
= Location
.Null
;
1167 VariablesInitialized
= 8,
1176 public bool Implicit
{
1178 return (flags
& Flags
.Implicit
) != 0;
1182 public bool Unchecked
{
1184 return (flags
& Flags
.Unchecked
) != 0;
1187 flags
|= Flags
.Unchecked
;
1191 public bool Unsafe
{
1193 return (flags
& Flags
.Unsafe
) != 0;
1196 flags
|= Flags
.Unsafe
;
1200 public bool HasVarargs
{
1203 return Parent
.HasVarargs
;
1205 return (flags
& Flags
.HasVarargs
) != 0;
1208 flags
|= Flags
.HasVarargs
;
1213 // The statements in this block
1215 public ArrayList statements
;
1219 // An array of Blocks. We keep track of children just
1220 // to generate the local variable declarations.
1222 // Statements and child statements are handled through the
1228 // Labels. (label, block) pairs.
1233 // Keeps track of (name, type) pairs
1235 Hashtable variables
;
1238 // Keeps track of constants
1239 Hashtable constants
;
1242 // The parameters for the block, this is only needed on the toplevel block really
1243 // TODO: move `parameters' into ToplevelBlock
1244 Parameters parameters
;
1247 // If this is a switch section, the enclosing switch block.
1251 protected static int id
;
1255 public Block (Block parent
)
1256 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1259 public Block (Block parent
, Flags flags
)
1260 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1263 public Block (Block parent
, Flags flags
, Parameters parameters
)
1264 : this (parent
, flags
, parameters
, Location
.Null
, Location
.Null
)
1267 public Block (Block parent
, Location start
, Location end
)
1268 : this (parent
, (Flags
) 0, start
, end
)
1271 public Block (Block parent
, Parameters parameters
, Location start
, Location end
)
1272 : this (parent
, (Flags
) 0, parameters
, start
, end
)
1275 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1276 : this (parent
, flags
, Parameters
.EmptyReadOnlyParameters
, start
, end
)
1279 public Block (Block parent
, Flags flags
, Parameters parameters
,
1280 Location start
, Location end
)
1283 parent
.AddChild (this);
1285 this.Parent
= parent
;
1287 this.parameters
= parameters
;
1288 this.StartLocation
= start
;
1289 this.EndLocation
= end
;
1292 statements
= new ArrayList ();
1294 if (parent
!= null && Implicit
) {
1295 if (parent
.child_variable_names
== null)
1296 parent
.child_variable_names
= new Hashtable();
1297 // share with parent
1298 child_variable_names
= parent
.child_variable_names
;
1303 public Block
CreateSwitchBlock (Location start
)
1305 Block new_block
= new Block (this, start
, start
);
1306 new_block
.switch_block
= this;
1316 void AddChild (Block b
)
1318 if (children
== null)
1319 children
= new ArrayList ();
1324 public void SetEndLocation (Location loc
)
1330 /// Adds a label to the current block.
1334 /// false if the name already exists in this block. true
1338 public bool AddLabel (string name
, LabeledStatement target
, Location loc
)
1340 if (switch_block
!= null)
1341 return switch_block
.AddLabel (name
, target
, loc
);
1344 while (cur
!= null) {
1345 if (cur
.DoLookupLabel (name
) != null) {
1347 140, loc
, "The label '{0}' is a duplicate",
1358 while (cur
!= null) {
1359 if (cur
.DoLookupLabel (name
) != null) {
1362 "The label '{0}' shadows another label " +
1363 "by the same name in a containing scope.",
1368 if (children
!= null) {
1369 foreach (Block b
in children
) {
1370 LabeledStatement s
= b
.DoLookupLabel (name
);
1376 "The label '{0}' shadows another " +
1377 "label by the same name in a " +
1378 "containing scope.",
1389 labels
= new Hashtable ();
1391 labels
.Add (name
, target
);
1395 public LabeledStatement
LookupLabel (string name
)
1397 LabeledStatement s
= DoLookupLabel (name
);
1401 if (children
== null)
1404 foreach (Block child
in children
) {
1405 if (!child
.Implicit
)
1408 s
= child
.LookupLabel (name
);
1416 LabeledStatement
DoLookupLabel (string name
)
1418 if (switch_block
!= null)
1419 return switch_block
.LookupLabel (name
);
1422 if (labels
.Contains (name
))
1423 return ((LabeledStatement
) labels
[name
]);
1428 LocalInfo this_variable
= null;
1431 // Returns the "this" instance variable of this block.
1432 // See AddThisVariable() for more information.
1434 public LocalInfo ThisVariable
{
1436 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1437 if (b
.this_variable
!= null)
1438 return b
.this_variable
;
1445 Hashtable child_variable_names
;
1448 // Marks a variable with name @name as being used in a child block.
1449 // If a variable name has been used in a child block, it's illegal to
1450 // declare a variable with the same name in the current block.
1452 public void AddChildVariableName (string name
)
1454 if (child_variable_names
== null)
1455 child_variable_names
= new Hashtable ();
1457 child_variable_names
[name
] = null;
1461 // Checks whether a variable name has already been used in a child block.
1463 public bool IsVariableNameUsedInChildBlock (string name
)
1465 if (child_variable_names
== null)
1468 return child_variable_names
.Contains (name
);
1472 // This is used by non-static `struct' constructors which do not have an
1473 // initializer - in this case, the constructor must initialize all of the
1474 // struct's fields. To do this, we add a "this" variable and use the flow
1475 // analysis code to ensure that it's been fully initialized before control
1476 // leaves the constructor.
1478 public LocalInfo
AddThisVariable (TypeContainer tc
, Location l
)
1480 if (this_variable
!= null)
1481 return this_variable
;
1483 if (variables
== null)
1484 variables
= new Hashtable ();
1486 this_variable
= new LocalInfo (tc
, this, l
);
1487 this_variable
.Used
= true;
1488 this_variable
.IsThis
= true;
1490 variables
.Add ("this", this_variable
);
1492 return this_variable
;
1495 public LocalInfo
AddVariable (Expression type
, string name
, Parameters pars
, Location l
)
1497 if (variables
== null)
1498 variables
= new Hashtable ();
1500 LocalInfo vi
= GetLocalInfo (name
);
1502 if (vi
.Block
!= this)
1503 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
1504 "cannot be declared in this scope since it would " +
1505 "give a different meaning to `" + name
+ "', which " +
1506 "is already used in a `parent or current' scope to " +
1507 "denote something else");
1509 Report
.Error (128, l
, "A local variable `" + name
+ "' is already " +
1510 "defined in this scope");
1514 if (IsVariableNameUsedInChildBlock (name
)) {
1515 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
1516 "cannot be declared in this scope since it would " +
1517 "give a different meaning to `" + name
+ "', which " +
1518 "is already used in a `child' scope to denote something " +
1525 Parameter p
= pars
.GetParameterByName (name
, out idx
);
1527 Report
.Error (136, l
, "A local variable named `" + name
+ "' " +
1528 "cannot be declared in this scope since it would " +
1529 "give a different meaning to `" + name
+ "', which " +
1530 "is already used in a `parent or current' scope to " +
1531 "denote something else");
1536 vi
= new LocalInfo (type
, name
, this, l
);
1538 variables
.Add (name
, vi
);
1540 // Mark 'name' as "used by a child block" in every surrounding block
1542 while (cur
!= null && cur
.Implicit
)
1545 for (Block par
= cur
.Parent
; par
!= null; par
= par
.Parent
)
1546 par
.AddChildVariableName (name
);
1548 if ((flags
& Flags
.VariablesInitialized
) != 0)
1549 throw new Exception ();
1551 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1555 public bool AddConstant (Expression type
, string name
, Expression
value, Parameters pars
, Location l
)
1557 if (AddVariable (type
, name
, pars
, l
) == null)
1560 if (constants
== null)
1561 constants
= new Hashtable ();
1563 constants
.Add (name
, value);
1567 public Hashtable Variables
{
1573 public LocalInfo
GetLocalInfo (string name
)
1575 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1576 if (b
.variables
!= null) {
1577 LocalInfo ret
= b
.variables
[name
] as LocalInfo
;
1585 public Expression
GetVariableType (string name
)
1587 LocalInfo vi
= GetLocalInfo (name
);
1595 public Expression
GetConstantExpression (string name
)
1597 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1598 if (b
.constants
!= null) {
1599 Expression ret
= b
.constants
[name
] as Expression
;
1608 /// True if the variable named @name is a constant
1610 public bool IsConstant (string name
)
1612 Expression e
= null;
1614 e
= GetConstantExpression (name
);
1620 // Returns a `ParameterReference' for the given name, or null if there
1621 // is no such parameter
1623 public ParameterReference
GetParameterReference (string name
, Location loc
)
1628 Parameters pars
= b
.parameters
;
1634 par
= pars
.GetParameterByName (name
, out idx
);
1636 ParameterReference pr
;
1638 pr
= new ParameterReference (pars
, this, idx
, name
, loc
);
1643 } while (b
!= null);
1648 // Whether the parameter named `name' is local to this block,
1649 // or false, if the parameter belongs to an encompassing block.
1651 public bool IsLocalParameter (string name
)
1654 int toplevel_count
= 0;
1657 if (this is ToplevelBlock
)
1660 Parameters pars
= b
.parameters
;
1662 if (pars
.GetParameterByName (name
) != null)
1666 if (toplevel_count
> 0)
1669 } while (b
!= null);
1674 // Whether the `name' is a parameter reference
1676 public bool IsParameterReference (string name
)
1681 Parameters pars
= b
.parameters
;
1684 if (pars
.GetParameterByName (name
) != null)
1687 } while (b
!= null);
1692 /// A list of labels that were not used within this block
1694 public string [] GetUnreferenced ()
1696 // FIXME: Implement me
1700 public void AddStatement (Statement s
)
1703 flags
|= Flags
.BlockUsed
;
1708 return (flags
& Flags
.BlockUsed
) != 0;
1714 flags
|= Flags
.BlockUsed
;
1717 public bool HasRet
{
1719 return (flags
& Flags
.HasRet
) != 0;
1723 public bool IsDestructor
{
1725 return (flags
& Flags
.IsDestructor
) != 0;
1729 public void SetDestructor ()
1731 flags
|= Flags
.IsDestructor
;
1734 VariableMap param_map
, local_map
;
1736 public VariableMap ParameterMap
{
1738 if ((flags
& Flags
.VariablesInitialized
) == 0)
1739 throw new Exception ("Variables have not been initialized yet");
1745 public VariableMap LocalMap
{
1747 if ((flags
& Flags
.VariablesInitialized
) == 0)
1748 throw new Exception ("Variables have not been initialized yet");
1755 /// Emits the variable declarations and labels.
1758 /// tc: is our typecontainer (to resolve type references)
1759 /// ig: is the code generator:
1761 public void ResolveMeta (ToplevelBlock toplevel
, EmitContext ec
, InternalParameters ip
)
1763 bool old_unsafe
= ec
.InUnsafe
;
1765 // If some parent block was unsafe, we remain unsafe even if this block
1766 // isn't explicitly marked as such.
1767 ec
.InUnsafe
|= Unsafe
;
1770 // Compute the VariableMap's.
1772 // Unfortunately, we don't know the type when adding variables with
1773 // AddVariable(), so we need to compute this info here.
1777 if (variables
!= null) {
1778 foreach (LocalInfo li
in variables
.Values
)
1781 locals
= new LocalInfo
[variables
.Count
];
1782 variables
.Values
.CopyTo (locals
, 0);
1784 locals
= new LocalInfo
[0];
1787 local_map
= new VariableMap (Parent
.LocalMap
, locals
);
1789 local_map
= new VariableMap (locals
);
1791 param_map
= new VariableMap (ip
);
1792 flags
|= Flags
.VariablesInitialized
;
1794 bool old_check_state
= ec
.ConstantCheckState
;
1795 ec
.ConstantCheckState
= (flags
& Flags
.Unchecked
) == 0;
1798 // Process this block variables
1800 if (variables
!= null){
1801 foreach (DictionaryEntry de
in variables
){
1802 string name
= (string) de
.Key
;
1803 LocalInfo vi
= (LocalInfo
) de
.Value
;
1805 if (vi
.VariableType
== null)
1808 Type variable_type
= vi
.VariableType
;
1810 if (variable_type
.IsPointer
){
1812 // Am not really convinced that this test is required (Microsoft does it)
1813 // but the fact is that you would not be able to use the pointer variable
1816 if (!TypeManager
.VerifyUnManaged (TypeManager
.GetElementType (variable_type
),
1823 vi
.FieldBuilder
= ec
.MapVariable (name
, vi
.VariableType
);
1826 // This is needed to compile on both .NET 1.x and .NET 2.x
1827 // the later introduced `DeclareLocal (Type t, bool pinned)'
1829 vi
.LocalBuilder
= TypeManager
.DeclareLocalPinned (ig
, vi
.VariableType
);
1830 else if (!vi
.IsThis
)
1831 vi
.LocalBuilder
= ig
.DeclareLocal (vi
.VariableType
);
1834 if (constants
== null)
1837 Expression cv
= (Expression
) constants
[name
];
1841 ec
.CurrentBlock
= this;
1842 Expression e
= cv
.Resolve (ec
);
1846 Constant ce
= e
as Constant
;
1848 Report
.Error (133, vi
.Location
,
1849 "The expression being assigned to `" +
1850 name
+ "' must be constant (" + e
+ ")");
1854 if (e
.Type
!= variable_type
){
1855 e
= Const
.ChangeType (vi
.Location
, ce
, variable_type
);
1860 constants
.Remove (name
);
1861 constants
.Add (name
, e
);
1864 ec
.ConstantCheckState
= old_check_state
;
1867 // Now, handle the children
1869 if (children
!= null){
1870 foreach (Block b
in children
)
1871 b
.ResolveMeta (toplevel
, ec
, ip
);
1873 ec
.InUnsafe
= old_unsafe
;
1877 // Emits the local variable declarations for a block
1879 public void EmitMeta (EmitContext ec
)
1881 ILGenerator ig
= ec
.ig
;
1883 if (variables
!= null){
1884 bool have_captured_vars
= ec
.HaveCapturedVariables ();
1885 bool remap_locals
= ec
.RemapToProxy
;
1887 foreach (DictionaryEntry de
in variables
){
1888 LocalInfo vi
= (LocalInfo
) de
.Value
;
1890 if (have_captured_vars
&& ec
.IsCaptured (vi
))
1894 vi
.FieldBuilder
= ec
.MapVariable (vi
.Name
, vi
.VariableType
);
1898 // This is needed to compile on both .NET 1.x and .NET 2.x
1899 // the later introduced `DeclareLocal (Type t, bool pinned)'
1901 vi
.LocalBuilder
= TypeManager
.DeclareLocalPinned (ig
, vi
.VariableType
);
1902 else if (!vi
.IsThis
)
1903 vi
.LocalBuilder
= ig
.DeclareLocal (vi
.VariableType
);
1908 if (children
!= null){
1909 foreach (Block b
in children
)
1914 void UsageWarning (FlowBranching
.UsageVector vector
)
1918 if ((variables
!= null) && (RootContext
.WarningLevel
>= 3)) {
1919 foreach (DictionaryEntry de
in variables
){
1920 LocalInfo vi
= (LocalInfo
) de
.Value
;
1925 name
= (string) de
.Key
;
1927 if (vector
.IsAssigned (vi
.VariableInfo
)){
1928 Report
.Warning (219, vi
.Location
, "The variable '{0}' is assigned but its value is never used", name
);
1930 Report
.Warning (168, vi
.Location
, "The variable '{0}' is declared but never used", name
);
1936 bool unreachable_shown
;
1938 public override bool Resolve (EmitContext ec
)
1940 Block prev_block
= ec
.CurrentBlock
;
1943 int errors
= Report
.Errors
;
1945 ec
.CurrentBlock
= this;
1946 ec
.StartFlowBranching (this);
1948 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
1950 bool unreachable
= unreachable_shown
;
1952 int statement_count
= statements
.Count
;
1953 for (int ix
= 0; ix
< statement_count
; ix
++){
1954 Statement s
= (Statement
) statements
[ix
];
1956 if (unreachable
&& !(s
is LabeledStatement
)) {
1957 if (s
== EmptyStatement
.Value
)
1958 s
.loc
= EndLocation
;
1960 if (!s
.ResolveUnreachable (ec
, !unreachable_shown
))
1963 if (s
!= EmptyStatement
.Value
)
1964 unreachable_shown
= true;
1966 s
.loc
= Location
.Null
;
1968 if (ok
&& !(s
is Block
)) {
1969 statements
[ix
] = EmptyStatement
.Value
;
1974 if (s
.Resolve (ec
) == false) {
1976 statements
[ix
] = EmptyStatement
.Value
;
1980 num_statements
= ix
+ 1;
1982 if (s
is LabeledStatement
)
1983 unreachable
= false;
1985 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.Reachability
.IsUnreachable
;
1988 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
1989 ec
.CurrentBranching
, statement_count
, num_statements
);
1992 FlowBranching
.UsageVector vector
= ec
.DoEndFlowBranching ();
1994 ec
.CurrentBlock
= prev_block
;
1996 // If we're a non-static `struct' constructor which doesn't have an
1997 // initializer, then we must initialize all of the struct's fields.
1998 if ((this_variable
!= null) &&
1999 (vector
.Reachability
.Throws
!= FlowBranching
.FlowReturns
.Always
) &&
2000 !this_variable
.IsThisAssigned (ec
, loc
))
2003 if ((labels
!= null) && (RootContext
.WarningLevel
>= 2)) {
2004 foreach (LabeledStatement label
in labels
.Values
)
2005 if (!label
.HasBeenReferenced
)
2006 Report
.Warning (164, label
.Location
,
2007 "This label has not been referenced");
2010 Report
.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation
, vector
);
2012 if ((vector
.Reachability
.Returns
== FlowBranching
.FlowReturns
.Always
) ||
2013 (vector
.Reachability
.Throws
== FlowBranching
.FlowReturns
.Always
) ||
2014 (vector
.Reachability
.Reachable
== FlowBranching
.FlowReturns
.Never
))
2015 flags
|= Flags
.HasRet
;
2017 if (ok
&& (errors
== Report
.Errors
)) {
2018 if (RootContext
.WarningLevel
>= 3)
2019 UsageWarning (vector
);
2025 public override bool ResolveUnreachable (EmitContext ec
, bool warn
)
2027 unreachable_shown
= true;
2029 if (warn
&& (RootContext
.WarningLevel
>= 2))
2030 Report
.Warning (162, loc
, "Unreachable code detected");
2033 return Resolve (ec
);
2035 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
2036 bool ok
= Resolve (ec
);
2037 ec
.KillFlowBranching ();
2042 protected override void DoEmit (EmitContext ec
)
2044 for (int ix
= 0; ix
< num_statements
; ix
++){
2045 Statement s
= (Statement
) statements
[ix
];
2047 // Check whether we are the last statement in a
2050 if (((Parent
== null) || Implicit
) && (ix
+1 == num_statements
) && !(s
is Block
))
2051 ec
.IsLastStatement
= true;
2053 ec
.IsLastStatement
= false;
2059 public override void Emit (EmitContext ec
)
2061 Block prev_block
= ec
.CurrentBlock
;
2063 ec
.CurrentBlock
= this;
2065 bool emit_debug_info
= (CodeGen
.SymbolWriter
!= null);
2066 bool is_lexical_block
= !Implicit
&& (Parent
!= null);
2068 if (emit_debug_info
) {
2069 if (is_lexical_block
)
2070 ec
.ig
.BeginScope ();
2072 if (variables
!= null) {
2073 foreach (DictionaryEntry de
in variables
) {
2074 string name
= (string) de
.Key
;
2075 LocalInfo vi
= (LocalInfo
) de
.Value
;
2077 if (vi
.LocalBuilder
== null)
2080 ec
.DefineLocalVariable (name
, vi
.LocalBuilder
);
2085 ec
.Mark (StartLocation
, true);
2087 ec
.Mark (EndLocation
, true);
2089 if (emit_debug_info
&& is_lexical_block
)
2092 ec
.CurrentBlock
= prev_block
;
2095 public ToplevelBlock Toplevel
{
2098 while (b
.Parent
!= null){
2099 if ((b
.flags
& Flags
.IsToplevel
) != 0)
2104 return (ToplevelBlock
) b
;
2109 // Returns true if we ar ea child of `b'.
2111 public bool IsChildOf (Block b
)
2113 Block current
= this;
2116 if (current
.Parent
== b
)
2118 current
= current
.Parent
;
2119 } while (current
!= null);
2125 // A toplevel block contains extra information, the split is done
2126 // only to separate information that would otherwise bloat the more
2127 // lightweight Block.
2129 // In particular, this was introduced when the support for Anonymous
2130 // Methods was implemented.
2132 public class ToplevelBlock
: Block
{
2134 // Pointer to the host of this anonymous method, or null
2135 // if we are the topmost block
2137 public ToplevelBlock Container
;
2138 CaptureContext capture_context
;
2140 Hashtable capture_contexts
;
2145 public void RegisterCaptureContext (CaptureContext cc
)
2147 if (capture_contexts
== null)
2148 capture_contexts
= new Hashtable ();
2149 capture_contexts
[cc
] = cc
;
2152 public void CompleteContexts ()
2154 if (capture_contexts
== null)
2157 foreach (CaptureContext cc
in capture_contexts
.Keys
){
2162 public CaptureContext ToplevelBlockCaptureContext
{
2164 return capture_context
;
2169 // Parent is only used by anonymous blocks to link back to their
2172 public ToplevelBlock (ToplevelBlock container
, Parameters parameters
, Location start
) :
2173 base (null, Flags
.IsToplevel
, parameters
, start
, Location
.Null
)
2175 Container
= container
;
2178 public ToplevelBlock (Parameters parameters
, Location start
) :
2179 base (null, Flags
.IsToplevel
, parameters
, start
, Location
.Null
)
2183 public ToplevelBlock (Flags flags
, Parameters parameters
, Location start
) :
2184 base (null, flags
| Flags
.IsToplevel
, parameters
, start
, Location
.Null
)
2188 public ToplevelBlock (Location loc
) : base (null, Flags
.IsToplevel
, loc
, loc
)
2192 public void SetHaveAnonymousMethods (Location loc
, AnonymousMethod host
)
2194 if (capture_context
== null)
2195 capture_context
= new CaptureContext (this, loc
, host
);
2198 public CaptureContext CaptureContext
{
2200 return capture_context
;
2205 public class SwitchLabel
{
2208 public Location loc
;
2212 Label il_label_code
;
2213 bool il_label_code_set
;
2216 // if expr == null, then it is the default case.
2218 public SwitchLabel (Expression expr
, Location l
)
2224 public Expression Label
{
2230 public object Converted
{
2236 public Label
GetILLabel (EmitContext ec
)
2239 il_label
= ec
.ig
.DefineLabel ();
2240 il_label_set
= true;
2245 public Label
GetILLabelCode (EmitContext ec
)
2247 if (!il_label_code_set
){
2248 il_label_code
= ec
.ig
.DefineLabel ();
2249 il_label_code_set
= true;
2251 return il_label_code
;
2255 // Resolves the expression, reduces it to a literal if possible
2256 // and then converts it to the requested type.
2258 public bool ResolveAndReduce (EmitContext ec
, Type required_type
)
2263 Expression e
= label
.Resolve (ec
);
2268 if (!(e
is Constant
)){
2269 Report
.Error (150, loc
, "A constant value is expected, got: " + e
);
2273 if (e
is StringConstant
|| e
is NullLiteral
){
2274 if (required_type
== TypeManager
.string_type
){
2280 converted
= Expression
.ConvertIntLiteral ((Constant
) e
, required_type
, loc
);
2281 if (converted
== null)
2288 public class SwitchSection
{
2289 // An array of SwitchLabels.
2290 public readonly ArrayList Labels
;
2291 public readonly Block Block
;
2293 public SwitchSection (ArrayList labels
, Block block
)
2300 public class Switch
: Statement
{
2301 public readonly ArrayList Sections
;
2302 public Expression Expr
;
2305 /// Maps constants whose type type SwitchType to their SwitchLabels.
2307 public Hashtable Elements
;
2310 /// The governing switch type
2312 public Type SwitchType
;
2318 Label default_target
;
2319 Expression new_expr
;
2321 SwitchSection constant_section
;
2324 // The types allowed to be implicitly cast from
2325 // on the governing type
2327 static Type
[] allowed_types
;
2329 public Switch (Expression e
, ArrayList sects
, Location l
)
2336 public bool GotDefault
{
2342 public Label DefaultTarget
{
2344 return default_target
;
2349 // Determines the governing type for a switch. The returned
2350 // expression might be the expression from the switch, or an
2351 // expression that includes any potential conversions to the
2352 // integral types or to string.
2354 Expression
SwitchGoverningType (EmitContext ec
, Type t
)
2356 if (t
== TypeManager
.int32_type
||
2357 t
== TypeManager
.uint32_type
||
2358 t
== TypeManager
.char_type
||
2359 t
== TypeManager
.byte_type
||
2360 t
== TypeManager
.sbyte_type
||
2361 t
== TypeManager
.ushort_type
||
2362 t
== TypeManager
.short_type
||
2363 t
== TypeManager
.uint64_type
||
2364 t
== TypeManager
.int64_type
||
2365 t
== TypeManager
.string_type
||
2366 t
== TypeManager
.bool_type
||
2367 t
.IsSubclassOf (TypeManager
.enum_type
))
2370 if (allowed_types
== null){
2371 allowed_types
= new Type
[] {
2372 TypeManager
.int32_type
,
2373 TypeManager
.uint32_type
,
2374 TypeManager
.sbyte_type
,
2375 TypeManager
.byte_type
,
2376 TypeManager
.short_type
,
2377 TypeManager
.ushort_type
,
2378 TypeManager
.int64_type
,
2379 TypeManager
.uint64_type
,
2380 TypeManager
.char_type
,
2381 TypeManager
.bool_type
,
2382 TypeManager
.string_type
2387 // Try to find a *user* defined implicit conversion.
2389 // If there is no implicit conversion, or if there are multiple
2390 // conversions, we have to report an error
2392 Expression converted
= null;
2393 foreach (Type tt
in allowed_types
){
2396 e
= Convert
.ImplicitUserConversion (ec
, Expr
, tt
, loc
);
2401 // Ignore over-worked ImplicitUserConversions that do
2402 // an implicit conversion in addition to the user conversion.
2405 UserCast ue
= e
as UserCast
;
2407 if (ue
.Source
!= Expr
)
2411 if (converted
!= null){
2412 Report
.ExtraInformation (
2414 String
.Format ("reason: more than one conversion to an integral type exist for type {0}",
2415 TypeManager
.CSharpName (Expr
.Type
)));
2424 static string Error152
{
2426 return "The label '{0}:' already occurs in this switch statement";
2431 // Performs the basic sanity checks on the switch statement
2432 // (looks for duplicate keys and non-constant expressions).
2434 // It also returns a hashtable with the keys that we will later
2435 // use to compute the switch tables
2437 bool CheckSwitch (EmitContext ec
)
2441 Elements
= new Hashtable ();
2443 got_default
= false;
2445 if (TypeManager
.IsEnumType (SwitchType
)){
2446 compare_type
= TypeManager
.EnumToUnderlying (SwitchType
);
2448 compare_type
= SwitchType
;
2450 foreach (SwitchSection ss
in Sections
){
2451 foreach (SwitchLabel sl
in ss
.Labels
){
2452 if (!sl
.ResolveAndReduce (ec
, SwitchType
)){
2457 if (sl
.Label
== null){
2459 Report
.Error (152, sl
.loc
, Error152
, "default");
2466 object key
= sl
.Converted
;
2468 if (key
is Constant
)
2469 key
= ((Constant
) key
).GetValue ();
2472 key
= NullLiteral
.Null
;
2474 string lname
= null;
2475 if (compare_type
== TypeManager
.uint64_type
){
2476 ulong v
= (ulong) key
;
2478 if (Elements
.Contains (v
))
2479 lname
= v
.ToString ();
2481 Elements
.Add (v
, sl
);
2482 } else if (compare_type
== TypeManager
.int64_type
){
2483 long v
= (long) key
;
2485 if (Elements
.Contains (v
))
2486 lname
= v
.ToString ();
2488 Elements
.Add (v
, sl
);
2489 } else if (compare_type
== TypeManager
.uint32_type
){
2490 uint v
= (uint) key
;
2492 if (Elements
.Contains (v
))
2493 lname
= v
.ToString ();
2495 Elements
.Add (v
, sl
);
2496 } else if (compare_type
== TypeManager
.char_type
){
2497 char v
= (char) key
;
2499 if (Elements
.Contains (v
))
2500 lname
= v
.ToString ();
2502 Elements
.Add (v
, sl
);
2503 } else if (compare_type
== TypeManager
.byte_type
){
2504 byte v
= (byte) key
;
2506 if (Elements
.Contains (v
))
2507 lname
= v
.ToString ();
2509 Elements
.Add (v
, sl
);
2510 } else if (compare_type
== TypeManager
.sbyte_type
){
2511 sbyte v
= (sbyte) key
;
2513 if (Elements
.Contains (v
))
2514 lname
= v
.ToString ();
2516 Elements
.Add (v
, sl
);
2517 } else if (compare_type
== TypeManager
.short_type
){
2518 short v
= (short) key
;
2520 if (Elements
.Contains (v
))
2521 lname
= v
.ToString ();
2523 Elements
.Add (v
, sl
);
2524 } else if (compare_type
== TypeManager
.ushort_type
){
2525 ushort v
= (ushort) key
;
2527 if (Elements
.Contains (v
))
2528 lname
= v
.ToString ();
2530 Elements
.Add (v
, sl
);
2531 } else if (compare_type
== TypeManager
.string_type
){
2532 if (key
is NullLiteral
){
2533 if (Elements
.Contains (NullLiteral
.Null
))
2536 Elements
.Add (NullLiteral
.Null
, null);
2538 string s
= (string) key
;
2540 if (Elements
.Contains (s
))
2543 Elements
.Add (s
, sl
);
2545 } else if (compare_type
== TypeManager
.int32_type
) {
2548 if (Elements
.Contains (v
))
2549 lname
= v
.ToString ();
2551 Elements
.Add (v
, sl
);
2552 } else if (compare_type
== TypeManager
.bool_type
) {
2553 bool v
= (bool) key
;
2555 if (Elements
.Contains (v
))
2556 lname
= v
.ToString ();
2558 Elements
.Add (v
, sl
);
2562 throw new Exception ("Unknown switch type!" +
2563 SwitchType
+ " " + compare_type
);
2567 Report
.Error (152, sl
.loc
, Error152
, "case " + lname
);
2578 void EmitObjectInteger (ILGenerator ig
, object k
)
2581 IntConstant
.EmitInt (ig
, (int) k
);
2582 else if (k
is Constant
) {
2583 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
2586 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
2589 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
2591 IntConstant
.EmitInt (ig
, (int) (long) k
);
2592 ig
.Emit (OpCodes
.Conv_I8
);
2595 LongConstant
.EmitLong (ig
, (long) k
);
2597 else if (k
is ulong)
2599 if ((ulong) k
< (1L<<32))
2601 IntConstant
.EmitInt (ig
, (int) (long) k
);
2602 ig
.Emit (OpCodes
.Conv_U8
);
2606 LongConstant
.EmitLong (ig
, unchecked ((long) (ulong) k
));
2610 IntConstant
.EmitInt (ig
, (int) ((char) k
));
2611 else if (k
is sbyte)
2612 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
2614 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
2615 else if (k
is short)
2616 IntConstant
.EmitInt (ig
, (int) ((short) k
));
2617 else if (k
is ushort)
2618 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
2620 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
2622 throw new Exception ("Unhandled case");
2625 // structure used to hold blocks of keys while calculating table switch
2626 class KeyBlock
: IComparable
2628 public KeyBlock (long _nFirst
)
2630 nFirst
= nLast
= _nFirst
;
2634 public ArrayList rgKeys
= null;
2635 // how many items are in the bucket
2636 public int Size
= 1;
2639 get { return (int) (nLast - nFirst + 1); }
2641 public static long TotalLength (KeyBlock kbFirst
, KeyBlock kbLast
)
2643 return kbLast
.nLast
- kbFirst
.nFirst
+ 1;
2645 public int CompareTo (object obj
)
2647 KeyBlock kb
= (KeyBlock
) obj
;
2648 int nLength
= Length
;
2649 int nLengthOther
= kb
.Length
;
2650 if (nLengthOther
== nLength
)
2651 return (int) (kb
.nFirst
- nFirst
);
2652 return nLength
- nLengthOther
;
2657 /// This method emits code for a lookup-based switch statement (non-string)
2658 /// Basically it groups the cases into blocks that are at least half full,
2659 /// and then spits out individual lookup opcodes for each block.
2660 /// It emits the longest blocks first, and short blocks are just
2661 /// handled with direct compares.
2663 /// <param name="ec"></param>
2664 /// <param name="val"></param>
2665 /// <returns></returns>
2666 void TableSwitchEmit (EmitContext ec
, LocalBuilder val
)
2668 int cElements
= Elements
.Count
;
2669 object [] rgKeys
= new object [cElements
];
2670 Elements
.Keys
.CopyTo (rgKeys
, 0);
2671 Array
.Sort (rgKeys
);
2673 // initialize the block list with one element per key
2674 ArrayList rgKeyBlocks
= new ArrayList ();
2675 foreach (object key
in rgKeys
)
2676 rgKeyBlocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
2679 // iteratively merge the blocks while they are at least half full
2680 // there's probably a really cool way to do this with a tree...
2681 while (rgKeyBlocks
.Count
> 1)
2683 ArrayList rgKeyBlocksNew
= new ArrayList ();
2684 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
2685 for (int ikb
= 1; ikb
< rgKeyBlocks
.Count
; ikb
++)
2687 KeyBlock kb
= (KeyBlock
) rgKeyBlocks
[ikb
];
2688 if ((kbCurr
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (kbCurr
, kb
))
2691 kbCurr
.nLast
= kb
.nLast
;
2692 kbCurr
.Size
+= kb
.Size
;
2696 // start a new block
2697 rgKeyBlocksNew
.Add (kbCurr
);
2701 rgKeyBlocksNew
.Add (kbCurr
);
2702 if (rgKeyBlocks
.Count
== rgKeyBlocksNew
.Count
)
2704 rgKeyBlocks
= rgKeyBlocksNew
;
2707 // initialize the key lists
2708 foreach (KeyBlock kb
in rgKeyBlocks
)
2709 kb
.rgKeys
= new ArrayList ();
2711 // fill the key lists
2713 if (rgKeyBlocks
.Count
> 0) {
2714 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
2715 foreach (object key
in rgKeys
)
2717 bool fNextBlock
= (key
is UInt64
) ? (ulong) key
> (ulong) kbCurr
.nLast
:
2718 System
.Convert
.ToInt64 (key
) > kbCurr
.nLast
;
2720 kbCurr
= (KeyBlock
) rgKeyBlocks
[++iBlockCurr
];
2721 kbCurr
.rgKeys
.Add (key
);
2725 // sort the blocks so we can tackle the largest ones first
2726 rgKeyBlocks
.Sort ();
2728 // okay now we can start...
2729 ILGenerator ig
= ec
.ig
;
2730 Label lblEnd
= ig
.DefineLabel (); // at the end ;-)
2731 Label lblDefault
= ig
.DefineLabel ();
2733 Type typeKeys
= null;
2734 if (rgKeys
.Length
> 0)
2735 typeKeys
= rgKeys
[0].GetType (); // used for conversions
2739 if (TypeManager
.IsEnumType (SwitchType
))
2740 compare_type
= TypeManager
.EnumToUnderlying (SwitchType
);
2742 compare_type
= SwitchType
;
2744 for (int iBlock
= rgKeyBlocks
.Count
- 1; iBlock
>= 0; --iBlock
)
2746 KeyBlock kb
= ((KeyBlock
) rgKeyBlocks
[iBlock
]);
2747 lblDefault
= (iBlock
== 0) ? DefaultTarget
: ig
.DefineLabel ();
2750 foreach (object key
in kb
.rgKeys
)
2752 ig
.Emit (OpCodes
.Ldloc
, val
);
2753 EmitObjectInteger (ig
, key
);
2754 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
2755 ig
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
2760 // TODO: if all the keys in the block are the same and there are
2761 // no gaps/defaults then just use a range-check.
2762 if (compare_type
== TypeManager
.int64_type
||
2763 compare_type
== TypeManager
.uint64_type
)
2765 // TODO: optimize constant/I4 cases
2767 // check block range (could be > 2^31)
2768 ig
.Emit (OpCodes
.Ldloc
, val
);
2769 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nFirst
, typeKeys
));
2770 ig
.Emit (OpCodes
.Blt
, lblDefault
);
2771 ig
.Emit (OpCodes
.Ldloc
, val
);
2772 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nLast
, typeKeys
));
2773 ig
.Emit (OpCodes
.Bgt
, lblDefault
);
2776 ig
.Emit (OpCodes
.Ldloc
, val
);
2779 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nFirst
, typeKeys
));
2780 ig
.Emit (OpCodes
.Sub
);
2782 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
2787 ig
.Emit (OpCodes
.Ldloc
, val
);
2788 int nFirst
= (int) kb
.nFirst
;
2791 IntConstant
.EmitInt (ig
, nFirst
);
2792 ig
.Emit (OpCodes
.Sub
);
2794 else if (nFirst
< 0)
2796 IntConstant
.EmitInt (ig
, -nFirst
);
2797 ig
.Emit (OpCodes
.Add
);
2801 // first, build the list of labels for the switch
2803 int cJumps
= kb
.Length
;
2804 Label
[] rgLabels
= new Label
[cJumps
];
2805 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
2807 object key
= kb
.rgKeys
[iKey
];
2808 if (System
.Convert
.ToInt64 (key
) == kb
.nFirst
+ iJump
)
2810 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
2811 rgLabels
[iJump
] = sl
.GetILLabel (ec
);
2815 rgLabels
[iJump
] = lblDefault
;
2817 // emit the switch opcode
2818 ig
.Emit (OpCodes
.Switch
, rgLabels
);
2821 // mark the default for this block
2823 ig
.MarkLabel (lblDefault
);
2826 // TODO: find the default case and emit it here,
2827 // to prevent having to do the following jump.
2828 // make sure to mark other labels in the default section
2830 // the last default just goes to the end
2831 ig
.Emit (OpCodes
.Br
, lblDefault
);
2833 // now emit the code for the sections
2834 bool fFoundDefault
= false;
2835 foreach (SwitchSection ss
in Sections
)
2837 foreach (SwitchLabel sl
in ss
.Labels
)
2839 ig
.MarkLabel (sl
.GetILLabel (ec
));
2840 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
2841 if (sl
.Label
== null)
2843 ig
.MarkLabel (lblDefault
);
2844 fFoundDefault
= true;
2848 //ig.Emit (OpCodes.Br, lblEnd);
2851 if (!fFoundDefault
) {
2852 ig
.MarkLabel (lblDefault
);
2854 ig
.MarkLabel (lblEnd
);
2857 // This simple emit switch works, but does not take advantage of the
2859 // TODO: remove non-string logic from here
2860 // TODO: binary search strings?
2862 void SimpleSwitchEmit (EmitContext ec
, LocalBuilder val
)
2864 ILGenerator ig
= ec
.ig
;
2865 Label end_of_switch
= ig
.DefineLabel ();
2866 Label next_test
= ig
.DefineLabel ();
2867 Label null_target
= ig
.DefineLabel ();
2868 bool default_found
= false;
2869 bool first_test
= true;
2870 bool pending_goto_end
= false;
2872 bool default_at_end
= false;
2874 ig
.Emit (OpCodes
.Ldloc
, val
);
2876 if (Elements
.Contains (NullLiteral
.Null
)){
2877 ig
.Emit (OpCodes
.Brfalse
, null_target
);
2879 ig
.Emit (OpCodes
.Brfalse
, default_target
);
2881 ig
.Emit (OpCodes
.Ldloc
, val
);
2882 ig
.Emit (OpCodes
.Call
, TypeManager
.string_isinterneted_string
);
2883 ig
.Emit (OpCodes
.Stloc
, val
);
2885 int section_count
= Sections
.Count
;
2886 for (int section
= 0; section
< section_count
; section
++){
2887 SwitchSection ss
= (SwitchSection
) Sections
[section
];
2888 Label sec_begin
= ig
.DefineLabel ();
2890 if (pending_goto_end
)
2891 ig
.Emit (OpCodes
.Br
, end_of_switch
);
2893 int label_count
= ss
.Labels
.Count
;
2894 bool mark_default
= false;
2896 for (int label
= 0; label
< label_count
; label
++){
2897 SwitchLabel sl
= (SwitchLabel
) ss
.Labels
[label
];
2898 ig
.MarkLabel (sl
.GetILLabel (ec
));
2901 ig
.MarkLabel (next_test
);
2902 next_test
= ig
.DefineLabel ();
2905 // If we are the default target
2907 if (sl
.Label
== null){
2908 if (label
+1 == label_count
)
2909 default_at_end
= true;
2910 mark_default
= true;
2911 default_found
= true;
2913 object lit
= sl
.Converted
;
2915 if (lit
is NullLiteral
){
2917 if (label_count
== 1)
2918 ig
.Emit (OpCodes
.Br
, next_test
);
2922 StringConstant str
= (StringConstant
) lit
;
2924 ig
.Emit (OpCodes
.Ldloc
, val
);
2925 ig
.Emit (OpCodes
.Ldstr
, str
.Value
);
2926 if (label_count
== 1)
2927 ig
.Emit (OpCodes
.Bne_Un
, next_test
);
2929 if (label
+1 == label_count
)
2930 ig
.Emit (OpCodes
.Bne_Un
, next_test
);
2932 ig
.Emit (OpCodes
.Beq
, sec_begin
);
2937 ig
.MarkLabel (null_target
);
2938 ig
.MarkLabel (sec_begin
);
2939 foreach (SwitchLabel sl
in ss
.Labels
)
2940 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
2943 ig
.MarkLabel (default_target
);
2945 pending_goto_end
= !ss
.Block
.HasRet
;
2948 ig
.MarkLabel (next_test
);
2950 if (!default_at_end
)
2951 ig
.Emit (OpCodes
.Br
, default_target
);
2953 ig
.MarkLabel (default_target
);
2954 ig
.MarkLabel (end_of_switch
);
2957 SwitchSection
FindSection (SwitchLabel label
)
2959 foreach (SwitchSection ss
in Sections
){
2960 foreach (SwitchLabel sl
in ss
.Labels
){
2969 bool ResolveConstantSwitch (EmitContext ec
)
2971 object key
= ((Constant
) new_expr
).GetValue ();
2972 SwitchLabel label
= (SwitchLabel
) Elements
[key
];
2977 constant_section
= FindSection (label
);
2978 if (constant_section
== null)
2981 if (constant_section
.Block
.Resolve (ec
) != true)
2987 public override bool Resolve (EmitContext ec
)
2989 Expr
= Expr
.Resolve (ec
);
2993 new_expr
= SwitchGoverningType (ec
, Expr
.Type
);
2994 if (new_expr
== null){
2995 Report
.Error (151, loc
, "An integer type or string was expected for switch");
3000 SwitchType
= new_expr
.Type
;
3002 if (!CheckSwitch (ec
))
3005 Switch old_switch
= ec
.Switch
;
3007 ec
.Switch
.SwitchType
= SwitchType
;
3009 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
3010 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
3012 is_constant
= new_expr
is Constant
;
3014 object key
= ((Constant
) new_expr
).GetValue ();
3015 SwitchLabel label
= (SwitchLabel
) Elements
[key
];
3017 constant_section
= FindSection (label
);
3021 foreach (SwitchSection ss
in Sections
){
3023 ec
.CurrentBranching
.CreateSibling (
3024 null, FlowBranching
.SiblingType
.SwitchSection
);
3028 if (is_constant
&& (ss
!= constant_section
)) {
3029 // If we're a constant switch, we're only emitting
3030 // one single section - mark all the others as
3032 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3033 if (!ss
.Block
.ResolveUnreachable (ec
, true))
3036 if (!ss
.Block
.Resolve (ec
))
3042 ec
.CurrentBranching
.CreateSibling (
3043 null, FlowBranching
.SiblingType
.SwitchSection
);
3045 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3046 ec
.Switch
= old_switch
;
3048 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
,
3054 protected override void DoEmit (EmitContext ec
)
3056 ILGenerator ig
= ec
.ig
;
3058 // Store variable for comparission purposes
3061 value = ig
.DeclareLocal (SwitchType
);
3063 ig
.Emit (OpCodes
.Stloc
, value);
3067 default_target
= ig
.DefineLabel ();
3070 // Setup the codegen context
3072 Label old_end
= ec
.LoopEnd
;
3073 Switch old_switch
= ec
.Switch
;
3075 ec
.LoopEnd
= ig
.DefineLabel ();
3080 if (constant_section
!= null)
3081 constant_section
.Block
.Emit (ec
);
3082 } else if (SwitchType
== TypeManager
.string_type
)
3083 SimpleSwitchEmit (ec
, value);
3085 TableSwitchEmit (ec
, value);
3087 // Restore context state.
3088 ig
.MarkLabel (ec
.LoopEnd
);
3091 // Restore the previous context
3093 ec
.LoopEnd
= old_end
;
3094 ec
.Switch
= old_switch
;
3098 public abstract class ExceptionStatement
: Statement
3100 public abstract void EmitFinally (EmitContext ec
);
3102 protected bool emit_finally
= true;
3103 ArrayList parent_vectors
;
3105 protected void DoEmitFinally (EmitContext ec
)
3108 ec
.ig
.BeginFinallyBlock ();
3110 ec
.CurrentIterator
.MarkFinally (ec
, parent_vectors
);
3114 protected void ResolveFinally (FlowBranchingException branching
)
3116 emit_finally
= branching
.EmitFinally
;
3118 branching
.Parent
.StealFinallyClauses (ref parent_vectors
);
3122 public class Lock
: ExceptionStatement
{
3124 Statement Statement
;
3127 public Lock (Expression expr
, Statement stmt
, Location l
)
3134 public override bool Resolve (EmitContext ec
)
3136 expr
= expr
.Resolve (ec
);
3140 if (expr
.Type
.IsValueType
){
3141 Error (185, "lock statement requires the expression to be " +
3142 " a reference type (type is: `{0}'",
3143 TypeManager
.CSharpName (expr
.Type
));
3147 FlowBranchingException branching
= ec
.StartFlowBranching (this);
3148 bool ok
= Statement
.Resolve (ec
);
3150 ec
.KillFlowBranching ();
3154 ResolveFinally (branching
);
3156 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3157 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
3158 // Unfortunately, System.Reflection.Emit automatically emits
3159 // a leave to the end of the finally block.
3160 // This is a problem if `returns' is true since we may jump
3161 // to a point after the end of the method.
3162 // As a workaround, emit an explicit ret here.
3163 ec
.NeedReturnLabel ();
3169 protected override void DoEmit (EmitContext ec
)
3171 Type type
= expr
.Type
;
3173 ILGenerator ig
= ec
.ig
;
3174 temp
= ig
.DeclareLocal (type
);
3177 ig
.Emit (OpCodes
.Dup
);
3178 ig
.Emit (OpCodes
.Stloc
, temp
);
3179 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
3183 ig
.BeginExceptionBlock ();
3184 Statement
.Emit (ec
);
3189 ig
.EndExceptionBlock ();
3192 public override void EmitFinally (EmitContext ec
)
3194 ILGenerator ig
= ec
.ig
;
3195 ig
.Emit (OpCodes
.Ldloc
, temp
);
3196 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
3200 public class Unchecked
: Statement
{
3201 public readonly Block Block
;
3203 public Unchecked (Block b
)
3209 public override bool Resolve (EmitContext ec
)
3211 bool previous_state
= ec
.CheckState
;
3212 bool previous_state_const
= ec
.ConstantCheckState
;
3214 ec
.CheckState
= false;
3215 ec
.ConstantCheckState
= false;
3216 bool ret
= Block
.Resolve (ec
);
3217 ec
.CheckState
= previous_state
;
3218 ec
.ConstantCheckState
= previous_state_const
;
3223 protected override void DoEmit (EmitContext ec
)
3225 bool previous_state
= ec
.CheckState
;
3226 bool previous_state_const
= ec
.ConstantCheckState
;
3228 ec
.CheckState
= false;
3229 ec
.ConstantCheckState
= false;
3231 ec
.CheckState
= previous_state
;
3232 ec
.ConstantCheckState
= previous_state_const
;
3236 public class Checked
: Statement
{
3237 public readonly Block Block
;
3239 public Checked (Block b
)
3242 b
.Unchecked
= false;
3245 public override bool Resolve (EmitContext ec
)
3247 bool previous_state
= ec
.CheckState
;
3248 bool previous_state_const
= ec
.ConstantCheckState
;
3250 ec
.CheckState
= true;
3251 ec
.ConstantCheckState
= true;
3252 bool ret
= Block
.Resolve (ec
);
3253 ec
.CheckState
= previous_state
;
3254 ec
.ConstantCheckState
= previous_state_const
;
3259 protected override void DoEmit (EmitContext ec
)
3261 bool previous_state
= ec
.CheckState
;
3262 bool previous_state_const
= ec
.ConstantCheckState
;
3264 ec
.CheckState
= true;
3265 ec
.ConstantCheckState
= true;
3267 ec
.CheckState
= previous_state
;
3268 ec
.ConstantCheckState
= previous_state_const
;
3272 public class Unsafe
: Statement
{
3273 public readonly Block Block
;
3275 public Unsafe (Block b
)
3278 Block
.Unsafe
= true;
3281 public override bool Resolve (EmitContext ec
)
3283 bool previous_state
= ec
.InUnsafe
;
3287 val
= Block
.Resolve (ec
);
3288 ec
.InUnsafe
= previous_state
;
3293 protected override void DoEmit (EmitContext ec
)
3295 bool previous_state
= ec
.InUnsafe
;
3299 ec
.InUnsafe
= previous_state
;
3306 public class Fixed
: Statement
{
3308 ArrayList declarators
;
3309 Statement statement
;
3315 public bool is_object
;
3316 public LocalInfo vi
;
3317 public Expression expr
;
3318 public Expression converted
;
3321 public Fixed (Expression type
, ArrayList decls
, Statement stmt
, Location l
)
3324 declarators
= decls
;
3329 public override bool Resolve (EmitContext ec
)
3332 Expression
.UnsafeError (loc
);
3336 TypeExpr texpr
= type
.ResolveAsTypeTerminal (ec
);
3340 expr_type
= texpr
.Type
;
3342 CheckObsolete (expr_type
);
3344 if (ec
.RemapToProxy
){
3345 Report
.Error (-210, loc
, "Fixed statement not allowed in iterators");
3349 data
= new FixedData
[declarators
.Count
];
3351 if (!expr_type
.IsPointer
){
3352 Report
.Error (209, loc
, "Variables in a fixed statement must be pointers");
3357 foreach (Pair p
in declarators
){
3358 LocalInfo vi
= (LocalInfo
) p
.First
;
3359 Expression e
= (Expression
) p
.Second
;
3361 vi
.VariableInfo
.SetAssigned (ec
);
3365 // The rules for the possible declarators are pretty wise,
3366 // but the production on the grammar is more concise.
3368 // So we have to enforce these rules here.
3370 // We do not resolve before doing the case 1 test,
3371 // because the grammar is explicit in that the token &
3372 // is present, so we need to test for this particular case.
3376 Report
.Error (254, loc
, "Cast expression not allowed as right hand expression in fixed statement");
3381 // Case 1: & object.
3383 if (e
is Unary
&& ((Unary
) e
).Oper
== Unary
.Operator
.AddressOf
){
3384 Expression child
= ((Unary
) e
).Expr
;
3386 if (child
is ParameterReference
|| child
is LocalVariableReference
){
3389 "No need to use fixed statement for parameters or " +
3390 "local variable declarations (address is already " +
3395 ec
.InFixedInitializer
= true;
3397 ec
.InFixedInitializer
= false;
3401 child
= ((Unary
) e
).Expr
;
3403 if (!TypeManager
.VerifyUnManaged (child
.Type
, loc
))
3406 data
[i
].is_object
= true;
3408 data
[i
].converted
= null;
3415 ec
.InFixedInitializer
= true;
3417 ec
.InFixedInitializer
= false;
3424 if (e
.Type
.IsArray
){
3425 Type array_type
= TypeManager
.GetElementType (e
.Type
);
3428 // Provided that array_type is unmanaged,
3430 if (!TypeManager
.VerifyUnManaged (array_type
, loc
))
3434 // and T* is implicitly convertible to the
3435 // pointer type given in the fixed statement.
3437 ArrayPtr array_ptr
= new ArrayPtr (e
, loc
);
3439 Expression converted
= Convert
.ImplicitConversionRequired (
3440 ec
, array_ptr
, vi
.VariableType
, loc
);
3441 if (converted
== null)
3444 data
[i
].is_object
= false;
3446 data
[i
].converted
= converted
;
3456 if (e
.Type
== TypeManager
.string_type
){
3457 data
[i
].is_object
= false;
3459 data
[i
].converted
= null;
3466 // For other cases, flag a `this is already fixed expression'
3468 if (e
is LocalVariableReference
|| e
is ParameterReference
||
3469 Convert
.ImplicitConversionExists (ec
, e
, vi
.VariableType
)){
3471 Report
.Error (245, loc
, "right hand expression is already fixed, no need to use fixed statement ");
3475 Report
.Error (245, loc
, "Fixed statement only allowed on strings, arrays or address-of expressions");
3479 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
3481 if (!statement
.Resolve (ec
)) {
3482 ec
.KillFlowBranching ();
3486 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3487 has_ret
= reachability
.IsUnreachable
;
3492 protected override void DoEmit (EmitContext ec
)
3494 ILGenerator ig
= ec
.ig
;
3496 LocalBuilder
[] clear_list
= new LocalBuilder
[data
.Length
];
3498 for (int i
= 0; i
< data
.Length
; i
++) {
3499 LocalInfo vi
= data
[i
].vi
;
3502 // Case 1: & object.
3504 if (data
[i
].is_object
) {
3506 // Store pointer in pinned location
3508 data
[i
].expr
.Emit (ec
);
3509 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3510 clear_list
[i
] = vi
.LocalBuilder
;
3517 if (data
[i
].expr
.Type
.IsArray
){
3519 // Store pointer in pinned location
3521 data
[i
].converted
.Emit (ec
);
3523 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3524 clear_list
[i
] = vi
.LocalBuilder
;
3531 if (data
[i
].expr
.Type
== TypeManager
.string_type
){
3532 LocalBuilder pinned_string
= TypeManager
.DeclareLocalPinned (ig
, TypeManager
.string_type
);
3533 clear_list
[i
] = pinned_string
;
3535 data
[i
].expr
.Emit (ec
);
3536 ig
.Emit (OpCodes
.Stloc
, pinned_string
);
3538 Expression sptr
= new StringPtr (pinned_string
, loc
);
3539 Expression converted
= Convert
.ImplicitConversionRequired (
3540 ec
, sptr
, vi
.VariableType
, loc
);
3542 if (converted
== null)
3545 converted
.Emit (ec
);
3546 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3550 statement
.Emit (ec
);
3556 // Clear the pinned variable
3558 for (int i
= 0; i
< data
.Length
; i
++) {
3559 if (data
[i
].is_object
|| data
[i
].expr
.Type
.IsArray
) {
3560 ig
.Emit (OpCodes
.Ldc_I4_0
);
3561 ig
.Emit (OpCodes
.Conv_U
);
3562 ig
.Emit (OpCodes
.Stloc
, clear_list
[i
]);
3563 } else if (data
[i
].expr
.Type
== TypeManager
.string_type
){
3564 ig
.Emit (OpCodes
.Ldnull
);
3565 ig
.Emit (OpCodes
.Stloc
, clear_list
[i
]);
3571 public class Catch
: Statement
{
3572 public readonly string Name
;
3573 public readonly Block Block
;
3575 Expression type_expr
;
3578 public Catch (Expression type
, string name
, Block block
, Location l
)
3586 public Type CatchType
{
3592 public bool IsGeneral
{
3594 return type_expr
== null;
3598 protected override void DoEmit(EmitContext ec
)
3602 public override bool Resolve (EmitContext ec
)
3604 if (type_expr
!= null) {
3605 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
);
3611 CheckObsolete (type
);
3613 if (type
!= TypeManager
.exception_type
&& !type
.IsSubclassOf (TypeManager
.exception_type
)){
3614 Error (155, "The type caught or thrown must be derived from System.Exception");
3620 return Block
.Resolve (ec
);
3624 public class Try
: ExceptionStatement
{
3625 public readonly Block Fini
, Block
;
3626 public readonly ArrayList Specific
;
3627 public readonly Catch General
;
3629 bool need_exc_block
;
3632 // specific, general and fini might all be null.
3634 public Try (Block block
, ArrayList specific
, Catch general
, Block fini
, Location l
)
3636 if (specific
== null && general
== null){
3637 Console
.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3641 this.Specific
= specific
;
3642 this.General
= general
;
3647 public override bool Resolve (EmitContext ec
)
3651 FlowBranchingException branching
= ec
.StartFlowBranching (this);
3653 Report
.Debug (1, "START OF TRY BLOCK", Block
.StartLocation
);
3655 if (!Block
.Resolve (ec
))
3658 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
3660 Report
.Debug (1, "START OF CATCH BLOCKS", vector
);
3662 Type
[] prevCatches
= new Type
[Specific
.Count
];
3664 foreach (Catch c
in Specific
){
3665 ec
.CurrentBranching
.CreateSibling (
3666 c
.Block
, FlowBranching
.SiblingType
.Catch
);
3668 Report
.Debug (1, "STARTED SIBLING FOR CATCH", ec
.CurrentBranching
);
3670 if (c
.Name
!= null) {
3671 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
3673 throw new Exception ();
3675 vi
.VariableInfo
= null;
3678 if (!c
.Resolve (ec
))
3681 Type resolvedType
= c
.CatchType
;
3682 for (int ii
= 0; ii
< last_index
; ++ii
) {
3683 if (resolvedType
== prevCatches
[ii
] || resolvedType
.IsSubclassOf (prevCatches
[ii
])) {
3684 Report
.Error (160, c
.loc
, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches
[ii
].FullName
);
3689 prevCatches
[last_index
++] = resolvedType
;
3690 need_exc_block
= true;
3693 Report
.Debug (1, "END OF CATCH BLOCKS", ec
.CurrentBranching
);
3695 if (General
!= null){
3696 ec
.CurrentBranching
.CreateSibling (
3697 General
.Block
, FlowBranching
.SiblingType
.Catch
);
3699 Report
.Debug (1, "STARTED SIBLING FOR GENERAL", ec
.CurrentBranching
);
3701 if (!General
.Resolve (ec
))
3704 need_exc_block
= true;
3707 Report
.Debug (1, "END OF GENERAL CATCH BLOCKS", ec
.CurrentBranching
);
3711 ec
.CurrentBranching
.CreateSibling (
3712 Fini
, FlowBranching
.SiblingType
.Finally
);
3714 Report
.Debug (1, "STARTED SIBLING FOR FINALLY", ec
.CurrentBranching
, vector
);
3716 if (!Fini
.Resolve (ec
))
3720 ResolveFinally (branching
);
3721 need_exc_block
|= emit_finally
;
3723 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3725 FlowBranching
.UsageVector f_vector
= ec
.CurrentBranching
.CurrentUsageVector
;
3727 Report
.Debug (1, "END OF TRY", ec
.CurrentBranching
, reachability
, vector
, f_vector
);
3729 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
3730 // Unfortunately, System.Reflection.Emit automatically emits
3731 // a leave to the end of the finally block. This is a problem
3732 // if `returns' is true since we may jump to a point after the
3733 // end of the method.
3734 // As a workaround, emit an explicit ret here.
3735 ec
.NeedReturnLabel ();
3741 protected override void DoEmit (EmitContext ec
)
3743 ILGenerator ig
= ec
.ig
;
3746 ig
.BeginExceptionBlock ();
3749 foreach (Catch c
in Specific
){
3752 ig
.BeginCatchBlock (c
.CatchType
);
3754 if (c
.Name
!= null){
3755 vi
= c
.Block
.GetLocalInfo (c
.Name
);
3757 throw new Exception ("Variable does not exist in this block");
3759 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3761 ig
.Emit (OpCodes
.Pop
);
3766 if (General
!= null){
3767 ig
.BeginCatchBlock (TypeManager
.object_type
);
3768 ig
.Emit (OpCodes
.Pop
);
3769 General
.Block
.Emit (ec
);
3774 ig
.EndExceptionBlock ();
3777 public override void EmitFinally (EmitContext ec
)
3785 public class Using
: ExceptionStatement
{
3786 object expression_or_block
;
3787 Statement Statement
;
3792 Expression
[] resolved_vars
;
3793 Expression
[] converted_vars
;
3794 ExpressionStatement
[] assign
;
3795 LocalBuilder local_copy
;
3797 public Using (object expression_or_block
, Statement stmt
, Location l
)
3799 this.expression_or_block
= expression_or_block
;
3805 // Resolves for the case of using using a local variable declaration.
3807 bool ResolveLocalVariableDecls (EmitContext ec
)
3811 TypeExpr texpr
= expr
.ResolveAsTypeTerminal (ec
);
3815 expr_type
= texpr
.Type
;
3818 // The type must be an IDisposable or an implicit conversion
3821 converted_vars
= new Expression
[var_list
.Count
];
3822 resolved_vars
= new Expression
[var_list
.Count
];
3823 assign
= new ExpressionStatement
[var_list
.Count
];
3825 bool need_conv
= !TypeManager
.ImplementsInterface (
3826 expr_type
, TypeManager
.idisposable_type
);
3828 foreach (DictionaryEntry e
in var_list
){
3829 Expression
var = (Expression
) e
.Key
;
3831 var = var.ResolveLValue (ec
, new EmptyExpression ());
3835 resolved_vars
[i
] = var;
3842 converted_vars
[i
] = Convert
.ImplicitConversionRequired (
3843 ec
, var, TypeManager
.idisposable_type
, loc
);
3845 if (converted_vars
[i
] == null)
3852 foreach (DictionaryEntry e
in var_list
){
3853 Expression
var = resolved_vars
[i
];
3854 Expression new_expr
= (Expression
) e
.Value
;
3857 a
= new Assign (var, new_expr
, loc
);
3863 converted_vars
[i
] = var;
3864 assign
[i
] = (ExpressionStatement
) a
;
3871 bool ResolveExpression (EmitContext ec
)
3873 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
)){
3874 conv
= Convert
.ImplicitConversionRequired (
3875 ec
, expr
, TypeManager
.idisposable_type
, loc
);
3885 // Emits the code for the case of using using a local variable declaration.
3887 void EmitLocalVariableDecls (EmitContext ec
)
3889 ILGenerator ig
= ec
.ig
;
3892 for (i
= 0; i
< assign
.Length
; i
++) {
3893 assign
[i
].EmitStatement (ec
);
3896 ig
.BeginExceptionBlock ();
3898 Statement
.Emit (ec
);
3900 var_list
.Reverse ();
3905 void EmitLocalVariableDeclFinally (EmitContext ec
)
3907 ILGenerator ig
= ec
.ig
;
3909 int i
= assign
.Length
;
3910 for (int ii
= 0; ii
< var_list
.Count
; ++ii
){
3911 Expression
var = resolved_vars
[--i
];
3912 Label skip
= ig
.DefineLabel ();
3914 ig
.BeginFinallyBlock ();
3916 if (!var.Type
.IsValueType
) {
3918 ig
.Emit (OpCodes
.Brfalse
, skip
);
3919 converted_vars
[i
].Emit (ec
);
3920 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
3922 Expression ml
= Expression
.MemberLookup(ec
, TypeManager
.idisposable_type
, var.Type
, "Dispose", Mono
.CSharp
.Location
.Null
);
3924 if (!(ml
is MethodGroupExpr
)) {
3926 ig
.Emit (OpCodes
.Box
, var.Type
);
3927 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
3929 MethodInfo mi
= null;
3931 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
3932 if (TypeManager
.GetArgumentTypes (mk
).Length
== 0) {
3939 Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
3943 IMemoryLocation mloc
= (IMemoryLocation
) var;
3945 mloc
.AddressOf (ec
, AddressOp
.Load
);
3946 ig
.Emit (OpCodes
.Call
, mi
);
3950 ig
.MarkLabel (skip
);
3953 ig
.EndExceptionBlock ();
3955 ig
.BeginFinallyBlock ();
3960 void EmitExpression (EmitContext ec
)
3963 // Make a copy of the expression and operate on that.
3965 ILGenerator ig
= ec
.ig
;
3966 local_copy
= ig
.DeclareLocal (expr_type
);
3971 ig
.Emit (OpCodes
.Stloc
, local_copy
);
3974 ig
.BeginExceptionBlock ();
3976 Statement
.Emit (ec
);
3980 ig
.EndExceptionBlock ();
3983 void EmitExpressionFinally (EmitContext ec
)
3985 ILGenerator ig
= ec
.ig
;
3986 if (!local_copy
.LocalType
.IsValueType
) {
3987 Label skip
= ig
.DefineLabel ();
3988 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
3989 ig
.Emit (OpCodes
.Brfalse
, skip
);
3990 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
3991 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
3992 ig
.MarkLabel (skip
);
3994 Expression ml
= Expression
.MemberLookup(ec
, TypeManager
.idisposable_type
, local_copy
.LocalType
, "Dispose", Mono
.CSharp
.Location
.Null
);
3996 if (!(ml
is MethodGroupExpr
)) {
3997 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
3998 ig
.Emit (OpCodes
.Box
, local_copy
.LocalType
);
3999 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4001 MethodInfo mi
= null;
4003 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
4004 if (TypeManager
.GetArgumentTypes (mk
).Length
== 0) {
4011 Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
4015 ig
.Emit (OpCodes
.Ldloca
, local_copy
);
4016 ig
.Emit (OpCodes
.Call
, mi
);
4021 public override bool Resolve (EmitContext ec
)
4023 if (expression_or_block
is DictionaryEntry
){
4024 expr
= (Expression
) ((DictionaryEntry
) expression_or_block
).Key
;
4025 var_list
= (ArrayList
)((DictionaryEntry
)expression_or_block
).Value
;
4027 if (!ResolveLocalVariableDecls (ec
))
4030 } else if (expression_or_block
is Expression
){
4031 expr
= (Expression
) expression_or_block
;
4033 expr
= expr
.Resolve (ec
);
4037 expr_type
= expr
.Type
;
4039 if (!ResolveExpression (ec
))
4043 FlowBranchingException branching
= ec
.StartFlowBranching (this);
4045 bool ok
= Statement
.Resolve (ec
);
4048 ec
.KillFlowBranching ();
4052 ResolveFinally (branching
);
4053 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
4055 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
4056 // Unfortunately, System.Reflection.Emit automatically emits a leave
4057 // to the end of the finally block. This is a problem if `returns'
4058 // is true since we may jump to a point after the end of the method.
4059 // As a workaround, emit an explicit ret here.
4060 ec
.NeedReturnLabel ();
4066 protected override void DoEmit (EmitContext ec
)
4068 if (expression_or_block
is DictionaryEntry
)
4069 EmitLocalVariableDecls (ec
);
4070 else if (expression_or_block
is Expression
)
4071 EmitExpression (ec
);
4074 public override void EmitFinally (EmitContext ec
)
4076 if (expression_or_block
is DictionaryEntry
)
4077 EmitLocalVariableDeclFinally (ec
);
4078 else if (expression_or_block
is Expression
)
4079 EmitExpressionFinally (ec
);
4084 /// Implementation of the foreach C# statement
4086 public class Foreach
: ExceptionStatement
{
4088 Expression variable
;
4090 Statement statement
;
4091 ForeachHelperMethods hm
;
4092 Expression empty
, conv
;
4093 Type array_type
, element_type
;
4095 VariableStorage enumerator
;
4097 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
4098 Statement stmt
, Location l
)
4101 this.variable
= var;
4107 public override bool Resolve (EmitContext ec
)
4109 expr
= expr
.Resolve (ec
);
4113 if (expr
is NullLiteral
) {
4114 Report
.Error (186, expr
.Location
, "Use of null is not valid in this context");
4118 TypeExpr texpr
= type
.ResolveAsTypeTerminal (ec
);
4122 var_type
= texpr
.Type
;
4125 // We need an instance variable. Not sure this is the best
4126 // way of doing this.
4128 // FIXME: When we implement propertyaccess, will those turn
4129 // out to return values in ExprClass? I think they should.
4131 if (!(expr
.eclass
== ExprClass
.Variable
|| expr
.eclass
== ExprClass
.Value
||
4132 expr
.eclass
== ExprClass
.PropertyAccess
|| expr
.eclass
== ExprClass
.IndexerAccess
)){
4133 error1579 (expr
.Type
);
4137 if (expr
.Type
.IsArray
) {
4138 array_type
= expr
.Type
;
4139 element_type
= TypeManager
.GetElementType (array_type
);
4141 empty
= new EmptyExpression (element_type
);
4143 hm
= ProbeCollectionType (ec
, expr
.Type
);
4145 error1579 (expr
.Type
);
4149 array_type
= expr
.Type
;
4150 element_type
= hm
.element_type
;
4152 empty
= new EmptyExpression (hm
.element_type
);
4157 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
4158 ec
.CurrentBranching
.CreateSibling ();
4162 // FIXME: maybe we can apply the same trick we do in the
4163 // array handling to avoid creating empty and conv in some cases.
4165 // Although it is not as important in this case, as the type
4166 // will not likely be object (what the enumerator will return).
4168 conv
= Convert
.ExplicitConversion (ec
, empty
, var_type
, loc
);
4172 variable
= variable
.ResolveLValue (ec
, empty
);
4173 if (variable
== null)
4176 bool disposable
= (hm
!= null) && hm
.is_disposable
;
4177 FlowBranchingException branching
= null;
4179 branching
= ec
.StartFlowBranching (this);
4181 if (!statement
.Resolve (ec
))
4185 ResolveFinally (branching
);
4186 ec
.EndFlowBranching ();
4188 emit_finally
= true;
4190 ec
.EndFlowBranching ();
4196 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4198 static MethodInfo
FetchMethodMoveNext (Type t
)
4200 MemberList move_next_list
;
4202 move_next_list
= TypeContainer
.FindMembers (
4203 t
, MemberTypes
.Method
,
4204 BindingFlags
.Public
| BindingFlags
.Instance
,
4205 Type
.FilterName
, "MoveNext");
4206 if (move_next_list
.Count
== 0)
4209 foreach (MemberInfo m
in move_next_list
){
4210 MethodInfo mi
= (MethodInfo
) m
;
4213 args
= TypeManager
.GetArgumentTypes (mi
);
4214 if (args
!= null && args
.Length
== 0){
4215 if (TypeManager
.TypeToCoreType (mi
.ReturnType
) == TypeManager
.bool_type
)
4223 // Retrieves a `public T get_Current ()' method from the Type `t'
4225 static MethodInfo
FetchMethodGetCurrent (Type t
)
4227 MemberList get_current_list
;
4229 get_current_list
= TypeContainer
.FindMembers (
4230 t
, MemberTypes
.Method
,
4231 BindingFlags
.Public
| BindingFlags
.Instance
,
4232 Type
.FilterName
, "get_Current");
4233 if (get_current_list
.Count
== 0)
4236 foreach (MemberInfo m
in get_current_list
){
4237 MethodInfo mi
= (MethodInfo
) m
;
4240 args
= TypeManager
.GetArgumentTypes (mi
);
4241 if (args
!= null && args
.Length
== 0)
4248 // Retrieves a `public void Dispose ()' method from the Type `t'
4250 static MethodInfo
FetchMethodDispose (Type t
)
4252 MemberList dispose_list
;
4254 dispose_list
= TypeContainer
.FindMembers (
4255 t
, MemberTypes
.Method
,
4256 BindingFlags
.Public
| BindingFlags
.Instance
,
4257 Type
.FilterName
, "Dispose");
4258 if (dispose_list
.Count
== 0)
4261 foreach (MemberInfo m
in dispose_list
){
4262 MethodInfo mi
= (MethodInfo
) m
;
4265 args
= TypeManager
.GetArgumentTypes (mi
);
4266 if (args
!= null && args
.Length
== 0){
4267 if (mi
.ReturnType
== TypeManager
.void_type
)
4275 // This struct records the helper methods used by the Foreach construct
4277 class ForeachHelperMethods
{
4278 public EmitContext ec
;
4279 public MethodInfo get_enumerator
;
4280 public MethodInfo move_next
;
4281 public MethodInfo get_current
;
4282 public Type element_type
;
4283 public Type enumerator_type
;
4284 public bool is_disposable
;
4286 public ForeachHelperMethods (EmitContext ec
)
4289 this.element_type
= TypeManager
.object_type
;
4290 this.enumerator_type
= TypeManager
.ienumerator_type
;
4291 this.is_disposable
= true;
4295 static bool GetEnumeratorFilter (MemberInfo m
, object criteria
)
4300 if (!(m
is MethodInfo
))
4303 if (m
.Name
!= "GetEnumerator")
4306 MethodInfo mi
= (MethodInfo
) m
;
4307 Type
[] args
= TypeManager
.GetArgumentTypes (mi
);
4309 if (args
.Length
!= 0)
4312 ForeachHelperMethods hm
= (ForeachHelperMethods
) criteria
;
4314 // Check whether GetEnumerator is public
4315 if ((mi
.Attributes
& MethodAttributes
.Public
) != MethodAttributes
.Public
)
4318 if ((mi
.ReturnType
== TypeManager
.ienumerator_type
) && (mi
.DeclaringType
== TypeManager
.string_type
))
4320 // Apply the same optimization as MS: skip the GetEnumerator
4321 // returning an IEnumerator, and use the one returning a
4322 // CharEnumerator instead. This allows us to avoid the
4323 // try-finally block and the boxing.
4328 // Ok, we can access it, now make sure that we can do something
4329 // with this `GetEnumerator'
4332 Type return_type
= mi
.ReturnType
;
4333 if (mi
.ReturnType
== TypeManager
.ienumerator_type
||
4334 TypeManager
.ienumerator_type
.IsAssignableFrom (return_type
) ||
4335 (!RootContext
.StdLib
&& TypeManager
.ImplementsInterface (return_type
, TypeManager
.ienumerator_type
))) {
4338 // If it is not an interface, lets try to find the methods ourselves.
4339 // For example, if we have:
4340 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4341 // We can avoid the iface call. This is a runtime perf boost.
4342 // even bigger if we have a ValueType, because we avoid the cost
4345 // We have to make sure that both methods exist for us to take
4346 // this path. If one of the methods does not exist, we will just
4347 // use the interface. Sadly, this complex if statement is the only
4348 // way I could do this without a goto
4351 if (return_type
.IsInterface
||
4352 (hm
.move_next
= FetchMethodMoveNext (return_type
)) == null ||
4353 (hm
.get_current
= FetchMethodGetCurrent (return_type
)) == null) {
4355 hm
.move_next
= TypeManager
.bool_movenext_void
;
4356 hm
.get_current
= TypeManager
.object_getcurrent_void
;
4363 // Ok, so they dont return an IEnumerable, we will have to
4364 // find if they support the GetEnumerator pattern.
4367 hm
.move_next
= FetchMethodMoveNext (return_type
);
4368 if (hm
.move_next
== null)
4371 hm
.get_current
= FetchMethodGetCurrent (return_type
);
4372 if (hm
.get_current
== null)
4376 hm
.element_type
= hm
.get_current
.ReturnType
;
4377 hm
.enumerator_type
= return_type
;
4378 hm
.is_disposable
= !hm
.enumerator_type
.IsSealed
||
4379 TypeManager
.ImplementsInterface (
4380 hm
.enumerator_type
, TypeManager
.idisposable_type
);
4386 /// This filter is used to find the GetEnumerator method
4387 /// on which IEnumerator operates
4389 static MemberFilter FilterEnumerator
;
4393 FilterEnumerator
= new MemberFilter (GetEnumeratorFilter
);
4396 void error1579 (Type t
)
4398 Report
.Error (1579, loc
,
4399 "foreach statement cannot operate on variables of type `" +
4400 t
.FullName
+ "' because that class does not provide a " +
4401 " GetEnumerator method or it is inaccessible");
4404 static bool TryType (Type t
, ForeachHelperMethods hm
)
4408 mi
= TypeContainer
.FindMembers (t
, MemberTypes
.Method
,
4409 BindingFlags
.Public
| BindingFlags
.NonPublic
|
4410 BindingFlags
.Instance
| BindingFlags
.DeclaredOnly
,
4411 FilterEnumerator
, hm
);
4416 hm
.get_enumerator
= (MethodInfo
) mi
[0];
4421 // Looks for a usable GetEnumerator in the Type, and if found returns
4422 // the three methods that participate: GetEnumerator, MoveNext and get_Current
4424 ForeachHelperMethods
ProbeCollectionType (EmitContext ec
, Type t
)
4426 ForeachHelperMethods hm
= new ForeachHelperMethods (ec
);
4428 for (Type tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
4429 if (TryType (tt
, hm
))
4435 // Now try to find the method in the interfaces
4438 Type
[] ifaces
= t
.GetInterfaces ();
4440 foreach (Type i
in ifaces
){
4441 if (TryType (i
, hm
))
4446 // Since TypeBuilder.GetInterfaces only returns the interface
4447 // types for this type, we have to keep looping, but once
4448 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4449 // done, because it returns all the types
4451 if ((t
is TypeBuilder
))
4461 // FIXME: possible optimization.
4462 // We might be able to avoid creating `empty' if the type is the sam
4464 bool EmitCollectionForeach (EmitContext ec
)
4466 ILGenerator ig
= ec
.ig
;
4468 enumerator
= new VariableStorage (ec
, hm
.enumerator_type
);
4469 enumerator
.EmitThis (ig
);
4471 // Instantiate the enumerator
4473 if (expr
.Type
.IsValueType
){
4474 IMemoryLocation ml
= expr
as IMemoryLocation
;
4475 // Load the address of the value type.
4477 // This happens if, for example, you have a property
4478 // returning a struct which is IEnumerable
4479 LocalBuilder t
= ec
.GetTemporaryLocal (expr
.Type
);
4481 ig
.Emit (OpCodes
.Stloc
, t
);
4482 ig
.Emit (OpCodes
.Ldloca
, t
);
4483 ec
.FreeTemporaryLocal (t
, expr
.Type
);
4485 ml
.AddressOf (ec
, AddressOp
.Load
);
4489 if (hm
.get_enumerator
.DeclaringType
.IsValueType
) {
4490 // the method is declared on the value type
4491 ig
.Emit (OpCodes
.Call
, hm
.get_enumerator
);
4493 // it is an interface method, so we must box
4494 ig
.Emit (OpCodes
.Box
, expr
.Type
);
4495 ig
.Emit (OpCodes
.Callvirt
, hm
.get_enumerator
);
4499 ig
.Emit (OpCodes
.Callvirt
, hm
.get_enumerator
);
4501 enumerator
.EmitStore (ig
);
4504 // Protect the code in a try/finalize block, so that
4505 // if the beast implement IDisposable, we get rid of it
4507 if (hm
.is_disposable
&& emit_finally
)
4508 ig
.BeginExceptionBlock ();
4510 Label end_try
= ig
.DefineLabel ();
4512 ig
.MarkLabel (ec
.LoopBegin
);
4514 enumerator
.EmitCall (ig
, hm
.move_next
);
4516 ig
.Emit (OpCodes
.Brfalse
, end_try
);
4519 ig
.Emit (OpCodes
.Ldarg_0
);
4521 enumerator
.EmitCall (ig
, hm
.get_current
);
4525 ig
.Emit (OpCodes
.Stfld
, ((LocalVariableReference
) variable
).local_info
.FieldBuilder
);
4527 ((IAssignMethod
)variable
).EmitAssign (ec
, conv
, false, false);
4529 statement
.Emit (ec
);
4530 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
4531 ig
.MarkLabel (end_try
);
4533 // The runtime provides this for us.
4534 // ig.Emit (OpCodes.Leave, end);
4537 // Now the finally block
4539 if (hm
.is_disposable
) {
4542 ig
.EndExceptionBlock ();
4545 ig
.MarkLabel (ec
.LoopEnd
);
4549 public override void EmitFinally (EmitContext ec
)
4551 ILGenerator ig
= ec
.ig
;
4553 if (hm
.enumerator_type
.IsValueType
) {
4554 enumerator
.EmitThis (ig
);
4556 MethodInfo mi
= FetchMethodDispose (hm
.enumerator_type
);
4558 enumerator
.EmitLoadAddress (ig
);
4559 ig
.Emit (OpCodes
.Call
, mi
);
4561 enumerator
.EmitLoad (ig
);
4562 ig
.Emit (OpCodes
.Box
, hm
.enumerator_type
);
4563 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4566 Label call_dispose
= ig
.DefineLabel ();
4568 enumerator
.EmitThis (ig
);
4569 enumerator
.EmitLoad (ig
);
4570 ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
4571 ig
.Emit (OpCodes
.Dup
);
4572 ig
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
4573 ig
.Emit (OpCodes
.Pop
);
4575 Label end_finally
= ig
.DefineLabel ();
4576 ig
.Emit (OpCodes
.Br
, end_finally
);
4578 ig
.MarkLabel (call_dispose
);
4579 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4580 ig
.MarkLabel (end_finally
);
4583 ig
.Emit (OpCodes
.Endfinally
);
4588 // FIXME: possible optimization.
4589 // We might be able to avoid creating `empty' if the type is the sam
4591 bool EmitArrayForeach (EmitContext ec
)
4593 int rank
= array_type
.GetArrayRank ();
4594 ILGenerator ig
= ec
.ig
;
4596 VariableStorage copy
= new VariableStorage (ec
, array_type
);
4599 // Make our copy of the array
4603 copy
.EmitStore (ig
);
4606 VariableStorage counter
= new VariableStorage (ec
,TypeManager
.int32_type
);
4610 counter
.EmitThis (ig
);
4611 ig
.Emit (OpCodes
.Ldc_I4_0
);
4612 counter
.EmitStore (ig
);
4613 test
= ig
.DefineLabel ();
4614 ig
.Emit (OpCodes
.Br
, test
);
4616 loop
= ig
.DefineLabel ();
4617 ig
.MarkLabel (loop
);
4620 ig
.Emit (OpCodes
.Ldarg_0
);
4624 counter
.EmitThis (ig
);
4625 counter
.EmitLoad (ig
);
4628 // Load the value, we load the value using the underlying type,
4629 // then we use the variable.EmitAssign to load using the proper cast.
4631 ArrayAccess
.EmitLoadOpcode (ig
, element_type
);
4634 ig
.Emit (OpCodes
.Stfld
, ((LocalVariableReference
) variable
).local_info
.FieldBuilder
);
4636 ((IAssignMethod
)variable
).EmitAssign (ec
, conv
, false, false);
4638 statement
.Emit (ec
);
4640 ig
.MarkLabel (ec
.LoopBegin
);
4641 counter
.EmitThis (ig
);
4642 counter
.EmitThis (ig
);
4643 counter
.EmitLoad (ig
);
4644 ig
.Emit (OpCodes
.Ldc_I4_1
);
4645 ig
.Emit (OpCodes
.Add
);
4646 counter
.EmitStore (ig
);
4648 ig
.MarkLabel (test
);
4649 counter
.EmitThis (ig
);
4650 counter
.EmitLoad (ig
);
4653 ig
.Emit (OpCodes
.Ldlen
);
4654 ig
.Emit (OpCodes
.Conv_I4
);
4655 ig
.Emit (OpCodes
.Blt
, loop
);
4657 VariableStorage
[] dim_len
= new VariableStorage
[rank
];
4658 VariableStorage
[] dim_count
= new VariableStorage
[rank
];
4659 Label
[] loop
= new Label
[rank
];
4660 Label
[] test
= new Label
[rank
];
4663 for (dim
= 0; dim
< rank
; dim
++){
4664 dim_len
[dim
] = new VariableStorage (ec
, TypeManager
.int32_type
);
4665 dim_count
[dim
] = new VariableStorage (ec
, TypeManager
.int32_type
);
4666 test
[dim
] = ig
.DefineLabel ();
4667 loop
[dim
] = ig
.DefineLabel ();
4670 for (dim
= 0; dim
< rank
; dim
++){
4671 dim_len
[dim
].EmitThis (ig
);
4674 IntLiteral
.EmitInt (ig
, dim
);
4675 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.int_getlength_int
);
4676 dim_len
[dim
].EmitStore (ig
);
4680 for (dim
= 0; dim
< rank
; dim
++){
4681 dim_count
[dim
].EmitThis (ig
);
4682 ig
.Emit (OpCodes
.Ldc_I4_0
);
4683 dim_count
[dim
].EmitStore (ig
);
4684 ig
.Emit (OpCodes
.Br
, test
[dim
]);
4685 ig
.MarkLabel (loop
[dim
]);
4689 ig
.Emit (OpCodes
.Ldarg_0
);
4693 for (dim
= 0; dim
< rank
; dim
++){
4694 dim_count
[dim
].EmitThis (ig
);
4695 dim_count
[dim
].EmitLoad (ig
);
4699 // FIXME: Maybe we can cache the computation of `get'?
4701 Type
[] args
= new Type
[rank
];
4704 for (int i
= 0; i
< rank
; i
++)
4705 args
[i
] = TypeManager
.int32_type
;
4707 ModuleBuilder mb
= CodeGen
.Module
.Builder
;
4708 get = mb
.GetArrayMethod (
4710 CallingConventions
.HasThis
| CallingConventions
.Standard
,
4712 ig
.Emit (OpCodes
.Call
, get);
4715 ig
.Emit (OpCodes
.Stfld
, ((LocalVariableReference
) variable
).local_info
.FieldBuilder
);
4717 ((IAssignMethod
)variable
).EmitAssign (ec
, conv
, false, false);
4718 statement
.Emit (ec
);
4719 ig
.MarkLabel (ec
.LoopBegin
);
4720 for (dim
= rank
- 1; dim
>= 0; dim
--){
4721 dim_count
[dim
].EmitThis (ig
);
4722 dim_count
[dim
].EmitThis (ig
);
4723 dim_count
[dim
].EmitLoad (ig
);
4724 ig
.Emit (OpCodes
.Ldc_I4_1
);
4725 ig
.Emit (OpCodes
.Add
);
4726 dim_count
[dim
].EmitStore (ig
);
4728 ig
.MarkLabel (test
[dim
]);
4729 dim_count
[dim
].EmitThis (ig
);
4730 dim_count
[dim
].EmitLoad (ig
);
4731 dim_len
[dim
].EmitThis (ig
);
4732 dim_len
[dim
].EmitLoad (ig
);
4733 ig
.Emit (OpCodes
.Blt
, loop
[dim
]);
4736 ig
.MarkLabel (ec
.LoopEnd
);
4741 protected override void DoEmit (EmitContext ec
)
4743 ILGenerator ig
= ec
.ig
;
4745 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
4746 ec
.LoopBegin
= ig
.DefineLabel ();
4747 ec
.LoopEnd
= ig
.DefineLabel ();
4750 EmitCollectionForeach (ec
);
4752 EmitArrayForeach (ec
);
4754 ec
.LoopBegin
= old_begin
;
4755 ec
.LoopEnd
= old_end
;