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
;
17 using System
.Collections
;
18 using System
.Collections
.Specialized
;
20 namespace Mono
.CSharp
{
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.
50 Report
.Warning (162, 2, loc
, "Unreachable code detected");
52 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
53 bool ok
= Resolve (ec
);
54 ec
.KillFlowBranching ();
60 /// Return value indicates whether all code paths emitted return.
62 protected abstract void DoEmit (EmitContext ec
);
65 /// Utility wrapper routine for Error, just to beautify the code
67 public void Error (int error
, string format
, params object[] args
)
69 Error (error
, String
.Format (format
, args
));
72 public void Error (int error
, string s
)
75 Report
.Error (error
, loc
, s
);
77 Report
.Error (error
, s
);
81 /// Return value indicates whether all code paths emitted return.
83 public virtual void Emit (EmitContext ec
)
90 public sealed class EmptyStatement
: Statement
{
92 private EmptyStatement () {}
94 public static readonly EmptyStatement Value
= new EmptyStatement ();
96 public override bool Resolve (EmitContext ec
)
101 protected override void DoEmit (EmitContext ec
)
106 public class If
: Statement
{
108 public Statement TrueStatement
;
109 public Statement FalseStatement
;
113 public If (Expression expr
, Statement trueStatement
, Location l
)
116 TrueStatement
= trueStatement
;
120 public If (Expression expr
,
121 Statement trueStatement
,
122 Statement falseStatement
,
126 TrueStatement
= trueStatement
;
127 FalseStatement
= falseStatement
;
131 public override bool Resolve (EmitContext ec
)
135 Report
.Debug (1, "START IF BLOCK", loc
);
137 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
143 Assign ass
= expr
as Assign
;
144 if (ass
!= null && ass
.Source
is Constant
) {
145 Report
.Warning (665, 3, loc
, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
149 // Dead code elimination
151 if (expr
is BoolConstant
){
152 bool take
= ((BoolConstant
) expr
).Value
;
155 if (!TrueStatement
.Resolve (ec
))
158 if ((FalseStatement
!= null) &&
159 !FalseStatement
.ResolveUnreachable (ec
, true))
161 FalseStatement
= null;
163 if (!TrueStatement
.ResolveUnreachable (ec
, true))
165 TrueStatement
= null;
167 if ((FalseStatement
!= null) &&
168 !FalseStatement
.Resolve (ec
))
175 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
177 ok
&= TrueStatement
.Resolve (ec
);
179 is_true_ret
= ec
.CurrentBranching
.CurrentUsageVector
.Reachability
.IsUnreachable
;
181 ec
.CurrentBranching
.CreateSibling ();
183 if (FalseStatement
!= null)
184 ok
&= FalseStatement
.Resolve (ec
);
186 ec
.EndFlowBranching ();
188 Report
.Debug (1, "END IF BLOCK", loc
);
193 protected override void DoEmit (EmitContext ec
)
195 ILGenerator ig
= ec
.ig
;
196 Label false_target
= ig
.DefineLabel ();
200 // If we're a boolean expression, Resolve() already
201 // eliminated dead code for us.
203 if (expr
is BoolConstant
){
204 bool take
= ((BoolConstant
) expr
).Value
;
207 TrueStatement
.Emit (ec
);
208 else if (FalseStatement
!= null)
209 FalseStatement
.Emit (ec
);
214 expr
.EmitBranchable (ec
, false_target
, false);
216 TrueStatement
.Emit (ec
);
218 if (FalseStatement
!= null){
219 bool branch_emitted
= false;
221 end
= ig
.DefineLabel ();
223 ig
.Emit (OpCodes
.Br
, end
);
224 branch_emitted
= true;
227 ig
.MarkLabel (false_target
);
228 FalseStatement
.Emit (ec
);
233 ig
.MarkLabel (false_target
);
238 public class Do
: Statement
{
239 public Expression expr
;
240 public readonly Statement EmbeddedStatement
;
243 public Do (Statement statement
, Expression boolExpr
, Location l
)
246 EmbeddedStatement
= statement
;
250 public override bool Resolve (EmitContext ec
)
254 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
256 if (!EmbeddedStatement
.Resolve (ec
))
259 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
262 else if (expr
is BoolConstant
){
263 bool res
= ((BoolConstant
) expr
).Value
;
269 ec
.CurrentBranching
.Infinite
= infinite
;
270 ec
.EndFlowBranching ();
275 protected override void DoEmit (EmitContext ec
)
277 ILGenerator ig
= ec
.ig
;
278 Label loop
= ig
.DefineLabel ();
279 Label old_begin
= ec
.LoopBegin
;
280 Label old_end
= ec
.LoopEnd
;
282 ec
.LoopBegin
= ig
.DefineLabel ();
283 ec
.LoopEnd
= ig
.DefineLabel ();
286 EmbeddedStatement
.Emit (ec
);
287 ig
.MarkLabel (ec
.LoopBegin
);
290 // Dead code elimination
292 if (expr
is BoolConstant
){
293 bool res
= ((BoolConstant
) expr
).Value
;
296 ec
.ig
.Emit (OpCodes
.Br
, loop
);
298 expr
.EmitBranchable (ec
, loop
, true);
300 ig
.MarkLabel (ec
.LoopEnd
);
302 ec
.LoopBegin
= old_begin
;
303 ec
.LoopEnd
= old_end
;
307 public class While
: Statement
{
308 public Expression expr
;
309 public readonly Statement Statement
;
310 bool infinite
, empty
;
312 public While (Expression boolExpr
, Statement statement
, Location l
)
314 this.expr
= boolExpr
;
315 Statement
= statement
;
319 public override bool Resolve (EmitContext ec
)
323 expr
= Expression
.ResolveBoolean (ec
, expr
, loc
);
328 // Inform whether we are infinite or not
330 if (expr
is BoolConstant
){
331 BoolConstant bc
= (BoolConstant
) expr
;
333 if (bc
.Value
== false){
334 if (!Statement
.ResolveUnreachable (ec
, true))
342 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
344 ec
.CurrentBranching
.CreateSibling ();
346 if (!Statement
.Resolve (ec
))
349 ec
.CurrentBranching
.Infinite
= infinite
;
350 ec
.EndFlowBranching ();
355 protected override void DoEmit (EmitContext ec
)
360 ILGenerator ig
= ec
.ig
;
361 Label old_begin
= ec
.LoopBegin
;
362 Label old_end
= ec
.LoopEnd
;
364 ec
.LoopBegin
= ig
.DefineLabel ();
365 ec
.LoopEnd
= ig
.DefineLabel ();
368 // Inform whether we are infinite or not
370 if (expr
is BoolConstant
){
371 ig
.MarkLabel (ec
.LoopBegin
);
373 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
376 // Inform that we are infinite (ie, `we return'), only
377 // if we do not `break' inside the code.
379 ig
.MarkLabel (ec
.LoopEnd
);
381 Label while_loop
= ig
.DefineLabel ();
383 ig
.Emit (OpCodes
.Br
, ec
.LoopBegin
);
384 ig
.MarkLabel (while_loop
);
388 ig
.MarkLabel (ec
.LoopBegin
);
390 expr
.EmitBranchable (ec
, while_loop
, true);
392 ig
.MarkLabel (ec
.LoopEnd
);
395 ec
.LoopBegin
= old_begin
;
396 ec
.LoopEnd
= old_end
;
400 public class For
: Statement
{
402 readonly Statement InitStatement
;
403 readonly Statement Increment
;
404 public readonly Statement Statement
;
405 bool infinite
, empty
;
407 public For (Statement initStatement
,
413 InitStatement
= initStatement
;
415 Increment
= increment
;
416 Statement
= statement
;
420 public override bool Resolve (EmitContext ec
)
424 if (InitStatement
!= null){
425 if (!InitStatement
.Resolve (ec
))
430 Test
= Expression
.ResolveBoolean (ec
, Test
, loc
);
433 else if (Test
is BoolConstant
){
434 BoolConstant bc
= (BoolConstant
) Test
;
436 if (bc
.Value
== false){
437 if (!Statement
.ResolveUnreachable (ec
, true))
439 if ((Increment
!= null) &&
440 !Increment
.ResolveUnreachable (ec
, false))
450 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
452 ec
.CurrentBranching
.CreateSibling ();
454 if (!Statement
.Resolve (ec
))
457 if (Increment
!= null){
458 if (!Increment
.Resolve (ec
))
462 ec
.CurrentBranching
.Infinite
= infinite
;
463 ec
.EndFlowBranching ();
468 protected override void DoEmit (EmitContext ec
)
473 ILGenerator ig
= ec
.ig
;
474 Label old_begin
= ec
.LoopBegin
;
475 Label old_end
= ec
.LoopEnd
;
476 Label loop
= ig
.DefineLabel ();
477 Label test
= ig
.DefineLabel ();
479 if (InitStatement
!= null && InitStatement
!= EmptyStatement
.Value
)
480 InitStatement
.Emit (ec
);
482 ec
.LoopBegin
= ig
.DefineLabel ();
483 ec
.LoopEnd
= ig
.DefineLabel ();
485 ig
.Emit (OpCodes
.Br
, test
);
489 ig
.MarkLabel (ec
.LoopBegin
);
490 if (Increment
!= EmptyStatement
.Value
)
495 // If test is null, there is no test, and we are just
500 // The Resolve code already catches the case for
501 // Test == BoolConstant (false) so we know that
504 if (Test
is BoolConstant
)
505 ig
.Emit (OpCodes
.Br
, loop
);
507 Test
.EmitBranchable (ec
, loop
, true);
510 ig
.Emit (OpCodes
.Br
, loop
);
511 ig
.MarkLabel (ec
.LoopEnd
);
513 ec
.LoopBegin
= old_begin
;
514 ec
.LoopEnd
= old_end
;
518 public class StatementExpression
: Statement
{
519 ExpressionStatement expr
;
521 public StatementExpression (ExpressionStatement expr
)
527 public override bool Resolve (EmitContext ec
)
530 expr
= expr
.ResolveStatement (ec
);
534 protected override void DoEmit (EmitContext ec
)
536 expr
.EmitStatement (ec
);
539 public override string ToString ()
541 return "StatementExpression (" + expr
+ ")";
546 /// Implements the return statement
548 public class Return
: Statement
{
549 public Expression Expr
;
551 public Return (Expression expr
, Location l
)
559 public override bool Resolve (EmitContext ec
)
561 AnonymousContainer am
= ec
.CurrentAnonymousMethod
;
562 if ((am
!= null) && am
.IsIterator
&& ec
.InIterator
) {
563 Report
.Error (1622, loc
, "Cannot return a value from iterators. Use the yield return " +
564 "statement to return a value, or yield break to end the iteration");
568 if (ec
.ReturnType
== null){
570 if (ec
.CurrentAnonymousMethod
!= null){
571 Report
.Error (1662, loc
,
572 "Cannot convert anonymous method block to delegate type `{0}' because some of the return types in the block are not implicitly convertible to the delegate return type",
573 ec
.CurrentAnonymousMethod
.GetSignatureForError ());
575 Error (127, "A return keyword must not be followed by any expression when method returns void");
580 Error (126, "An object of a type convertible to `{0}' is required " +
581 "for the return statement",
582 TypeManager
.CSharpName (ec
.ReturnType
));
586 Expr
= Expr
.Resolve (ec
);
590 if (Expr
.Type
!= ec
.ReturnType
) {
591 Expr
= Convert
.ImplicitConversionRequired (
592 ec
, Expr
, ec
.ReturnType
, loc
);
598 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
600 if (ec
.CurrentBranching
.InTryOrCatch (true)) {
601 ec
.CurrentBranching
.AddFinallyVector (vector
);
603 } else if (ec
.InFinally
) {
604 Error (157, "Control cannot leave the body of a finally clause");
607 vector
.CheckOutParameters (ec
.CurrentBranching
);
610 ec
.NeedReturnLabel ();
612 ec
.CurrentBranching
.CurrentUsageVector
.Return ();
616 protected override void DoEmit (EmitContext ec
)
622 ec
.ig
.Emit (OpCodes
.Stloc
, ec
.TemporaryReturn ());
626 ec
.ig
.Emit (OpCodes
.Leave
, ec
.ReturnLabel
);
628 ec
.ig
.Emit (OpCodes
.Ret
);
632 public class Goto
: Statement
{
634 LabeledStatement label
;
636 public override bool Resolve (EmitContext ec
)
638 label
= ec
.CurrentBranching
.LookupLabel (target
, loc
);
642 // If this is a forward goto.
643 if (!label
.IsDefined
)
644 label
.AddUsageVector (ec
.CurrentBranching
.CurrentUsageVector
);
646 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
647 label
.AddReference ();
652 public Goto (string label
, Location l
)
658 public string Target
{
664 protected override void DoEmit (EmitContext ec
)
666 Label l
= label
.LabelTarget (ec
);
667 ec
.ig
.Emit (OpCodes
.Br
, l
);
671 public class LabeledStatement
: Statement
{
677 FlowBranching
.UsageVector vectors
;
679 public LabeledStatement (Location l
)
684 public Label
LabelTarget (EmitContext ec
)
689 label
= ec
.ig
.DefineLabel ();
695 public bool IsDefined
{
701 public bool HasBeenReferenced
{
707 public void AddUsageVector (FlowBranching
.UsageVector vector
)
709 vector
= vector
.Clone ();
710 vector
.Next
= vectors
;
714 public override bool Resolve (EmitContext ec
)
716 ec
.CurrentBranching
.Label (vectors
);
721 protected override void DoEmit (EmitContext ec
)
723 if (ig
!= null && ig
!= ec
.ig
) {
724 // TODO: location is wrong
725 Report
.Error (1632, loc
, "Control cannot leave the body of an anonymous method");
729 ec
.ig
.MarkLabel (label
);
732 public void AddReference ()
740 /// `goto default' statement
742 public class GotoDefault
: Statement
{
744 public GotoDefault (Location l
)
749 public override bool Resolve (EmitContext ec
)
751 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
755 protected override void DoEmit (EmitContext ec
)
757 if (ec
.Switch
== null){
758 Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
762 if (!ec
.Switch
.GotDefault
){
763 Report
.Error (159, loc
, "No such label `default:' within the scope of the goto statement");
766 ec
.ig
.Emit (OpCodes
.Br
, ec
.Switch
.DefaultTarget
);
771 /// `goto case' statement
773 public class GotoCase
: Statement
{
777 public GotoCase (Expression e
, Location l
)
783 public override bool Resolve (EmitContext ec
)
785 if (ec
.Switch
== null){
786 Report
.Error (153, loc
, "A goto case is only valid inside a switch statement");
790 expr
= expr
.Resolve (ec
);
794 Constant c
= expr
as Constant
;
796 Error (150, "A constant value is expected");
800 c
= c
.ToType (ec
.Switch
.SwitchType
, loc
);
804 object val
= c
.GetValue ();
806 val
= SwitchLabel
.NullStringCase
;
808 sl
= (SwitchLabel
) ec
.Switch
.Elements
[val
];
811 Report
.Error (159, loc
, "No such label `case {0}:' within the scope of the goto statement", c
.GetValue () == null ? "null" : val
.ToString ());
815 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
819 protected override void DoEmit (EmitContext ec
)
821 ec
.ig
.Emit (OpCodes
.Br
, sl
.GetILLabelCode (ec
));
825 public class Throw
: Statement
{
828 public Throw (Expression expr
, Location l
)
834 public override bool Resolve (EmitContext ec
)
836 ec
.CurrentBranching
.CurrentUsageVector
.Throw ();
839 expr
= expr
.Resolve (ec
);
843 ExprClass eclass
= expr
.eclass
;
845 if (!(eclass
== ExprClass
.Variable
|| eclass
== ExprClass
.PropertyAccess
||
846 eclass
== ExprClass
.Value
|| eclass
== ExprClass
.IndexerAccess
)) {
847 expr
.Error_UnexpectedKind (ec
.DeclContainer
, "value, variable, property or indexer access ", loc
);
853 if ((t
!= TypeManager
.exception_type
) &&
854 !TypeManager
.IsSubclassOf (t
, TypeManager
.exception_type
) &&
855 !(expr
is NullLiteral
)) {
857 "The type caught or thrown must be derived " +
858 "from System.Exception");
865 Error (156, "A throw statement with no arguments is not allowed outside of a catch clause");
870 Error (724, "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
876 protected override void DoEmit (EmitContext ec
)
879 ec
.ig
.Emit (OpCodes
.Rethrow
);
883 ec
.ig
.Emit (OpCodes
.Throw
);
888 public class Break
: Statement
{
890 public Break (Location l
)
897 public override bool Resolve (EmitContext ec
)
899 if (!ec
.CurrentBranching
.InLoop () && !ec
.CurrentBranching
.InSwitch ()){
900 Error (139, "No enclosing loop out of which to break or continue");
902 } else if (ec
.InFinally
&& ec
.CurrentBranching
.BreakCrossesTryCatchBoundary()) {
903 Error (157, "Control cannot leave the body of a finally clause");
905 } else if (ec
.CurrentBranching
.InTryOrCatch (false))
906 ec
.CurrentBranching
.AddFinallyVector (
907 ec
.CurrentBranching
.CurrentUsageVector
);
908 else if (ec
.CurrentBranching
.InLoop () || ec
.CurrentBranching
.InSwitch ())
909 ec
.CurrentBranching
.AddBreakVector (
910 ec
.CurrentBranching
.CurrentUsageVector
);
912 crossing_exc
= ec
.CurrentBranching
.BreakCrossesTryCatchBoundary ();
915 ec
.NeedReturnLabel ();
917 ec
.CurrentBranching
.CurrentUsageVector
.Break ();
921 protected override void DoEmit (EmitContext ec
)
923 ILGenerator ig
= ec
.ig
;
926 ig
.Emit (OpCodes
.Leave
, ec
.LoopEnd
);
928 ig
.Emit (OpCodes
.Br
, ec
.LoopEnd
);
933 public class Continue
: Statement
{
935 public Continue (Location l
)
942 public override bool Resolve (EmitContext ec
)
944 if (!ec
.CurrentBranching
.InLoop ()){
945 Error (139, "No enclosing loop out of which to break or continue");
947 } else if (ec
.InFinally
) {
948 Error (157, "Control cannot leave the body of a finally clause");
950 } else if (ec
.CurrentBranching
.InTryOrCatch (false))
951 ec
.CurrentBranching
.AddFinallyVector (ec
.CurrentBranching
.CurrentUsageVector
);
953 crossing_exc
= ec
.CurrentBranching
.BreakCrossesTryCatchBoundary ();
955 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
959 protected override void DoEmit (EmitContext ec
)
961 Label begin
= ec
.LoopBegin
;
964 ec
.ig
.Emit (OpCodes
.Leave
, begin
);
966 ec
.ig
.Emit (OpCodes
.Br
, begin
);
971 // The information about a user-perceived local variable
973 public class LocalInfo
{
974 public Expression Type
;
977 // Most of the time a variable will be stored in a LocalBuilder
979 // But sometimes, it will be stored in a field (variables that have been
980 // hoisted by iterators or by anonymous methods). The context of the field will
981 // be stored in the EmitContext
984 public LocalBuilder LocalBuilder
;
985 public FieldBuilder FieldBuilder
;
987 public Type VariableType
;
988 public readonly string Name
;
989 public readonly Location Location
;
990 public readonly Block Block
;
992 public VariableInfo VariableInfo
;
1001 CompilerGenerated
= 64
1004 public enum ReadOnlyContext
: byte {
1011 ReadOnlyContext ro_context
;
1013 public LocalInfo (Expression type
, string name
, Block block
, Location l
)
1021 public LocalInfo (DeclSpace ds
, Block block
, Location l
)
1023 VariableType
= ds
.TypeBuilder
;
1028 public bool IsThisAssigned (EmitContext ec
, Location loc
)
1030 if (VariableInfo
== null)
1031 throw new Exception ();
1033 if (!ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
))
1036 return VariableInfo
.TypeInfo
.IsFullyInitialized (ec
.CurrentBranching
, VariableInfo
, loc
);
1039 public bool IsAssigned (EmitContext ec
)
1041 if (VariableInfo
== null)
1042 throw new Exception ();
1044 return !ec
.DoFlowAnalysis
|| ec
.CurrentBranching
.IsAssigned (VariableInfo
);
1047 public bool Resolve (EmitContext ec
)
1049 if (VariableType
== null) {
1050 TypeExpr texpr
= Type
.ResolveAsTypeTerminal (ec
, false);
1054 VariableType
= texpr
.Type
;
1057 if (VariableType
== TypeManager
.void_type
) {
1058 Report
.Error (1547, Location
,
1059 "Keyword 'void' cannot be used in this context");
1063 if (VariableType
.IsAbstract
&& VariableType
.IsSealed
) {
1064 Report
.Error (723, Location
, "Cannot declare variable of static type `{0}'", TypeManager
.CSharpName (VariableType
));
1068 if (VariableType
.IsPointer
&& !ec
.InUnsafe
)
1069 Expression
.UnsafeError (Location
);
1074 public bool IsCaptured
{
1076 return (flags
& Flags
.Captured
) != 0;
1080 flags
|= Flags
.Captured
;
1084 public bool AddressTaken
{
1086 return (flags
& Flags
.AddressTaken
) != 0;
1090 flags
|= Flags
.AddressTaken
;
1094 public bool CompilerGenerated
{
1096 return (flags
& Flags
.CompilerGenerated
) != 0;
1100 flags
|= Flags
.CompilerGenerated
;
1104 public override string ToString ()
1106 return String
.Format ("LocalInfo ({0},{1},{2},{3})",
1107 Name
, Type
, VariableInfo
, Location
);
1112 return (flags
& Flags
.Used
) != 0;
1115 flags
= value ? (flags
| Flags
.Used
) : (unchecked (flags
& ~Flags
.Used
));
1119 public bool ReadOnly
{
1121 return (flags
& Flags
.ReadOnly
) != 0;
1125 public void SetReadOnlyContext (ReadOnlyContext context
)
1127 flags
|= Flags
.ReadOnly
;
1128 ro_context
= context
;
1131 public string GetReadOnlyContext ()
1134 throw new InternalErrorException ("Variable is not readonly");
1136 switch (ro_context
) {
1137 case ReadOnlyContext
.Fixed
:
1138 return "fixed variable";
1139 case ReadOnlyContext
.Foreach
:
1140 return "foreach iteration variable";
1141 case ReadOnlyContext
.Using
:
1142 return "using variable";
1144 throw new NotImplementedException ();
1148 // Whether the variable is pinned, if Pinned the variable has been
1149 // allocated in a pinned slot with DeclareLocal.
1151 public bool Pinned
{
1153 return (flags
& Flags
.Pinned
) != 0;
1156 flags
= value ? (flags
| Flags
.Pinned
) : (flags
& ~Flags
.Pinned
);
1160 public bool IsThis
{
1162 return (flags
& Flags
.IsThis
) != 0;
1165 flags
= value ? (flags
| Flags
.IsThis
) : (flags
& ~Flags
.IsThis
);
1171 /// Block represents a C# block.
1175 /// This class is used in a number of places: either to represent
1176 /// explicit blocks that the programmer places or implicit blocks.
1178 /// Implicit blocks are used as labels or to introduce variable
1181 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1182 /// they contain extra information that is not necessary on normal blocks.
1184 public class Block
: Statement
{
1185 public Block Parent
;
1186 public readonly Location StartLocation
;
1187 public Location EndLocation
= Location
.Null
;
1189 public readonly ToplevelBlock Toplevel
;
1192 public enum Flags
: ushort {
1196 VariablesInitialized
= 8,
1201 HasVarargs
= 256 // Used in ToplevelBlock
1203 protected Flags flags
;
1205 public bool Implicit
{
1206 get { return (flags & Flags.Implicit) != 0; }
1209 public bool Unchecked
{
1210 get { return (flags & Flags.Unchecked) != 0; }
1211 set { flags |= Flags.Unchecked; }
1214 public bool Unsafe
{
1215 get { return (flags & Flags.Unsafe) != 0; }
1216 set { flags |= Flags.Unsafe; }
1220 // The statements in this block
1222 ArrayList statements
;
1226 // An array of Blocks. We keep track of children just
1227 // to generate the local variable declarations.
1229 // Statements and child statements are handled through the
1235 // Labels. (label, block) pairs.
1240 // Keeps track of (name, type) pairs
1242 IDictionary variables
;
1245 // Keeps track of constants
1246 Hashtable constants
;
1249 // Temporary variables.
1251 ArrayList temporary_variables
;
1254 // If this is a switch section, the enclosing switch block.
1258 protected static int id
;
1262 public Block (Block parent
)
1263 : this (parent
, (Flags
) 0, Location
.Null
, Location
.Null
)
1266 public Block (Block parent
, Flags flags
)
1267 : this (parent
, flags
, Location
.Null
, Location
.Null
)
1270 public Block (Block parent
, Location start
, Location end
)
1271 : this (parent
, (Flags
) 0, start
, end
)
1274 public Block (Block parent
, Flags flags
, Location start
, Location end
)
1277 parent
.AddChild (this);
1279 this.Parent
= parent
;
1281 this.StartLocation
= start
;
1282 this.EndLocation
= end
;
1285 statements
= new ArrayList ();
1287 if ((flags
& Flags
.IsToplevel
) != 0)
1288 Toplevel
= (ToplevelBlock
) this;
1290 Toplevel
= parent
.Toplevel
;
1292 if (parent
!= null && Implicit
) {
1293 if (parent
.known_variables
== null)
1294 parent
.known_variables
= new Hashtable ();
1295 // share with parent
1296 known_variables
= parent
.known_variables
;
1300 public Block
CreateSwitchBlock (Location start
)
1302 Block new_block
= new Block (this, start
, start
);
1303 new_block
.switch_block
= this;
1308 get { return this_id; }
1311 protected IDictionary Variables
{
1313 if (variables
== null)
1314 variables
= new ListDictionary ();
1319 void AddChild (Block b
)
1321 if (children
== null)
1322 children
= new ArrayList ();
1327 public void SetEndLocation (Location loc
)
1333 /// Adds a label to the current block.
1337 /// false if the name already exists in this block. true
1341 public bool AddLabel (string name
, LabeledStatement target
, Location loc
)
1343 if (switch_block
!= null)
1344 return switch_block
.AddLabel (name
, target
, loc
);
1347 while (cur
!= null) {
1348 if (cur
.DoLookupLabel (name
) != null) {
1350 140, loc
, "The label `{0}' is a duplicate",
1361 while (cur
!= null) {
1362 if (cur
.DoLookupLabel (name
) != null) {
1365 "The label `{0}' shadows another label " +
1366 "by the same name in a contained scope.",
1371 if (children
!= null) {
1372 foreach (Block b
in children
) {
1373 LabeledStatement s
= b
.DoLookupLabel (name
);
1379 "The label `{0}' shadows another " +
1380 "label by the same name in a " +
1392 labels
= new Hashtable ();
1394 labels
.Add (name
, target
);
1398 public LabeledStatement
LookupLabel (string name
)
1400 LabeledStatement s
= DoLookupLabel (name
);
1404 if (children
== null)
1407 foreach (Block child
in children
) {
1408 if (!child
.Implicit
)
1411 s
= child
.LookupLabel (name
);
1419 LabeledStatement
DoLookupLabel (string name
)
1421 if (switch_block
!= null)
1422 return switch_block
.LookupLabel (name
);
1425 if (labels
.Contains (name
))
1426 return ((LabeledStatement
) labels
[name
]);
1431 Hashtable known_variables
;
1434 // Marks a variable with name @name as being used in this or a child block.
1435 // If a variable name has been used in a child block, it's illegal to
1436 // declare a variable with the same name in the current block.
1438 void AddKnownVariable (string name
, LocalInfo info
)
1440 if (known_variables
== null)
1441 known_variables
= new Hashtable ();
1443 known_variables
[name
] = info
;
1446 LocalInfo
GetKnownVariableInfo (string name
)
1448 if (known_variables
== null)
1450 return (LocalInfo
) known_variables
[name
];
1453 public bool CheckInvariantMeaningInBlock (string name
, Expression e
, Location loc
)
1456 LocalInfo kvi
= b
.GetKnownVariableInfo (name
);
1457 while (kvi
== null) {
1463 kvi
= b
.GetKnownVariableInfo (name
);
1469 // Is kvi.Block nested inside 'b'
1470 if (b
.known_variables
!= kvi
.Block
.known_variables
) {
1472 // If a variable by the same name it defined in a nested block of this
1473 // block, we violate the invariant meaning in a block.
1476 Report
.SymbolRelatedToPreviousError (kvi
.Location
, name
);
1477 Report
.Error (135, loc
, "`{0}' conflicts with a declaration in a child block", name
);
1482 // It's ok if the definition is in a nested subblock of b, but not
1483 // nested inside this block -- a definition in a sibling block
1484 // should not affect us.
1490 // Block 'b' and kvi.Block are the same textual block.
1491 // However, different variables are extant.
1493 // Check if the variable is in scope in both blocks. We use
1494 // an indirect check that depends on AddVariable doing its
1495 // part in maintaining the invariant-meaning-in-block property.
1497 if (e
is LocalVariableReference
|| (e
is Constant
&& b
.GetLocalInfo (name
) != null))
1501 // Even though we detected the error when the name is used, we
1502 // treat it as if the variable declaration was in error.
1504 Report
.SymbolRelatedToPreviousError (loc
, name
);
1505 Error_AlreadyDeclared (kvi
.Location
, name
, "parent or current");
1509 public LocalInfo
AddVariable (Expression type
, string name
, Location l
)
1511 LocalInfo vi
= GetLocalInfo (name
);
1513 Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1514 if (known_variables
== vi
.Block
.known_variables
)
1515 Report
.Error (128, l
,
1516 "A local variable named `{0}' is already defined in this scope", name
);
1518 Error_AlreadyDeclared (l
, name
, "parent");
1522 vi
= GetKnownVariableInfo (name
);
1524 Report
.SymbolRelatedToPreviousError (vi
.Location
, name
);
1525 Error_AlreadyDeclared (l
, name
, "child");
1530 Parameter p
= Toplevel
.Parameters
.GetParameterByName (name
, out idx
);
1532 Report
.SymbolRelatedToPreviousError (p
.Location
, name
);
1533 Error_AlreadyDeclared (l
, name
, "method argument");
1537 vi
= new LocalInfo (type
, name
, this, l
);
1539 Variables
.Add (name
, vi
);
1541 for (Block b
= this; b
!= null; b
= b
.Parent
)
1542 b
.AddKnownVariable (name
, vi
);
1544 if ((flags
& Flags
.VariablesInitialized
) != 0)
1545 throw new Exception ();
1550 void Error_AlreadyDeclared (Location loc
, string var, string reason
)
1552 Report
.Error (136, loc
, "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning to `{0}', " +
1553 "which is already used in a `{1}' scope", var, reason
);
1556 public bool AddConstant (Expression type
, string name
, Expression
value, Location l
)
1558 if (AddVariable (type
, name
, l
) == null)
1561 if (constants
== null)
1562 constants
= new Hashtable ();
1564 constants
.Add (name
, value);
1566 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1571 static int next_temp_id
= 0;
1573 public LocalInfo
AddTemporaryVariable (TypeExpr te
, Location loc
)
1575 if (temporary_variables
== null)
1576 temporary_variables
= new ArrayList ();
1578 int id
= ++next_temp_id
;
1579 string name
= "$s_" + id
.ToString ();
1581 LocalInfo li
= new LocalInfo (te
, name
, this, loc
);
1582 li
.CompilerGenerated
= true;
1583 temporary_variables
.Add (li
);
1587 public LocalInfo
GetLocalInfo (string name
)
1589 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1590 if (b
.variables
!= null) {
1591 LocalInfo ret
= b
.variables
[name
] as LocalInfo
;
1599 public Expression
GetVariableType (string name
)
1601 LocalInfo vi
= GetLocalInfo (name
);
1602 return vi
== null ? null : vi
.Type
;
1605 public Expression
GetConstantExpression (string name
)
1607 for (Block b
= this; b
!= null; b
= b
.Parent
) {
1608 if (b
.constants
!= null) {
1609 Expression ret
= b
.constants
[name
] as Expression
;
1617 public void AddStatement (Statement s
)
1620 flags
|= Flags
.BlockUsed
;
1624 get { return (flags & Flags.BlockUsed) != 0; }
1629 flags
|= Flags
.BlockUsed
;
1632 public bool HasRet
{
1633 get { return (flags & Flags.HasRet) != 0; }
1636 public bool IsDestructor
{
1637 get { return (flags & Flags.IsDestructor) != 0; }
1640 public void SetDestructor ()
1642 flags
|= Flags
.IsDestructor
;
1645 VariableMap param_map
, local_map
;
1647 public VariableMap ParameterMap
{
1649 if ((flags
& Flags
.VariablesInitialized
) == 0)
1650 throw new Exception ("Variables have not been initialized yet");
1656 public VariableMap LocalMap
{
1658 if ((flags
& Flags
.VariablesInitialized
) == 0)
1659 throw new Exception ("Variables have not been initialized yet");
1666 /// Emits the variable declarations and labels.
1669 /// tc: is our typecontainer (to resolve type references)
1670 /// ig: is the code generator:
1672 public void ResolveMeta (ToplevelBlock toplevel
, EmitContext ec
, Parameters ip
)
1674 bool old_unsafe
= ec
.InUnsafe
;
1676 // If some parent block was unsafe, we remain unsafe even if this block
1677 // isn't explicitly marked as such.
1678 ec
.InUnsafe
|= Unsafe
;
1681 // Compute the VariableMap's.
1683 // Unfortunately, we don't know the type when adding variables with
1684 // AddVariable(), so we need to compute this info here.
1688 if (variables
!= null) {
1689 foreach (LocalInfo li
in variables
.Values
)
1692 locals
= new LocalInfo
[variables
.Count
];
1693 variables
.Values
.CopyTo (locals
, 0);
1695 locals
= new LocalInfo
[0];
1698 local_map
= new VariableMap (Parent
.LocalMap
, locals
);
1700 local_map
= new VariableMap (locals
);
1702 param_map
= new VariableMap (ip
);
1703 flags
|= Flags
.VariablesInitialized
;
1705 bool old_check_state
= ec
.ConstantCheckState
;
1706 ec
.ConstantCheckState
= (flags
& Flags
.Unchecked
) == 0;
1709 // Process this block variables
1711 if (variables
!= null){
1712 foreach (DictionaryEntry de
in variables
){
1713 string name
= (string) de
.Key
;
1714 LocalInfo vi
= (LocalInfo
) de
.Value
;
1716 if (vi
.VariableType
== null)
1719 Type variable_type
= vi
.VariableType
;
1721 if (variable_type
.IsPointer
){
1723 // Am not really convinced that this test is required (Microsoft does it)
1724 // but the fact is that you would not be able to use the pointer variable
1727 if (!TypeManager
.VerifyUnManaged (TypeManager
.GetElementType (variable_type
),
1732 if (constants
== null)
1735 Expression cv
= (Expression
) constants
[name
];
1739 // Don't let 'const int Foo = Foo;' succeed.
1740 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1741 // which in turn causes the 'must be constant' error to be triggered.
1742 constants
.Remove (name
);
1744 ec
.CurrentBlock
= this;
1745 Expression e
= cv
.Resolve (ec
);
1749 Constant ce
= e
as Constant
;
1751 Const
.Error_ExpressionMustBeConstant (vi
.Location
, name
);
1755 e
= ce
.ToType (variable_type
, vi
.Location
);
1759 if (!variable_type
.IsValueType
&& variable_type
!= TypeManager
.string_type
&& !ce
.IsDefaultValue
) {
1760 Const
.Error_ConstantCanBeInitializedWithNullOnly (vi
.Location
, vi
.Name
);
1764 constants
.Add (name
, e
);
1767 ec
.ConstantCheckState
= old_check_state
;
1770 // Now, handle the children
1772 if (children
!= null){
1773 foreach (Block b
in children
)
1774 b
.ResolveMeta (toplevel
, ec
, ip
);
1776 ec
.InUnsafe
= old_unsafe
;
1780 // Emits the local variable declarations for a block
1782 public void EmitMeta (EmitContext ec
)
1784 ILGenerator ig
= ec
.ig
;
1786 if (variables
!= null){
1787 bool have_captured_vars
= ec
.HaveCapturedVariables ();
1789 foreach (DictionaryEntry de
in variables
){
1790 LocalInfo vi
= (LocalInfo
) de
.Value
;
1792 if (have_captured_vars
&& ec
.IsCaptured (vi
))
1797 // This is needed to compile on both .NET 1.x and .NET 2.x
1798 // the later introduced `DeclareLocal (Type t, bool pinned)'
1800 vi
.LocalBuilder
= TypeManager
.DeclareLocalPinned (ig
, vi
.VariableType
);
1801 else if (!vi
.IsThis
)
1802 vi
.LocalBuilder
= ig
.DeclareLocal (vi
.VariableType
);
1806 if (temporary_variables
!= null) {
1807 AnonymousContainer am
= ec
.CurrentAnonymousMethod
;
1808 TypeBuilder scope
= null;
1809 if ((am
!= null) && am
.IsIterator
) {
1810 scope
= am
.Scope
.ScopeTypeBuilder
;
1812 throw new InternalErrorException ();
1814 foreach (LocalInfo vi
in temporary_variables
) {
1815 if (scope
!= null) {
1816 if (vi
.FieldBuilder
== null)
1817 vi
.FieldBuilder
= scope
.DefineField (
1818 vi
.Name
, vi
.VariableType
, FieldAttributes
.Assembly
);
1820 vi
.LocalBuilder
= ig
.DeclareLocal (vi
.VariableType
);
1824 if (children
!= null){
1825 foreach (Block b
in children
)
1830 void UsageWarning (FlowBranching
.UsageVector vector
)
1834 if ((variables
!= null) && (RootContext
.WarningLevel
>= 3)) {
1835 foreach (DictionaryEntry de
in variables
){
1836 LocalInfo vi
= (LocalInfo
) de
.Value
;
1841 name
= (string) de
.Key
;
1843 // vi.VariableInfo can be null for 'catch' variables
1844 if (vi
.VariableInfo
!= null && vector
.IsAssigned (vi
.VariableInfo
)){
1845 Report
.Warning (219, 3, vi
.Location
, "The variable `{0}' is assigned but its value is never used", name
);
1847 Report
.Warning (168, 3, vi
.Location
, "The variable `{0}' is declared but never used", name
);
1853 bool unreachable_shown
;
1856 private void CheckPossibleMistakenEmptyStatement (Statement s
)
1860 // Some statements are wrapped by a Block. Since
1861 // others' internal could be changed, here I treat
1862 // them as possibly wrapped by Block equally.
1863 Block b
= s
as Block
;
1864 if (b
!= null && b
.statements
.Count
== 1)
1865 s
= (Statement
) b
.statements
[0];
1868 body
= ((Lock
) s
).Statement
;
1870 body
= ((For
) s
).Statement
;
1871 else if (s
is Foreach
)
1872 body
= ((Foreach
) s
).Statement
;
1873 else if (s
is While
)
1874 body
= ((While
) s
).Statement
;
1875 else if (s
is Using
)
1876 body
= ((Using
) s
).Statement
;
1877 else if (s
is Fixed
)
1878 body
= ((Fixed
) s
).Statement
;
1882 if (body
== null || body
is EmptyStatement
)
1883 Report
.Warning (642, 3, s
.loc
, "Possible mistaken empty statement");
1886 public override bool Resolve (EmitContext ec
)
1888 Block prev_block
= ec
.CurrentBlock
;
1891 int errors
= Report
.Errors
;
1893 ec
.CurrentBlock
= this;
1894 ec
.StartFlowBranching (this);
1896 Report
.Debug (4, "RESOLVE BLOCK", StartLocation
, ec
.CurrentBranching
);
1899 // This flag is used to notate nested statements as unreachable from the beginning of this block.
1900 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
1901 // from the beginning of the function. The outer Resolve() that detected the unreachability is
1902 // responsible for handling the situation.
1904 int statement_count
= statements
.Count
;
1905 for (int ix
= 0; ix
< statement_count
; ix
++){
1906 Statement s
= (Statement
) statements
[ix
];
1907 // Check possible empty statement (CS0642)
1908 if (RootContext
.WarningLevel
>= 3 &&
1909 ix
+ 1 < statement_count
&&
1910 statements
[ix
+ 1] is Block
)
1911 CheckPossibleMistakenEmptyStatement (s
);
1914 if (s
is EmptyStatement
)
1918 ((Block
) s
).unreachable
= true;
1920 if (!unreachable_shown
) {
1921 Report
.Warning (162, 2, s
.loc
, "Unreachable code detected");
1922 unreachable_shown
= true;
1926 if (!s
.Resolve (ec
)) {
1928 statements
[ix
] = EmptyStatement
.Value
;
1932 if (unreachable
&& !(s
is LabeledStatement
) && !(s
is Block
))
1933 statements
[ix
] = EmptyStatement
.Value
;
1935 num_statements
= ix
+ 1;
1936 if (s
is LabeledStatement
)
1937 unreachable
= false;
1939 unreachable
= ec
.CurrentBranching
.CurrentUsageVector
.Reachability
.IsUnreachable
;
1942 Report
.Debug (4, "RESOLVE BLOCK DONE", StartLocation
,
1943 ec
.CurrentBranching
, statement_count
, num_statements
);
1945 FlowBranching
.UsageVector vector
= ec
.DoEndFlowBranching ();
1947 ec
.CurrentBlock
= prev_block
;
1949 // If we're a non-static `struct' constructor which doesn't have an
1950 // initializer, then we must initialize all of the struct's fields.
1951 if ((flags
& Flags
.IsToplevel
) != 0 &&
1952 !Toplevel
.IsThisAssigned (ec
) &&
1953 vector
.Reachability
.Throws
!= FlowBranching
.FlowReturns
.Always
)
1956 if ((labels
!= null) && (RootContext
.WarningLevel
>= 2)) {
1957 foreach (LabeledStatement label
in labels
.Values
)
1958 if (!label
.HasBeenReferenced
)
1959 Report
.Warning (164, 2, label
.loc
,
1960 "This label has not been referenced");
1963 Report
.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation
, vector
);
1965 if ((vector
.Reachability
.Returns
== FlowBranching
.FlowReturns
.Always
) ||
1966 (vector
.Reachability
.Throws
== FlowBranching
.FlowReturns
.Always
) ||
1967 (vector
.Reachability
.Reachable
== FlowBranching
.FlowReturns
.Never
))
1968 flags
|= Flags
.HasRet
;
1970 if (ok
&& (errors
== Report
.Errors
)) {
1971 if (RootContext
.WarningLevel
>= 3)
1972 UsageWarning (vector
);
1978 public override bool ResolveUnreachable (EmitContext ec
, bool warn
)
1980 unreachable_shown
= true;
1984 Report
.Warning (162, 2, loc
, "Unreachable code detected");
1986 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Block
, loc
);
1987 bool ok
= Resolve (ec
);
1988 ec
.KillFlowBranching ();
1993 protected override void DoEmit (EmitContext ec
)
1995 for (int ix
= 0; ix
< num_statements
; ix
++){
1996 Statement s
= (Statement
) statements
[ix
];
1998 // Check whether we are the last statement in a
2001 if (((Parent
== null) || Implicit
) && (ix
+1 == num_statements
) && !(s
is Block
))
2002 ec
.IsLastStatement
= true;
2004 ec
.IsLastStatement
= false;
2010 public override void Emit (EmitContext ec
)
2012 Block prev_block
= ec
.CurrentBlock
;
2014 ec
.CurrentBlock
= this;
2016 bool emit_debug_info
= (CodeGen
.SymbolWriter
!= null);
2017 bool is_lexical_block
= !Implicit
&& (Parent
!= null);
2019 if (emit_debug_info
) {
2020 if (is_lexical_block
)
2023 if (variables
!= null) {
2024 foreach (DictionaryEntry de
in variables
) {
2025 string name
= (string) de
.Key
;
2026 LocalInfo vi
= (LocalInfo
) de
.Value
;
2028 if (vi
.LocalBuilder
== null)
2031 ec
.DefineLocalVariable (name
, vi
.LocalBuilder
);
2035 ec
.Mark (StartLocation
, true);
2036 ec
.EmitScopeInitFromBlock (this);
2038 ec
.Mark (EndLocation
, true);
2040 if (emit_debug_info
&& is_lexical_block
)
2043 ec
.CurrentBlock
= prev_block
;
2047 // Returns true if we ar ea child of `b'.
2049 public bool IsChildOf (Block b
)
2051 Block current
= this;
2054 if (current
.Parent
== b
)
2056 current
= current
.Parent
;
2057 } while (current
!= null);
2061 public override string ToString ()
2063 return String
.Format ("{0} ({1}:{2})", GetType (),ID
, StartLocation
);
2068 // A toplevel block contains extra information, the split is done
2069 // only to separate information that would otherwise bloat the more
2070 // lightweight Block.
2072 // In particular, this was introduced when the support for Anonymous
2073 // Methods was implemented.
2075 public class ToplevelBlock
: Block
{
2077 // Pointer to the host of this anonymous method, or null
2078 // if we are the topmost block
2080 ToplevelBlock container
;
2081 CaptureContext capture_context
;
2082 FlowBranching top_level_branching
;
2084 Hashtable capture_contexts
;
2087 public bool HasVarargs
{
2088 get { return (flags & Flags.HasVarargs) != 0; }
2089 set { flags |= Flags.HasVarargs; }
2093 // The parameters for the block.
2095 Parameters parameters
;
2096 public Parameters Parameters
{
2097 get { return parameters; }
2100 public void RegisterCaptureContext (CaptureContext cc
)
2102 if (capture_contexts
== null)
2103 capture_contexts
= new Hashtable ();
2104 capture_contexts
[cc
] = cc
;
2107 public void CompleteContexts ()
2109 if (capture_contexts
== null)
2112 foreach (CaptureContext cc
in capture_contexts
.Keys
){
2117 public CaptureContext ToplevelBlockCaptureContext
{
2118 get { return capture_context; }
2121 public ToplevelBlock Container
{
2122 get { return container; }
2125 protected void AddChild (ToplevelBlock block
)
2127 if (children
== null)
2128 children
= new ArrayList ();
2130 children
.Add (block
);
2134 // Parent is only used by anonymous blocks to link back to their
2137 public ToplevelBlock (ToplevelBlock container
, Parameters parameters
, Location start
) :
2138 this (container
, (Flags
) 0, parameters
, start
)
2142 public ToplevelBlock (Parameters parameters
, Location start
) :
2143 this (null, (Flags
) 0, parameters
, start
)
2147 public ToplevelBlock (Flags flags
, Parameters parameters
, Location start
) :
2148 this (null, flags
, parameters
, start
)
2152 public ToplevelBlock (ToplevelBlock container
, Flags flags
, Parameters parameters
, Location start
) :
2153 base (null, flags
| Flags
.IsToplevel
, start
, Location
.Null
)
2155 this.parameters
= parameters
== null ? Parameters
.EmptyReadOnlyParameters
: parameters
;
2156 this.container
= container
;
2158 if (container
!= null)
2159 container
.AddChild (this);
2162 public ToplevelBlock (Location loc
) : this (null, (Flags
) 0, null, loc
)
2166 public void SetHaveAnonymousMethods (Location loc
, AnonymousContainer host
)
2168 if (capture_context
== null)
2169 capture_context
= new CaptureContext (this, loc
, host
);
2172 public CaptureContext CaptureContext
{
2173 get { return capture_context; }
2176 public FlowBranching TopLevelBranching
{
2177 get { return top_level_branching; }
2181 // This is used if anonymous methods are used inside an iterator
2182 // (see 2test-22.cs for an example).
2184 // The AnonymousMethod is created while parsing - at a time when we don't
2185 // know yet that we're inside an iterator, so it's `Container' is initially
2186 // null. Later on, when resolving the iterator, we need to move the
2187 // anonymous method into that iterator.
2189 public void ReParent (ToplevelBlock new_parent
, AnonymousContainer new_host
)
2191 foreach (ToplevelBlock block
in children
) {
2192 if (block
.CaptureContext
== null)
2195 block
.container
= new_parent
;
2196 block
.CaptureContext
.ReParent (new_parent
, new_host
);
2201 // Returns a `ParameterReference' for the given name, or null if there
2202 // is no such parameter
2204 public ParameterReference
GetParameterReference (string name
, Location loc
)
2209 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2210 Parameters pars
= t
.Parameters
;
2211 par
= pars
.GetParameterByName (name
, out idx
);
2213 return new ParameterReference (par
, this, idx
, loc
);
2219 // Whether the parameter named `name' is local to this block,
2220 // or false, if the parameter belongs to an encompassing block.
2222 public bool IsLocalParameter (string name
)
2224 return Parameters
.GetParameterByName (name
) != null;
2228 // Whether the `name' is a parameter reference
2230 public bool IsParameterReference (string name
)
2232 for (ToplevelBlock t
= this; t
!= null; t
= t
.Container
) {
2233 if (t
.IsLocalParameter (name
))
2239 LocalInfo this_variable
= null;
2242 // Returns the "this" instance variable of this block.
2243 // See AddThisVariable() for more information.
2245 public LocalInfo ThisVariable
{
2246 get { return this_variable; }
2251 // This is used by non-static `struct' constructors which do not have an
2252 // initializer - in this case, the constructor must initialize all of the
2253 // struct's fields. To do this, we add a "this" variable and use the flow
2254 // analysis code to ensure that it's been fully initialized before control
2255 // leaves the constructor.
2257 public LocalInfo
AddThisVariable (DeclSpace ds
, Location l
)
2259 if (this_variable
== null) {
2260 this_variable
= new LocalInfo (ds
, this, l
);
2261 this_variable
.Used
= true;
2262 this_variable
.IsThis
= true;
2264 Variables
.Add ("this", this_variable
);
2267 return this_variable
;
2270 public bool IsThisAssigned (EmitContext ec
)
2272 return this_variable
== null || this_variable
.IsThisAssigned (ec
, loc
);
2275 public bool ResolveMeta (EmitContext ec
, Parameters ip
)
2277 int errors
= Report
.Errors
;
2279 if (top_level_branching
!= null)
2285 ResolveMeta (this, ec
, ip
);
2287 top_level_branching
= ec
.StartFlowBranching (this);
2289 return Report
.Errors
== errors
;
2293 public class SwitchLabel
{
2300 Label il_label_code
;
2301 bool il_label_code_set
;
2303 public static readonly object NullStringCase
= new object ();
2306 // if expr == null, then it is the default case.
2308 public SwitchLabel (Expression expr
, Location l
)
2314 public Expression Label
{
2320 public object Converted
{
2326 public Label
GetILLabel (EmitContext ec
)
2329 il_label
= ec
.ig
.DefineLabel ();
2330 il_label_set
= true;
2335 public Label
GetILLabelCode (EmitContext ec
)
2337 if (!il_label_code_set
){
2338 il_label_code
= ec
.ig
.DefineLabel ();
2339 il_label_code_set
= true;
2341 return il_label_code
;
2345 // Resolves the expression, reduces it to a literal if possible
2346 // and then converts it to the requested type.
2348 public bool ResolveAndReduce (EmitContext ec
, Type required_type
)
2350 Expression e
= label
.Resolve (ec
);
2355 Constant c
= e
as Constant
;
2357 Report
.Error (150, loc
, "A constant value is expected");
2361 if (required_type
== TypeManager
.string_type
&& c
.GetValue () == null) {
2362 converted
= NullStringCase
;
2366 c
= c
.ToType (required_type
, loc
);
2370 converted
= c
.GetValue ();
2374 public void Erorr_AlreadyOccurs ()
2377 if (converted
== null)
2379 else if (converted
== NullStringCase
)
2382 label
= converted
.ToString ();
2384 Report
.Error (152, loc
, "The label `case {0}:' already occurs in this switch statement", label
);
2388 public class SwitchSection
{
2389 // An array of SwitchLabels.
2390 public readonly ArrayList Labels
;
2391 public readonly Block Block
;
2393 public SwitchSection (ArrayList labels
, Block block
)
2400 public class Switch
: Statement
{
2401 public readonly ArrayList Sections
;
2402 public Expression Expr
;
2405 /// Maps constants whose type type SwitchType to their SwitchLabels.
2407 public IDictionary Elements
;
2410 /// The governing switch type
2412 public Type SwitchType
;
2417 Label default_target
;
2418 Expression new_expr
;
2420 SwitchSection constant_section
;
2421 SwitchSection default_section
;
2424 // The types allowed to be implicitly cast from
2425 // on the governing type
2427 static Type
[] allowed_types
;
2429 public Switch (Expression e
, ArrayList sects
, Location l
)
2436 public bool GotDefault
{
2438 return default_section
!= null;
2442 public Label DefaultTarget
{
2444 return default_target
;
2449 // Determines the governing type for a switch. The returned
2450 // expression might be the expression from the switch, or an
2451 // expression that includes any potential conversions to the
2452 // integral types or to string.
2454 Expression
SwitchGoverningType (EmitContext ec
, Type t
)
2456 if (t
== TypeManager
.byte_type
||
2457 t
== TypeManager
.sbyte_type
||
2458 t
== TypeManager
.ushort_type
||
2459 t
== TypeManager
.short_type
||
2460 t
== TypeManager
.uint32_type
||
2461 t
== TypeManager
.int32_type
||
2462 t
== TypeManager
.uint64_type
||
2463 t
== TypeManager
.int64_type
||
2464 t
== TypeManager
.char_type
||
2465 t
== TypeManager
.string_type
||
2466 t
== TypeManager
.bool_type
||
2467 t
.IsSubclassOf (TypeManager
.enum_type
))
2470 if (allowed_types
== null){
2471 allowed_types
= new Type
[] {
2472 TypeManager
.sbyte_type
,
2473 TypeManager
.byte_type
,
2474 TypeManager
.short_type
,
2475 TypeManager
.ushort_type
,
2476 TypeManager
.int32_type
,
2477 TypeManager
.uint32_type
,
2478 TypeManager
.int64_type
,
2479 TypeManager
.uint64_type
,
2480 TypeManager
.char_type
,
2481 TypeManager
.string_type
,
2482 TypeManager
.bool_type
2487 // Try to find a *user* defined implicit conversion.
2489 // If there is no implicit conversion, or if there are multiple
2490 // conversions, we have to report an error
2492 Expression converted
= null;
2493 foreach (Type tt
in allowed_types
){
2496 e
= Convert
.ImplicitUserConversion (ec
, Expr
, tt
, loc
);
2501 // Ignore over-worked ImplicitUserConversions that do
2502 // an implicit conversion in addition to the user conversion.
2504 if (!(e
is UserCast
))
2507 if (converted
!= null){
2508 Report
.ExtraInformation (
2510 String
.Format ("reason: more than one conversion to an integral type exist for type {0}",
2511 TypeManager
.CSharpName (Expr
.Type
)));
2521 // Performs the basic sanity checks on the switch statement
2522 // (looks for duplicate keys and non-constant expressions).
2524 // It also returns a hashtable with the keys that we will later
2525 // use to compute the switch tables
2527 bool CheckSwitch (EmitContext ec
)
2530 Elements
= Sections
.Count
> 10 ?
2531 (IDictionary
)new Hashtable () :
2532 (IDictionary
)new ListDictionary ();
2534 foreach (SwitchSection ss
in Sections
){
2535 foreach (SwitchLabel sl
in ss
.Labels
){
2536 if (sl
.Label
== null){
2537 if (default_section
!= null){
2538 sl
.Erorr_AlreadyOccurs ();
2541 default_section
= ss
;
2545 if (!sl
.ResolveAndReduce (ec
, SwitchType
)){
2550 object key
= sl
.Converted
;
2552 Elements
.Add (key
, sl
);
2554 catch (ArgumentException
) {
2555 sl
.Erorr_AlreadyOccurs ();
2563 void EmitObjectInteger (ILGenerator ig
, object k
)
2566 IntConstant
.EmitInt (ig
, (int) k
);
2567 else if (k
is Constant
) {
2568 EmitObjectInteger (ig
, ((Constant
) k
).GetValue ());
2571 IntConstant
.EmitInt (ig
, unchecked ((int) (uint) k
));
2574 if ((long) k
>= int.MinValue
&& (long) k
<= int.MaxValue
)
2576 IntConstant
.EmitInt (ig
, (int) (long) k
);
2577 ig
.Emit (OpCodes
.Conv_I8
);
2580 LongConstant
.EmitLong (ig
, (long) k
);
2582 else if (k
is ulong)
2584 ulong ul
= (ulong) k
;
2587 IntConstant
.EmitInt (ig
, unchecked ((int) ul
));
2588 ig
.Emit (OpCodes
.Conv_U8
);
2592 LongConstant
.EmitLong (ig
, unchecked ((long) ul
));
2596 IntConstant
.EmitInt (ig
, (int) ((char) k
));
2597 else if (k
is sbyte)
2598 IntConstant
.EmitInt (ig
, (int) ((sbyte) k
));
2600 IntConstant
.EmitInt (ig
, (int) ((byte) k
));
2601 else if (k
is short)
2602 IntConstant
.EmitInt (ig
, (int) ((short) k
));
2603 else if (k
is ushort)
2604 IntConstant
.EmitInt (ig
, (int) ((ushort) k
));
2606 IntConstant
.EmitInt (ig
, ((bool) k
) ? 1 : 0);
2608 throw new Exception ("Unhandled case");
2611 // structure used to hold blocks of keys while calculating table switch
2612 class KeyBlock
: IComparable
2614 public KeyBlock (long _nFirst
)
2616 nFirst
= nLast
= _nFirst
;
2620 public ArrayList rgKeys
= null;
2621 // how many items are in the bucket
2622 public int Size
= 1;
2625 get { return (int) (nLast - nFirst + 1); }
2627 public static long TotalLength (KeyBlock kbFirst
, KeyBlock kbLast
)
2629 return kbLast
.nLast
- kbFirst
.nFirst
+ 1;
2631 public int CompareTo (object obj
)
2633 KeyBlock kb
= (KeyBlock
) obj
;
2634 int nLength
= Length
;
2635 int nLengthOther
= kb
.Length
;
2636 if (nLengthOther
== nLength
)
2637 return (int) (kb
.nFirst
- nFirst
);
2638 return nLength
- nLengthOther
;
2643 /// This method emits code for a lookup-based switch statement (non-string)
2644 /// Basically it groups the cases into blocks that are at least half full,
2645 /// and then spits out individual lookup opcodes for each block.
2646 /// It emits the longest blocks first, and short blocks are just
2647 /// handled with direct compares.
2649 /// <param name="ec"></param>
2650 /// <param name="val"></param>
2651 /// <returns></returns>
2652 void TableSwitchEmit (EmitContext ec
, LocalBuilder val
)
2654 int cElements
= Elements
.Count
;
2655 object [] rgKeys
= new object [cElements
];
2656 Elements
.Keys
.CopyTo (rgKeys
, 0);
2657 Array
.Sort (rgKeys
);
2659 // initialize the block list with one element per key
2660 ArrayList rgKeyBlocks
= new ArrayList ();
2661 foreach (object key
in rgKeys
)
2662 rgKeyBlocks
.Add (new KeyBlock (System
.Convert
.ToInt64 (key
)));
2665 // iteratively merge the blocks while they are at least half full
2666 // there's probably a really cool way to do this with a tree...
2667 while (rgKeyBlocks
.Count
> 1)
2669 ArrayList rgKeyBlocksNew
= new ArrayList ();
2670 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
2671 for (int ikb
= 1; ikb
< rgKeyBlocks
.Count
; ikb
++)
2673 KeyBlock kb
= (KeyBlock
) rgKeyBlocks
[ikb
];
2674 if ((kbCurr
.Size
+ kb
.Size
) * 2 >= KeyBlock
.TotalLength (kbCurr
, kb
))
2677 kbCurr
.nLast
= kb
.nLast
;
2678 kbCurr
.Size
+= kb
.Size
;
2682 // start a new block
2683 rgKeyBlocksNew
.Add (kbCurr
);
2687 rgKeyBlocksNew
.Add (kbCurr
);
2688 if (rgKeyBlocks
.Count
== rgKeyBlocksNew
.Count
)
2690 rgKeyBlocks
= rgKeyBlocksNew
;
2693 // initialize the key lists
2694 foreach (KeyBlock kb
in rgKeyBlocks
)
2695 kb
.rgKeys
= new ArrayList ();
2697 // fill the key lists
2699 if (rgKeyBlocks
.Count
> 0) {
2700 kbCurr
= (KeyBlock
) rgKeyBlocks
[0];
2701 foreach (object key
in rgKeys
)
2703 bool fNextBlock
= (key
is UInt64
) ? (ulong) key
> (ulong) kbCurr
.nLast
:
2704 System
.Convert
.ToInt64 (key
) > kbCurr
.nLast
;
2706 kbCurr
= (KeyBlock
) rgKeyBlocks
[++iBlockCurr
];
2707 kbCurr
.rgKeys
.Add (key
);
2711 // sort the blocks so we can tackle the largest ones first
2712 rgKeyBlocks
.Sort ();
2714 // okay now we can start...
2715 ILGenerator ig
= ec
.ig
;
2716 Label lblEnd
= ig
.DefineLabel (); // at the end ;-)
2717 Label lblDefault
= ig
.DefineLabel ();
2719 Type typeKeys
= null;
2720 if (rgKeys
.Length
> 0)
2721 typeKeys
= rgKeys
[0].GetType (); // used for conversions
2725 if (TypeManager
.IsEnumType (SwitchType
))
2726 compare_type
= TypeManager
.EnumToUnderlying (SwitchType
);
2728 compare_type
= SwitchType
;
2730 for (int iBlock
= rgKeyBlocks
.Count
- 1; iBlock
>= 0; --iBlock
)
2732 KeyBlock kb
= ((KeyBlock
) rgKeyBlocks
[iBlock
]);
2733 lblDefault
= (iBlock
== 0) ? DefaultTarget
: ig
.DefineLabel ();
2736 foreach (object key
in kb
.rgKeys
)
2738 ig
.Emit (OpCodes
.Ldloc
, val
);
2739 EmitObjectInteger (ig
, key
);
2740 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
2741 ig
.Emit (OpCodes
.Beq
, sl
.GetILLabel (ec
));
2746 // TODO: if all the keys in the block are the same and there are
2747 // no gaps/defaults then just use a range-check.
2748 if (compare_type
== TypeManager
.int64_type
||
2749 compare_type
== TypeManager
.uint64_type
)
2751 // TODO: optimize constant/I4 cases
2753 // check block range (could be > 2^31)
2754 ig
.Emit (OpCodes
.Ldloc
, val
);
2755 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nFirst
, typeKeys
));
2756 ig
.Emit (OpCodes
.Blt
, lblDefault
);
2757 ig
.Emit (OpCodes
.Ldloc
, val
);
2758 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nLast
, typeKeys
));
2759 ig
.Emit (OpCodes
.Bgt
, lblDefault
);
2762 ig
.Emit (OpCodes
.Ldloc
, val
);
2765 EmitObjectInteger (ig
, System
.Convert
.ChangeType (kb
.nFirst
, typeKeys
));
2766 ig
.Emit (OpCodes
.Sub
);
2768 ig
.Emit (OpCodes
.Conv_I4
); // assumes < 2^31 labels!
2773 ig
.Emit (OpCodes
.Ldloc
, val
);
2774 int nFirst
= (int) kb
.nFirst
;
2777 IntConstant
.EmitInt (ig
, nFirst
);
2778 ig
.Emit (OpCodes
.Sub
);
2780 else if (nFirst
< 0)
2782 IntConstant
.EmitInt (ig
, -nFirst
);
2783 ig
.Emit (OpCodes
.Add
);
2787 // first, build the list of labels for the switch
2789 int cJumps
= kb
.Length
;
2790 Label
[] rgLabels
= new Label
[cJumps
];
2791 for (int iJump
= 0; iJump
< cJumps
; iJump
++)
2793 object key
= kb
.rgKeys
[iKey
];
2794 if (System
.Convert
.ToInt64 (key
) == kb
.nFirst
+ iJump
)
2796 SwitchLabel sl
= (SwitchLabel
) Elements
[key
];
2797 rgLabels
[iJump
] = sl
.GetILLabel (ec
);
2801 rgLabels
[iJump
] = lblDefault
;
2803 // emit the switch opcode
2804 ig
.Emit (OpCodes
.Switch
, rgLabels
);
2807 // mark the default for this block
2809 ig
.MarkLabel (lblDefault
);
2812 // TODO: find the default case and emit it here,
2813 // to prevent having to do the following jump.
2814 // make sure to mark other labels in the default section
2816 // the last default just goes to the end
2817 ig
.Emit (OpCodes
.Br
, lblDefault
);
2819 // now emit the code for the sections
2820 bool fFoundDefault
= false;
2821 foreach (SwitchSection ss
in Sections
)
2823 foreach (SwitchLabel sl
in ss
.Labels
)
2825 ig
.MarkLabel (sl
.GetILLabel (ec
));
2826 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
2827 if (sl
.Label
== null)
2829 ig
.MarkLabel (lblDefault
);
2830 fFoundDefault
= true;
2834 //ig.Emit (OpCodes.Br, lblEnd);
2837 if (!fFoundDefault
) {
2838 ig
.MarkLabel (lblDefault
);
2840 ig
.MarkLabel (lblEnd
);
2843 // This simple emit switch works, but does not take advantage of the
2845 // TODO: remove non-string logic from here
2846 // TODO: binary search strings?
2848 void SimpleSwitchEmit (EmitContext ec
, LocalBuilder val
)
2850 ILGenerator ig
= ec
.ig
;
2851 Label end_of_switch
= ig
.DefineLabel ();
2852 Label next_test
= ig
.DefineLabel ();
2853 Label null_target
= ig
.DefineLabel ();
2854 bool first_test
= true;
2855 bool pending_goto_end
= false;
2856 bool null_marked
= false;
2859 ig
.Emit (OpCodes
.Ldloc
, val
);
2861 if (Elements
.Contains (SwitchLabel
.NullStringCase
)){
2862 ig
.Emit (OpCodes
.Brfalse
, null_target
);
2864 ig
.Emit (OpCodes
.Brfalse
, default_target
);
2866 ig
.Emit (OpCodes
.Ldloc
, val
);
2867 ig
.Emit (OpCodes
.Call
, TypeManager
.string_isinterneted_string
);
2868 ig
.Emit (OpCodes
.Stloc
, val
);
2870 int section_count
= Sections
.Count
;
2871 for (int section
= 0; section
< section_count
; section
++){
2872 SwitchSection ss
= (SwitchSection
) Sections
[section
];
2874 if (ss
== default_section
)
2877 Label sec_begin
= ig
.DefineLabel ();
2879 ig
.Emit (OpCodes
.Nop
);
2881 if (pending_goto_end
)
2882 ig
.Emit (OpCodes
.Br
, end_of_switch
);
2884 int label_count
= ss
.Labels
.Count
;
2886 for (int label
= 0; label
< label_count
; label
++){
2887 SwitchLabel sl
= (SwitchLabel
) ss
.Labels
[label
];
2888 ig
.MarkLabel (sl
.GetILLabel (ec
));
2891 ig
.MarkLabel (next_test
);
2892 next_test
= ig
.DefineLabel ();
2895 // If we are the default target
2897 if (sl
.Label
!= null){
2898 object lit
= sl
.Converted
;
2900 if (lit
== SwitchLabel
.NullStringCase
){
2902 if (label_count
== 1)
2903 ig
.Emit (OpCodes
.Br
, next_test
);
2907 ig
.Emit (OpCodes
.Ldloc
, val
);
2908 ig
.Emit (OpCodes
.Ldstr
, (string)lit
);
2909 if (label_count
== 1)
2910 ig
.Emit (OpCodes
.Bne_Un
, next_test
);
2912 if (label
+1 == label_count
)
2913 ig
.Emit (OpCodes
.Bne_Un
, next_test
);
2915 ig
.Emit (OpCodes
.Beq
, sec_begin
);
2920 ig
.MarkLabel (null_target
);
2923 ig
.MarkLabel (sec_begin
);
2924 foreach (SwitchLabel sl
in ss
.Labels
)
2925 ig
.MarkLabel (sl
.GetILLabelCode (ec
));
2928 pending_goto_end
= !ss
.Block
.HasRet
;
2931 ig
.MarkLabel (next_test
);
2932 ig
.MarkLabel (default_target
);
2934 ig
.MarkLabel (null_target
);
2935 if (default_section
!= null)
2936 default_section
.Block
.Emit (ec
);
2937 ig
.MarkLabel (end_of_switch
);
2940 SwitchSection
FindSection (SwitchLabel label
)
2942 foreach (SwitchSection ss
in Sections
){
2943 foreach (SwitchLabel sl
in ss
.Labels
){
2952 public override bool Resolve (EmitContext ec
)
2954 Expr
= Expr
.Resolve (ec
);
2958 new_expr
= SwitchGoverningType (ec
, Expr
.Type
);
2959 if (new_expr
== null){
2960 Report
.Error (151, loc
, "A value of an integral type or string expected for switch");
2965 SwitchType
= new_expr
.Type
;
2967 if (!CheckSwitch (ec
))
2970 Switch old_switch
= ec
.Switch
;
2972 ec
.Switch
.SwitchType
= SwitchType
;
2974 Report
.Debug (1, "START OF SWITCH BLOCK", loc
, ec
.CurrentBranching
);
2975 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Switch
, loc
);
2977 is_constant
= new_expr
is Constant
;
2979 object key
= ((Constant
) new_expr
).GetValue ();
2980 SwitchLabel label
= (SwitchLabel
) Elements
[key
];
2982 constant_section
= FindSection (label
);
2983 if (constant_section
== null)
2984 constant_section
= default_section
;
2988 foreach (SwitchSection ss
in Sections
){
2990 ec
.CurrentBranching
.CreateSibling (
2991 null, FlowBranching
.SiblingType
.SwitchSection
);
2995 if (is_constant
&& (ss
!= constant_section
)) {
2996 // If we're a constant switch, we're only emitting
2997 // one single section - mark all the others as
2999 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
3000 if (!ss
.Block
.ResolveUnreachable (ec
, true))
3003 if (!ss
.Block
.Resolve (ec
))
3008 if (default_section
== null)
3009 ec
.CurrentBranching
.CreateSibling (
3010 null, FlowBranching
.SiblingType
.SwitchSection
);
3012 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3013 ec
.Switch
= old_switch
;
3015 Report
.Debug (1, "END OF SWITCH BLOCK", loc
, ec
.CurrentBranching
,
3021 protected override void DoEmit (EmitContext ec
)
3023 ILGenerator ig
= ec
.ig
;
3025 // Store variable for comparission purposes
3028 value = ig
.DeclareLocal (SwitchType
);
3030 ig
.Emit (OpCodes
.Stloc
, value);
3034 default_target
= ig
.DefineLabel ();
3037 // Setup the codegen context
3039 Label old_end
= ec
.LoopEnd
;
3040 Switch old_switch
= ec
.Switch
;
3042 ec
.LoopEnd
= ig
.DefineLabel ();
3047 if (constant_section
!= null)
3048 constant_section
.Block
.Emit (ec
);
3049 } else if (SwitchType
== TypeManager
.string_type
)
3050 SimpleSwitchEmit (ec
, value);
3052 TableSwitchEmit (ec
, value);
3054 // Restore context state.
3055 ig
.MarkLabel (ec
.LoopEnd
);
3058 // Restore the previous context
3060 ec
.LoopEnd
= old_end
;
3061 ec
.Switch
= old_switch
;
3065 public abstract class ExceptionStatement
: Statement
3067 public abstract void EmitFinally (EmitContext ec
);
3069 protected bool emit_finally
= true;
3070 ArrayList parent_vectors
;
3072 protected void DoEmitFinally (EmitContext ec
)
3075 ec
.ig
.BeginFinallyBlock ();
3076 else if (ec
.InIterator
)
3077 ec
.CurrentIterator
.MarkFinally (ec
, parent_vectors
);
3081 protected void ResolveFinally (FlowBranchingException branching
)
3083 emit_finally
= branching
.EmitFinally
;
3085 branching
.Parent
.StealFinallyClauses (ref parent_vectors
);
3089 public class Lock
: ExceptionStatement
{
3091 public Statement Statement
;
3092 TemporaryVariable temp
;
3094 public Lock (Expression expr
, Statement stmt
, Location l
)
3101 public override bool Resolve (EmitContext ec
)
3103 expr
= expr
.Resolve (ec
);
3107 if (expr
.Type
.IsValueType
){
3108 Report
.Error (185, loc
,
3109 "`{0}' is not a reference type as required by the lock statement",
3110 TypeManager
.CSharpName (expr
.Type
));
3114 FlowBranchingException branching
= ec
.StartFlowBranching (this);
3115 bool ok
= Statement
.Resolve (ec
);
3117 ec
.KillFlowBranching ();
3121 ResolveFinally (branching
);
3123 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3124 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
3125 // Unfortunately, System.Reflection.Emit automatically emits
3126 // a leave to the end of the finally block.
3127 // This is a problem if `returns' is true since we may jump
3128 // to a point after the end of the method.
3129 // As a workaround, emit an explicit ret here.
3130 ec
.NeedReturnLabel ();
3133 temp
= new TemporaryVariable (expr
.Type
, loc
);
3139 protected override void DoEmit (EmitContext ec
)
3141 ILGenerator ig
= ec
.ig
;
3143 temp
.Store (ec
, expr
);
3145 ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_enter_object
);
3149 ig
.BeginExceptionBlock ();
3150 Statement
.Emit (ec
);
3155 ig
.EndExceptionBlock ();
3158 public override void EmitFinally (EmitContext ec
)
3161 ec
.ig
.Emit (OpCodes
.Call
, TypeManager
.void_monitor_exit_object
);
3165 public class Unchecked
: Statement
{
3166 public readonly Block Block
;
3168 public Unchecked (Block b
)
3174 public override bool Resolve (EmitContext ec
)
3176 bool previous_state
= ec
.CheckState
;
3177 bool previous_state_const
= ec
.ConstantCheckState
;
3179 ec
.CheckState
= false;
3180 ec
.ConstantCheckState
= false;
3181 bool ret
= Block
.Resolve (ec
);
3182 ec
.CheckState
= previous_state
;
3183 ec
.ConstantCheckState
= previous_state_const
;
3188 protected override void DoEmit (EmitContext ec
)
3190 bool previous_state
= ec
.CheckState
;
3191 bool previous_state_const
= ec
.ConstantCheckState
;
3193 ec
.CheckState
= false;
3194 ec
.ConstantCheckState
= false;
3196 ec
.CheckState
= previous_state
;
3197 ec
.ConstantCheckState
= previous_state_const
;
3201 public class Checked
: Statement
{
3202 public readonly Block Block
;
3204 public Checked (Block b
)
3207 b
.Unchecked
= false;
3210 public override bool Resolve (EmitContext ec
)
3212 bool previous_state
= ec
.CheckState
;
3213 bool previous_state_const
= ec
.ConstantCheckState
;
3215 ec
.CheckState
= true;
3216 ec
.ConstantCheckState
= true;
3217 bool ret
= Block
.Resolve (ec
);
3218 ec
.CheckState
= previous_state
;
3219 ec
.ConstantCheckState
= previous_state_const
;
3224 protected override void DoEmit (EmitContext ec
)
3226 bool previous_state
= ec
.CheckState
;
3227 bool previous_state_const
= ec
.ConstantCheckState
;
3229 ec
.CheckState
= true;
3230 ec
.ConstantCheckState
= true;
3232 ec
.CheckState
= previous_state
;
3233 ec
.ConstantCheckState
= previous_state_const
;
3237 public class Unsafe
: Statement
{
3238 public readonly Block Block
;
3240 public Unsafe (Block b
)
3243 Block
.Unsafe
= true;
3246 public override bool Resolve (EmitContext ec
)
3248 bool previous_state
= ec
.InUnsafe
;
3252 val
= Block
.Resolve (ec
);
3253 ec
.InUnsafe
= previous_state
;
3258 protected override void DoEmit (EmitContext ec
)
3260 bool previous_state
= ec
.InUnsafe
;
3264 ec
.InUnsafe
= previous_state
;
3271 public class Fixed
: Statement
{
3273 ArrayList declarators
;
3274 Statement statement
;
3279 abstract class Emitter
3281 protected LocalInfo vi
;
3282 protected Expression converted
;
3284 protected Emitter (Expression expr
, LocalInfo li
)
3290 public abstract void Emit (EmitContext ec
);
3291 public abstract void EmitExit (ILGenerator ig
);
3294 class ExpressionEmitter
: Emitter
{
3295 public ExpressionEmitter (Expression converted
, LocalInfo li
) :
3296 base (converted
, li
)
3300 public override void Emit (EmitContext ec
) {
3302 // Store pointer in pinned location
3304 converted
.Emit (ec
);
3305 ec
.ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3308 public override void EmitExit (ILGenerator ig
)
3310 ig
.Emit (OpCodes
.Ldc_I4_0
);
3311 ig
.Emit (OpCodes
.Conv_U
);
3312 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3316 class StringEmitter
: Emitter
{
3317 LocalBuilder pinned_string
;
3320 public StringEmitter (Expression expr
, LocalInfo li
, Location loc
):
3326 public override void Emit (EmitContext ec
)
3328 ILGenerator ig
= ec
.ig
;
3329 pinned_string
= TypeManager
.DeclareLocalPinned (ig
, TypeManager
.string_type
);
3331 converted
.Emit (ec
);
3332 ig
.Emit (OpCodes
.Stloc
, pinned_string
);
3334 Expression sptr
= new StringPtr (pinned_string
, loc
);
3335 converted
= Convert
.ImplicitConversionRequired (
3336 ec
, sptr
, vi
.VariableType
, loc
);
3338 if (converted
== null)
3341 converted
.Emit (ec
);
3342 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3345 public override void EmitExit(ILGenerator ig
)
3347 ig
.Emit (OpCodes
.Ldnull
);
3348 ig
.Emit (OpCodes
.Stloc
, pinned_string
);
3352 public Fixed (Expression type
, ArrayList decls
, Statement stmt
, Location l
)
3355 declarators
= decls
;
3360 public Statement Statement
{
3361 get { return statement; }
3364 public override bool Resolve (EmitContext ec
)
3367 Expression
.UnsafeError (loc
);
3371 TypeExpr texpr
= type
.ResolveAsTypeTerminal (ec
, false);
3375 expr_type
= texpr
.Type
;
3377 data
= new Emitter
[declarators
.Count
];
3379 if (!expr_type
.IsPointer
){
3380 Report
.Error (209, loc
, "The type of locals declared in a fixed statement must be a pointer type");
3385 foreach (Pair p
in declarators
){
3386 LocalInfo vi
= (LocalInfo
) p
.First
;
3387 Expression e
= (Expression
) p
.Second
;
3389 vi
.VariableInfo
.SetAssigned (ec
);
3390 vi
.SetReadOnlyContext (LocalInfo
.ReadOnlyContext
.Fixed
);
3393 // The rules for the possible declarators are pretty wise,
3394 // but the production on the grammar is more concise.
3396 // So we have to enforce these rules here.
3398 // We do not resolve before doing the case 1 test,
3399 // because the grammar is explicit in that the token &
3400 // is present, so we need to test for this particular case.
3404 Report
.Error (254, loc
, "The right hand side of a fixed statement assignment may not be a cast expression");
3409 // Case 1: & object.
3411 if (e
is Unary
&& ((Unary
) e
).Oper
== Unary
.Operator
.AddressOf
){
3412 Expression child
= ((Unary
) e
).Expr
;
3414 if (child
is ParameterReference
|| child
is LocalVariableReference
){
3417 "No need to use fixed statement for parameters or " +
3418 "local variable declarations (address is already " +
3423 ec
.InFixedInitializer
= true;
3425 ec
.InFixedInitializer
= false;
3429 child
= ((Unary
) e
).Expr
;
3431 if (!TypeManager
.VerifyUnManaged (child
.Type
, loc
))
3434 if (!Convert
.ImplicitConversionExists (ec
, e
, expr_type
)) {
3435 e
.Error_ValueCannotBeConverted (e
.Location
, expr_type
, false);
3439 data
[i
] = new ExpressionEmitter (e
, vi
);
3445 ec
.InFixedInitializer
= true;
3447 ec
.InFixedInitializer
= false;
3454 if (e
.Type
.IsArray
){
3455 Type array_type
= TypeManager
.GetElementType (e
.Type
);
3458 // Provided that array_type is unmanaged,
3460 if (!TypeManager
.VerifyUnManaged (array_type
, loc
))
3464 // and T* is implicitly convertible to the
3465 // pointer type given in the fixed statement.
3467 ArrayPtr array_ptr
= new ArrayPtr (e
, array_type
, loc
);
3469 Expression converted
= Convert
.ImplicitConversionRequired (
3470 ec
, array_ptr
, vi
.VariableType
, loc
);
3471 if (converted
== null)
3474 data
[i
] = new ExpressionEmitter (converted
, vi
);
3483 if (e
.Type
== TypeManager
.string_type
){
3484 data
[i
] = new StringEmitter (e
, vi
, loc
);
3489 // Case 4: fixed buffer
3490 FieldExpr fe
= e
as FieldExpr
;
3492 IFixedBuffer ff
= AttributeTester
.GetFixedBuffer (fe
.FieldInfo
);
3494 Expression fixed_buffer_ptr
= new FixedBufferPtr (fe
, ff
.ElementType
, loc
);
3496 Expression converted
= Convert
.ImplicitConversionRequired (
3497 ec
, fixed_buffer_ptr
, vi
.VariableType
, loc
);
3498 if (converted
== null)
3501 data
[i
] = new ExpressionEmitter (converted
, vi
);
3509 // For other cases, flag a `this is already fixed expression'
3511 if (e
is LocalVariableReference
|| e
is ParameterReference
||
3512 Convert
.ImplicitConversionExists (ec
, e
, vi
.VariableType
)){
3514 Report
.Error (245, loc
, "right hand expression is already fixed, no need to use fixed statement ");
3518 Report
.Error (245, loc
, "Fixed statement only allowed on strings, arrays or address-of expressions");
3522 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Conditional
, loc
);
3524 if (!statement
.Resolve (ec
)) {
3525 ec
.KillFlowBranching ();
3529 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3530 has_ret
= reachability
.IsUnreachable
;
3535 protected override void DoEmit (EmitContext ec
)
3537 for (int i
= 0; i
< data
.Length
; i
++) {
3541 statement
.Emit (ec
);
3546 ILGenerator ig
= ec
.ig
;
3549 // Clear the pinned variable
3551 for (int i
= 0; i
< data
.Length
; i
++) {
3552 data
[i
].EmitExit (ig
);
3557 public class Catch
: Statement
{
3558 public readonly string Name
;
3559 public readonly Block Block
;
3560 public readonly Block VarBlock
;
3562 Expression type_expr
;
3565 public Catch (Expression type
, string name
, Block block
, Block var_block
, Location l
)
3570 VarBlock
= var_block
;
3574 public Type CatchType
{
3580 public bool IsGeneral
{
3582 return type_expr
== null;
3586 protected override void DoEmit(EmitContext ec
)
3590 public override bool Resolve (EmitContext ec
)
3592 bool was_catch
= ec
.InCatch
;
3595 if (type_expr
!= null) {
3596 TypeExpr te
= type_expr
.ResolveAsTypeTerminal (ec
, false);
3602 if (type
!= TypeManager
.exception_type
&& !type
.IsSubclassOf (TypeManager
.exception_type
)){
3603 Error (155, "The type caught or thrown must be derived from System.Exception");
3609 if (!Block
.Resolve (ec
))
3612 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
3613 // emit the "unused variable" warnings.
3614 if (VarBlock
!= null)
3615 return VarBlock
.Resolve (ec
);
3620 ec
.InCatch
= was_catch
;
3625 public class Try
: ExceptionStatement
{
3626 public readonly Block Fini
, Block
;
3627 public readonly ArrayList Specific
;
3628 public readonly Catch General
;
3630 bool need_exc_block
;
3633 // specific, general and fini might all be null.
3635 public Try (Block block
, ArrayList specific
, Catch general
, Block fini
, Location l
)
3637 if (specific
== null && general
== null){
3638 Console
.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3642 this.Specific
= specific
;
3643 this.General
= general
;
3648 public override bool Resolve (EmitContext ec
)
3652 FlowBranchingException branching
= ec
.StartFlowBranching (this);
3654 Report
.Debug (1, "START OF TRY BLOCK", Block
.StartLocation
);
3656 if (!Block
.Resolve (ec
))
3659 FlowBranching
.UsageVector vector
= ec
.CurrentBranching
.CurrentUsageVector
;
3661 Report
.Debug (1, "START OF CATCH BLOCKS", vector
);
3663 Type
[] prevCatches
= new Type
[Specific
.Count
];
3665 foreach (Catch c
in Specific
){
3666 ec
.CurrentBranching
.CreateSibling (
3667 c
.Block
, FlowBranching
.SiblingType
.Catch
);
3669 Report
.Debug (1, "STARTED SIBLING FOR CATCH", ec
.CurrentBranching
);
3671 if (c
.Name
!= null) {
3672 LocalInfo vi
= c
.Block
.GetLocalInfo (c
.Name
);
3674 throw new Exception ();
3676 vi
.VariableInfo
= null;
3679 if (!c
.Resolve (ec
))
3682 Type resolvedType
= c
.CatchType
;
3683 for (int ii
= 0; ii
< last_index
; ++ii
) {
3684 if (resolvedType
== prevCatches
[ii
] || resolvedType
.IsSubclassOf (prevCatches
[ii
])) {
3685 Report
.Error (160, c
.loc
, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prevCatches
[ii
].FullName
);
3690 prevCatches
[last_index
++] = resolvedType
;
3691 need_exc_block
= true;
3694 Report
.Debug (1, "END OF CATCH BLOCKS", ec
.CurrentBranching
);
3696 if (General
!= null){
3697 if (CodeGen
.Assembly
.WrapNonExceptionThrows
) {
3698 foreach (Catch c
in Specific
){
3699 if (c
.CatchType
== TypeManager
.exception_type
) {
3700 Report
.Warning (1058, 1, c
.loc
, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
3705 ec
.CurrentBranching
.CreateSibling (
3706 General
.Block
, FlowBranching
.SiblingType
.Catch
);
3708 Report
.Debug (1, "STARTED SIBLING FOR GENERAL", ec
.CurrentBranching
);
3710 if (!General
.Resolve (ec
))
3713 need_exc_block
= true;
3716 Report
.Debug (1, "END OF GENERAL CATCH BLOCKS", ec
.CurrentBranching
);
3720 ec
.CurrentBranching
.CreateSibling (
3721 Fini
, FlowBranching
.SiblingType
.Finally
);
3723 Report
.Debug (1, "STARTED SIBLING FOR FINALLY", ec
.CurrentBranching
, vector
);
3724 bool was_finally
= ec
.InFinally
;
3725 ec
.InFinally
= true;
3726 if (!Fini
.Resolve (ec
))
3728 ec
.InFinally
= was_finally
;
3731 need_exc_block
= true;
3734 if (ec
.InIterator
) {
3735 ResolveFinally (branching
);
3736 need_exc_block
|= emit_finally
;
3738 emit_finally
= Fini
!= null;
3740 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
3742 FlowBranching
.UsageVector f_vector
= ec
.CurrentBranching
.CurrentUsageVector
;
3744 Report
.Debug (1, "END OF TRY", ec
.CurrentBranching
, reachability
, vector
, f_vector
);
3746 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
3747 // Unfortunately, System.Reflection.Emit automatically emits
3748 // a leave to the end of the finally block. This is a problem
3749 // if `returns' is true since we may jump to a point after the
3750 // end of the method.
3751 // As a workaround, emit an explicit ret here.
3752 ec
.NeedReturnLabel ();
3758 protected override void DoEmit (EmitContext ec
)
3760 ILGenerator ig
= ec
.ig
;
3763 ig
.BeginExceptionBlock ();
3766 foreach (Catch c
in Specific
){
3769 ig
.BeginCatchBlock (c
.CatchType
);
3771 if (c
.VarBlock
!= null)
3772 ec
.EmitScopeInitFromBlock (c
.VarBlock
);
3773 if (c
.Name
!= null){
3774 vi
= c
.Block
.GetLocalInfo (c
.Name
);
3776 throw new Exception ("Variable does not exist in this block");
3779 LocalBuilder e
= ig
.DeclareLocal (vi
.VariableType
);
3780 ig
.Emit (OpCodes
.Stloc
, e
);
3782 ec
.EmitCapturedVariableInstance (vi
);
3783 ig
.Emit (OpCodes
.Ldloc
, e
);
3784 ig
.Emit (OpCodes
.Stfld
, vi
.FieldBuilder
);
3786 ig
.Emit (OpCodes
.Stloc
, vi
.LocalBuilder
);
3788 ig
.Emit (OpCodes
.Pop
);
3793 if (General
!= null){
3794 ig
.BeginCatchBlock (TypeManager
.object_type
);
3795 ig
.Emit (OpCodes
.Pop
);
3796 General
.Block
.Emit (ec
);
3801 ig
.EndExceptionBlock ();
3804 public override void EmitFinally (EmitContext ec
)
3810 public bool HasCatch
3813 return General
!= null || Specific
.Count
> 0;
3818 public class Using
: ExceptionStatement
{
3819 object expression_or_block
;
3820 public Statement Statement
;
3824 Expression
[] resolved_vars
;
3825 Expression
[] converted_vars
;
3826 ExpressionStatement
[] assign
;
3827 LocalBuilder local_copy
;
3829 public Using (object expression_or_block
, Statement stmt
, Location l
)
3831 this.expression_or_block
= expression_or_block
;
3837 // Resolves for the case of using using a local variable declaration.
3839 bool ResolveLocalVariableDecls (EmitContext ec
)
3843 TypeExpr texpr
= expr
.ResolveAsTypeTerminal (ec
, false);
3847 expr_type
= texpr
.Type
;
3850 // The type must be an IDisposable or an implicit conversion
3853 converted_vars
= new Expression
[var_list
.Count
];
3854 resolved_vars
= new Expression
[var_list
.Count
];
3855 assign
= new ExpressionStatement
[var_list
.Count
];
3857 bool need_conv
= !TypeManager
.ImplementsInterface (
3858 expr_type
, TypeManager
.idisposable_type
);
3860 foreach (DictionaryEntry e
in var_list
){
3861 Expression
var = (Expression
) e
.Key
;
3863 var = var.ResolveLValue (ec
, new EmptyExpression (), loc
);
3867 resolved_vars
[i
] = var;
3874 converted_vars
[i
] = Convert
.ImplicitConversion (
3875 ec
, var, TypeManager
.idisposable_type
, loc
);
3877 if (converted_vars
[i
] == null) {
3878 Error_IsNotConvertibleToIDisposable ();
3886 foreach (DictionaryEntry e
in var_list
){
3887 Expression
var = resolved_vars
[i
];
3888 Expression new_expr
= (Expression
) e
.Value
;
3891 a
= new Assign (var, new_expr
, loc
);
3897 converted_vars
[i
] = var;
3898 assign
[i
] = (ExpressionStatement
) a
;
3905 void Error_IsNotConvertibleToIDisposable ()
3907 Report
.Error (1674, loc
, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
3908 TypeManager
.CSharpName (expr_type
));
3911 bool ResolveExpression (EmitContext ec
)
3913 if (!TypeManager
.ImplementsInterface (expr_type
, TypeManager
.idisposable_type
)){
3914 if (Convert
.ImplicitConversion (ec
, expr
, TypeManager
.idisposable_type
, loc
) == null) {
3915 Error_IsNotConvertibleToIDisposable ();
3924 // Emits the code for the case of using using a local variable declaration.
3926 void EmitLocalVariableDecls (EmitContext ec
)
3928 ILGenerator ig
= ec
.ig
;
3931 for (i
= 0; i
< assign
.Length
; i
++) {
3932 assign
[i
].EmitStatement (ec
);
3935 ig
.BeginExceptionBlock ();
3937 Statement
.Emit (ec
);
3939 var_list
.Reverse ();
3944 void EmitLocalVariableDeclFinally (EmitContext ec
)
3946 ILGenerator ig
= ec
.ig
;
3948 int i
= assign
.Length
;
3949 for (int ii
= 0; ii
< var_list
.Count
; ++ii
){
3950 Expression
var = resolved_vars
[--i
];
3951 Label skip
= ig
.DefineLabel ();
3953 ig
.BeginFinallyBlock ();
3955 if (!var.Type
.IsValueType
) {
3957 ig
.Emit (OpCodes
.Brfalse
, skip
);
3958 converted_vars
[i
].Emit (ec
);
3959 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
3961 Expression ml
= Expression
.MemberLookup(ec
.ContainerType
, TypeManager
.idisposable_type
, var.Type
, "Dispose", Mono
.CSharp
.Location
.Null
);
3963 if (!(ml
is MethodGroupExpr
)) {
3965 ig
.Emit (OpCodes
.Box
, var.Type
);
3966 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
3968 MethodInfo mi
= null;
3970 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
3971 if (TypeManager
.GetParameterData (mk
).Count
== 0) {
3978 Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
3982 IMemoryLocation mloc
= (IMemoryLocation
) var;
3984 mloc
.AddressOf (ec
, AddressOp
.Load
);
3985 ig
.Emit (OpCodes
.Call
, mi
);
3989 ig
.MarkLabel (skip
);
3992 ig
.EndExceptionBlock ();
3994 ig
.BeginFinallyBlock ();
3999 void EmitExpression (EmitContext ec
)
4002 // Make a copy of the expression and operate on that.
4004 ILGenerator ig
= ec
.ig
;
4005 local_copy
= ig
.DeclareLocal (expr_type
);
4008 ig
.Emit (OpCodes
.Stloc
, local_copy
);
4011 ig
.BeginExceptionBlock ();
4013 Statement
.Emit (ec
);
4017 ig
.EndExceptionBlock ();
4020 void EmitExpressionFinally (EmitContext ec
)
4022 ILGenerator ig
= ec
.ig
;
4023 if (!local_copy
.LocalType
.IsValueType
) {
4024 Label skip
= ig
.DefineLabel ();
4025 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
4026 ig
.Emit (OpCodes
.Brfalse
, skip
);
4027 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
4028 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4029 ig
.MarkLabel (skip
);
4031 Expression ml
= Expression
.MemberLookup(ec
.ContainerType
, TypeManager
.idisposable_type
, local_copy
.LocalType
, "Dispose", Mono
.CSharp
.Location
.Null
);
4033 if (!(ml
is MethodGroupExpr
)) {
4034 ig
.Emit (OpCodes
.Ldloc
, local_copy
);
4035 ig
.Emit (OpCodes
.Box
, local_copy
.LocalType
);
4036 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4038 MethodInfo mi
= null;
4040 foreach (MethodInfo mk
in ((MethodGroupExpr
) ml
).Methods
) {
4041 if (TypeManager
.GetParameterData (mk
).Count
== 0) {
4048 Report
.Error(-100, Mono
.CSharp
.Location
.Null
, "Internal error: No Dispose method which takes 0 parameters.");
4052 ig
.Emit (OpCodes
.Ldloca
, local_copy
);
4053 ig
.Emit (OpCodes
.Call
, mi
);
4058 public override bool Resolve (EmitContext ec
)
4060 if (expression_or_block
is DictionaryEntry
){
4061 expr
= (Expression
) ((DictionaryEntry
) expression_or_block
).Key
;
4062 var_list
= (ArrayList
)((DictionaryEntry
)expression_or_block
).Value
;
4064 if (!ResolveLocalVariableDecls (ec
))
4067 } else if (expression_or_block
is Expression
){
4068 expr
= (Expression
) expression_or_block
;
4070 expr
= expr
.Resolve (ec
);
4074 expr_type
= expr
.Type
;
4076 if (!ResolveExpression (ec
))
4080 FlowBranchingException branching
= ec
.StartFlowBranching (this);
4082 bool ok
= Statement
.Resolve (ec
);
4085 ec
.KillFlowBranching ();
4089 ResolveFinally (branching
);
4090 FlowBranching
.Reachability reachability
= ec
.EndFlowBranching ();
4092 if (reachability
.Returns
!= FlowBranching
.FlowReturns
.Always
) {
4093 // Unfortunately, System.Reflection.Emit automatically emits a leave
4094 // to the end of the finally block. This is a problem if `returns'
4095 // is true since we may jump to a point after the end of the method.
4096 // As a workaround, emit an explicit ret here.
4097 ec
.NeedReturnLabel ();
4103 protected override void DoEmit (EmitContext ec
)
4105 if (expression_or_block
is DictionaryEntry
)
4106 EmitLocalVariableDecls (ec
);
4107 else if (expression_or_block
is Expression
)
4108 EmitExpression (ec
);
4111 public override void EmitFinally (EmitContext ec
)
4113 if (expression_or_block
is DictionaryEntry
)
4114 EmitLocalVariableDeclFinally (ec
);
4115 else if (expression_or_block
is Expression
)
4116 EmitExpressionFinally (ec
);
4121 /// Implementation of the foreach C# statement
4123 public class Foreach
: Statement
{
4125 Expression variable
;
4127 Statement statement
;
4129 CollectionForeach collection
;
4131 public Foreach (Expression type
, LocalVariableReference
var, Expression expr
,
4132 Statement stmt
, Location l
)
4135 this.variable
= var;
4141 public Statement Statement
{
4142 get { return statement; }
4145 public override bool Resolve (EmitContext ec
)
4147 expr
= expr
.Resolve (ec
);
4151 Constant c
= expr
as Constant
;
4152 if (c
!= null && c
.GetValue () == null) {
4153 Report
.Error (186, loc
, "Use of null is not valid in this context");
4157 TypeExpr texpr
= type
.ResolveAsTypeTerminal (ec
, false);
4161 Type var_type
= texpr
.Type
;
4163 if (expr
.eclass
== ExprClass
.MethodGroup
|| expr
is AnonymousMethod
) {
4164 Report
.Error (446, expr
.Location
, "Foreach statement cannot operate on a `{0}'",
4165 expr
.ExprClassName
);
4170 // We need an instance variable. Not sure this is the best
4171 // way of doing this.
4173 // FIXME: When we implement propertyaccess, will those turn
4174 // out to return values in ExprClass? I think they should.
4176 if (!(expr
.eclass
== ExprClass
.Variable
|| expr
.eclass
== ExprClass
.Value
||
4177 expr
.eclass
== ExprClass
.PropertyAccess
|| expr
.eclass
== ExprClass
.IndexerAccess
)){
4178 collection
.Error_Enumerator ();
4182 if (expr
.Type
.IsArray
) {
4183 array
= new ArrayForeach (var_type
, variable
, expr
, statement
, loc
);
4184 return array
.Resolve (ec
);
4186 collection
= new CollectionForeach (
4187 var_type
, variable
, expr
, statement
, loc
);
4188 return collection
.Resolve (ec
);
4192 protected override void DoEmit (EmitContext ec
)
4194 ILGenerator ig
= ec
.ig
;
4196 Label old_begin
= ec
.LoopBegin
, old_end
= ec
.LoopEnd
;
4197 ec
.LoopBegin
= ig
.DefineLabel ();
4198 ec
.LoopEnd
= ig
.DefineLabel ();
4200 if (collection
!= null)
4201 collection
.Emit (ec
);
4205 ec
.LoopBegin
= old_begin
;
4206 ec
.LoopEnd
= old_end
;
4209 protected class ArrayCounter
: TemporaryVariable
4211 public ArrayCounter (Location loc
)
4212 : base (TypeManager
.int32_type
, loc
)
4215 public void Initialize (EmitContext ec
)
4218 ec
.ig
.Emit (OpCodes
.Ldc_I4_0
);
4222 public void Increment (EmitContext ec
)
4226 ec
.ig
.Emit (OpCodes
.Ldc_I4_1
);
4227 ec
.ig
.Emit (OpCodes
.Add
);
4232 protected class ArrayForeach
: Statement
4234 Expression variable
, expr
, conv
;
4235 Statement statement
;
4238 TemporaryVariable
[] lengths
;
4239 ArrayCounter
[] counter
;
4242 TemporaryVariable copy
;
4245 public ArrayForeach (Type var_type
, Expression
var,
4246 Expression expr
, Statement stmt
, Location l
)
4248 this.var_type
= var_type
;
4249 this.variable
= var;
4255 public override bool Resolve (EmitContext ec
)
4257 array_type
= expr
.Type
;
4258 rank
= array_type
.GetArrayRank ();
4260 copy
= new TemporaryVariable (array_type
, loc
);
4263 counter
= new ArrayCounter
[rank
];
4264 lengths
= new TemporaryVariable
[rank
];
4266 ArrayList list
= new ArrayList ();
4267 for (int i
= 0; i
< rank
; i
++) {
4268 counter
[i
] = new ArrayCounter (loc
);
4269 counter
[i
].Resolve (ec
);
4271 lengths
[i
] = new TemporaryVariable (TypeManager
.int32_type
, loc
);
4272 lengths
[i
].Resolve (ec
);
4274 list
.Add (counter
[i
]);
4277 access
= new ElementAccess (copy
, list
).Resolve (ec
);
4281 conv
= Convert
.ExplicitConversion (ec
, access
, var_type
, loc
);
4287 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
4288 ec
.CurrentBranching
.CreateSibling ();
4290 variable
= variable
.ResolveLValue (ec
, conv
, loc
);
4291 if (variable
== null)
4294 if (!statement
.Resolve (ec
))
4297 ec
.EndFlowBranching ();
4302 protected override void DoEmit (EmitContext ec
)
4304 ILGenerator ig
= ec
.ig
;
4306 copy
.Store (ec
, expr
);
4308 Label
[] test
= new Label
[rank
];
4309 Label
[] loop
= new Label
[rank
];
4311 for (int i
= 0; i
< rank
; i
++) {
4312 test
[i
] = ig
.DefineLabel ();
4313 loop
[i
] = ig
.DefineLabel ();
4315 lengths
[i
].EmitThis (ec
);
4316 ((ArrayAccess
) access
).EmitGetLength (ec
, i
);
4317 lengths
[i
].EmitStore (ig
);
4320 for (int i
= 0; i
< rank
; i
++) {
4321 counter
[i
].Initialize (ec
);
4323 ig
.Emit (OpCodes
.Br
, test
[i
]);
4324 ig
.MarkLabel (loop
[i
]);
4327 ((IAssignMethod
) variable
).EmitAssign (ec
, conv
, false, false);
4329 statement
.Emit (ec
);
4331 ig
.MarkLabel (ec
.LoopBegin
);
4333 for (int i
= rank
- 1; i
>= 0; i
--){
4334 counter
[i
].Increment (ec
);
4336 ig
.MarkLabel (test
[i
]);
4337 counter
[i
].Emit (ec
);
4338 lengths
[i
].Emit (ec
);
4339 ig
.Emit (OpCodes
.Blt
, loop
[i
]);
4342 ig
.MarkLabel (ec
.LoopEnd
);
4346 protected class CollectionForeach
: ExceptionStatement
4348 Expression variable
, expr
;
4349 Statement statement
;
4351 TemporaryVariable enumerator
;
4355 MethodGroupExpr get_enumerator
;
4356 PropertyExpr get_current
;
4357 MethodInfo move_next
;
4358 Type var_type
, enumerator_type
;
4360 bool enumerator_found
;
4362 public CollectionForeach (Type var_type
, Expression
var,
4363 Expression expr
, Statement stmt
, Location l
)
4365 this.var_type
= var_type
;
4366 this.variable
= var;
4372 bool GetEnumeratorFilter (EmitContext ec
, MethodInfo mi
)
4374 Type return_type
= mi
.ReturnType
;
4376 if ((return_type
== TypeManager
.ienumerator_type
) && (mi
.DeclaringType
== TypeManager
.string_type
))
4378 // Apply the same optimization as MS: skip the GetEnumerator
4379 // returning an IEnumerator, and use the one returning a
4380 // CharEnumerator instead. This allows us to avoid the
4381 // try-finally block and the boxing.
4386 // Ok, we can access it, now make sure that we can do something
4387 // with this `GetEnumerator'
4390 if (return_type
== TypeManager
.ienumerator_type
||
4391 TypeManager
.ienumerator_type
.IsAssignableFrom (return_type
) ||
4392 (!RootContext
.StdLib
&& TypeManager
.ImplementsInterface (return_type
, TypeManager
.ienumerator_type
))) {
4394 // If it is not an interface, lets try to find the methods ourselves.
4395 // For example, if we have:
4396 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4397 // We can avoid the iface call. This is a runtime perf boost.
4398 // even bigger if we have a ValueType, because we avoid the cost
4401 // We have to make sure that both methods exist for us to take
4402 // this path. If one of the methods does not exist, we will just
4403 // use the interface. Sadly, this complex if statement is the only
4404 // way I could do this without a goto
4407 if (return_type
.IsInterface
&& return_type
.IsGenericType
) {
4408 enumerator_type
= return_type
;
4409 if (!FetchGetCurrent (ec
, return_type
))
4410 get_current
= new PropertyExpr (
4411 ec
.ContainerType
, TypeManager
.ienumerator_getcurrent
, loc
);
4412 if (!FetchMoveNext (return_type
))
4413 move_next
= TypeManager
.bool_movenext_void
;
4417 if (return_type
.IsInterface
||
4418 !FetchMoveNext (return_type
) ||
4419 !FetchGetCurrent (ec
, return_type
)) {
4420 enumerator_type
= return_type
;
4421 move_next
= TypeManager
.bool_movenext_void
;
4422 get_current
= new PropertyExpr (
4423 ec
.ContainerType
, TypeManager
.ienumerator_getcurrent
, loc
);
4428 // Ok, so they dont return an IEnumerable, we will have to
4429 // find if they support the GetEnumerator pattern.
4432 if (TypeManager
.HasElementType (return_type
) || !FetchMoveNext (return_type
) || !FetchGetCurrent (ec
, return_type
)) {
4433 Report
.Error (202, loc
, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
4434 TypeManager
.CSharpName (return_type
), TypeManager
.CSharpSignature (mi
));
4439 enumerator_type
= return_type
;
4440 is_disposable
= !enumerator_type
.IsSealed
||
4441 TypeManager
.ImplementsInterface (
4442 enumerator_type
, TypeManager
.idisposable_type
);
4448 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4450 bool FetchMoveNext (Type t
)
4452 MemberList move_next_list
;
4454 move_next_list
= TypeContainer
.FindMembers (
4455 t
, MemberTypes
.Method
,
4456 Expression
.AllBindingFlags
,
4457 Type
.FilterName
, "MoveNext");
4458 if (move_next_list
.Count
== 0)
4462 foreach (MemberInfo m
in move_next_list
){
4463 MethodInfo mi
= (MethodInfo
) m
;
4465 if ((TypeManager
.GetParameterData (mi
).Count
== 0) &&
4466 TypeManager
.TypeToCoreType (mi
.ReturnType
) == TypeManager
.bool_type
) {
4478 // Retrieves a `public T get_Current ()' method from the Type `t'
4480 bool FetchGetCurrent (EmitContext ec
, Type t
)
4482 PropertyExpr pe
= Expression
.MemberLookup (
4483 ec
.ContainerType
, t
, "Current", MemberTypes
.Property
,
4484 Expression
.AllBindingFlags
, loc
) as PropertyExpr
;
4493 // Retrieves a `public void Dispose ()' method from the Type `t'
4495 static MethodInfo
FetchMethodDispose (Type t
)
4497 MemberList dispose_list
;
4499 dispose_list
= TypeContainer
.FindMembers (
4500 t
, MemberTypes
.Method
,
4501 BindingFlags
.Public
| BindingFlags
.Instance
,
4502 Type
.FilterName
, "Dispose");
4503 if (dispose_list
.Count
== 0)
4506 foreach (MemberInfo m
in dispose_list
){
4507 MethodInfo mi
= (MethodInfo
) m
;
4509 if (TypeManager
.GetParameterData (mi
).Count
== 0){
4510 if (mi
.ReturnType
== TypeManager
.void_type
)
4517 public void Error_Enumerator ()
4519 if (enumerator_found
) {
4523 Report
.Error (1579, loc
,
4524 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
4525 TypeManager
.CSharpName (expr
.Type
));
4528 bool TryType (EmitContext ec
, Type t
)
4530 MethodGroupExpr mg
= Expression
.MemberLookup (
4531 ec
.ContainerType
, t
, "GetEnumerator", MemberTypes
.Method
,
4532 Expression
.AllBindingFlags
, loc
) as MethodGroupExpr
;
4536 MethodBase result
= null;
4537 MethodInfo tmp_move_next
= null;
4538 PropertyExpr tmp_get_cur
= null;
4539 Type tmp_enumerator_type
= enumerator_type
;
4540 foreach (MethodInfo mi
in mg
.Methods
) {
4541 if (TypeManager
.GetParameterData (mi
).Count
!= 0)
4544 // Check whether GetEnumerator is public
4545 if ((mi
.Attributes
& MethodAttributes
.Public
) != MethodAttributes
.Public
)
4548 if (TypeManager
.IsOverride (mi
))
4551 enumerator_found
= true;
4553 if (!GetEnumeratorFilter (ec
, mi
)) {
4558 tmp_move_next
= move_next
;
4559 tmp_get_cur
= get_current
;
4560 tmp_enumerator_type
= enumerator_type
;
4561 if (mi
.DeclaringType
== t
)
4565 if (result
!= null) {
4566 move_next
= tmp_move_next
;
4567 get_current
= tmp_get_cur
;
4568 enumerator_type
= tmp_enumerator_type
;
4569 MethodInfo
[] mi
= new MethodInfo
[] { (MethodInfo) result }
;
4570 get_enumerator
= new MethodGroupExpr (mi
, loc
);
4572 if (t
!= expr
.Type
) {
4573 expr
= Convert
.ExplicitConversion (
4576 throw new InternalErrorException ();
4579 get_enumerator
.InstanceExpression
= expr
;
4580 get_enumerator
.IsBase
= t
!= expr
.Type
;
4588 bool ProbeCollectionType (EmitContext ec
, Type t
)
4590 for (Type tt
= t
; tt
!= null && tt
!= TypeManager
.object_type
;){
4591 if (TryType (ec
, tt
))
4597 // Now try to find the method in the interfaces
4600 Type
[] ifaces
= t
.GetInterfaces ();
4602 foreach (Type i
in ifaces
){
4603 if (TryType (ec
, i
))
4608 // Since TypeBuilder.GetInterfaces only returns the interface
4609 // types for this type, we have to keep looping, but once
4610 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4611 // done, because it returns all the types
4613 if ((t
is TypeBuilder
))
4622 public override bool Resolve (EmitContext ec
)
4624 enumerator_type
= TypeManager
.ienumerator_type
;
4625 is_disposable
= true;
4627 if (!ProbeCollectionType (ec
, expr
.Type
)) {
4628 Error_Enumerator ();
4632 enumerator
= new TemporaryVariable (enumerator_type
, loc
);
4633 enumerator
.Resolve (ec
);
4635 init
= new Invocation (get_enumerator
, new ArrayList ());
4636 init
= init
.Resolve (ec
);
4640 Expression move_next_expr
;
4642 MemberInfo
[] mi
= new MemberInfo
[] { move_next }
;
4643 MethodGroupExpr mg
= new MethodGroupExpr (mi
, loc
);
4644 mg
.InstanceExpression
= enumerator
;
4646 move_next_expr
= new Invocation (mg
, new ArrayList ());
4649 get_current
.InstanceExpression
= enumerator
;
4651 Statement block
= new CollectionForeachStatement (
4652 var_type
, variable
, get_current
, statement
, loc
);
4654 loop
= new While (move_next_expr
, block
, loc
);
4658 ec
.StartFlowBranching (FlowBranching
.BranchingType
.Loop
, loc
);
4659 ec
.CurrentBranching
.CreateSibling ();
4661 FlowBranchingException branching
= null;
4663 branching
= ec
.StartFlowBranching (this);
4665 if (!loop
.Resolve (ec
))
4668 if (is_disposable
) {
4669 ResolveFinally (branching
);
4670 ec
.EndFlowBranching ();
4672 emit_finally
= true;
4674 ec
.EndFlowBranching ();
4679 protected override void DoEmit (EmitContext ec
)
4681 ILGenerator ig
= ec
.ig
;
4683 enumerator
.Store (ec
, init
);
4686 // Protect the code in a try/finalize block, so that
4687 // if the beast implement IDisposable, we get rid of it
4689 if (is_disposable
&& emit_finally
)
4690 ig
.BeginExceptionBlock ();
4695 // Now the finally block
4697 if (is_disposable
) {
4700 ig
.EndExceptionBlock ();
4705 public override void EmitFinally (EmitContext ec
)
4707 ILGenerator ig
= ec
.ig
;
4709 if (enumerator_type
.IsValueType
) {
4710 MethodInfo mi
= FetchMethodDispose (enumerator_type
);
4712 enumerator
.EmitLoadAddress (ec
);
4713 ig
.Emit (OpCodes
.Call
, mi
);
4715 enumerator
.Emit (ec
);
4716 ig
.Emit (OpCodes
.Box
, enumerator_type
);
4717 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4720 Label call_dispose
= ig
.DefineLabel ();
4722 enumerator
.Emit (ec
);
4723 ig
.Emit (OpCodes
.Isinst
, TypeManager
.idisposable_type
);
4724 ig
.Emit (OpCodes
.Dup
);
4725 ig
.Emit (OpCodes
.Brtrue_S
, call_dispose
);
4726 ig
.Emit (OpCodes
.Pop
);
4728 Label end_finally
= ig
.DefineLabel ();
4729 ig
.Emit (OpCodes
.Br
, end_finally
);
4731 ig
.MarkLabel (call_dispose
);
4732 ig
.Emit (OpCodes
.Callvirt
, TypeManager
.void_dispose_void
);
4733 ig
.MarkLabel (end_finally
);
4738 protected class CollectionForeachStatement
: Statement
4741 Expression variable
, current
, conv
;
4742 Statement statement
;
4745 public CollectionForeachStatement (Type type
, Expression variable
,
4746 Expression current
, Statement statement
,
4750 this.variable
= variable
;
4751 this.current
= current
;
4752 this.statement
= statement
;
4756 public override bool Resolve (EmitContext ec
)
4758 current
= current
.Resolve (ec
);
4759 if (current
== null)
4762 conv
= Convert
.ExplicitConversion (ec
, current
, type
, loc
);
4766 assign
= new Assign (variable
, conv
, loc
);
4767 if (assign
.Resolve (ec
) == null)
4770 if (!statement
.Resolve (ec
))
4776 protected override void DoEmit (EmitContext ec
)
4778 assign
.EmitStatement (ec
);
4779 statement
.Emit (ec
);