2005-02-19 Ben Maurer <bmaurer@ximian.com>
[mono-project.git] / mcs / bmcs / statement.cs
blobdf0ba63808e3ad1c3ce29b5672793959be85d387
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 //
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
12 using System;
13 using System.Text;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Diagnostics;
18 namespace Mono.CSharp {
20 using System.Collections;
22 public abstract class Statement {
23 public Location loc;
25 /// <summary>
26 /// Resolves the statement, true means that all sub-statements
27 /// did resolve ok.
28 // </summary>
29 public virtual bool Resolve (EmitContext ec)
31 return true;
34 /// <summary>
35 /// We already know that the statement is unreachable, but we still
36 /// need to resolve it to catch errors.
37 /// </summary>
38 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
41 // This conflicts with csc's way of doing this, but IMHO it's
42 // the right thing to do.
44 // If something is unreachable, we still check whether it's
45 // correct. This means that you cannot use unassigned variables
46 // in unreachable code, for instance.
49 if (warn && (RootContext.WarningLevel >= 2))
50 Report.Warning (162, loc, "Unreachable code detected");
52 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53 bool ok = Resolve (ec);
54 ec.KillFlowBranching ();
56 return ok;
59 protected void CheckObsolete (Type type)
61 ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type);
62 if (obsolete_attr == null)
63 return;
65 AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, loc);
68 /// <summary>
69 /// Return value indicates whether all code paths emitted return.
70 /// </summary>
71 protected abstract void DoEmit (EmitContext ec);
73 /// <summary>
74 /// Utility wrapper routine for Error, just to beautify the code
75 /// </summary>
76 public void Error (int error, string format, params object[] args)
78 Error (error, String.Format (format, args));
81 public void Error (int error, string s)
83 if (!Location.IsNull (loc))
84 Report.Error (error, loc, s);
85 else
86 Report.Error (error, s);
89 /// <summary>
90 /// Return value indicates whether all code paths emitted return.
91 /// </summary>
92 public virtual void Emit (EmitContext ec)
94 ec.Mark (loc, true);
95 DoEmit (ec);
99 public sealed class EmptyStatement : Statement {
101 private EmptyStatement () {}
103 public static readonly EmptyStatement Value = new EmptyStatement ();
105 public override bool Resolve (EmitContext ec)
107 return true;
110 protected override void DoEmit (EmitContext ec)
115 public class If : Statement {
116 Expression expr;
117 public Statement TrueStatement;
118 public Statement FalseStatement;
120 bool is_true_ret;
122 public If (Expression expr, Statement trueStatement, Location l)
124 this.expr = expr;
125 TrueStatement = trueStatement;
126 loc = l;
129 public If (Expression expr,
130 Statement trueStatement,
131 Statement falseStatement,
132 Location l)
134 this.expr = expr;
135 TrueStatement = trueStatement;
136 FalseStatement = falseStatement;
137 loc = l;
140 public override bool Resolve (EmitContext ec)
142 Report.Debug (1, "START IF BLOCK", loc);
144 expr = Expression.ResolveBoolean (ec, expr, loc);
145 if (expr == null){
146 return false;
149 Assign ass = expr as Assign;
150 if (ass != null && ass.Source is Constant) {
151 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
155 // Dead code elimination
157 if (expr is BoolConstant){
158 bool take = ((BoolConstant) expr).Value;
160 if (take){
161 if (!TrueStatement.Resolve (ec))
162 return false;
164 if ((FalseStatement != null) &&
165 !FalseStatement.ResolveUnreachable (ec, true))
166 return false;
167 FalseStatement = null;
168 } else {
169 if (!TrueStatement.ResolveUnreachable (ec, true))
170 return false;
171 TrueStatement = null;
173 if ((FalseStatement != null) &&
174 !FalseStatement.Resolve (ec))
175 return false;
178 return true;
181 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
183 bool ok = TrueStatement.Resolve (ec);
185 is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
187 ec.CurrentBranching.CreateSibling ();
189 if ((FalseStatement != null) && !FalseStatement.Resolve (ec))
190 ok = false;
192 ec.EndFlowBranching ();
194 Report.Debug (1, "END IF BLOCK", loc);
196 return ok;
199 protected override void DoEmit (EmitContext ec)
201 ILGenerator ig = ec.ig;
202 Label false_target = ig.DefineLabel ();
203 Label end;
206 // If we're a boolean expression, Resolve() already
207 // eliminated dead code for us.
209 if (expr is BoolConstant){
210 bool take = ((BoolConstant) expr).Value;
212 if (take)
213 TrueStatement.Emit (ec);
214 else if (FalseStatement != null)
215 FalseStatement.Emit (ec);
217 return;
220 expr.EmitBranchable (ec, false_target, false);
222 TrueStatement.Emit (ec);
224 if (FalseStatement != null){
225 bool branch_emitted = false;
227 end = ig.DefineLabel ();
228 if (!is_true_ret){
229 ig.Emit (OpCodes.Br, end);
230 branch_emitted = true;
233 ig.MarkLabel (false_target);
234 FalseStatement.Emit (ec);
236 if (branch_emitted)
237 ig.MarkLabel (end);
238 } else {
239 ig.MarkLabel (false_target);
244 public class Do : Statement {
245 public Expression expr;
246 public readonly Statement EmbeddedStatement;
247 bool infinite;
249 public Do (Statement statement, Expression boolExpr, Location l)
251 expr = boolExpr;
252 EmbeddedStatement = statement;
253 loc = l;
256 public override bool Resolve (EmitContext ec)
258 bool ok = true;
260 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
262 if (!EmbeddedStatement.Resolve (ec))
263 ok = false;
265 expr = Expression.ResolveBoolean (ec, expr, loc);
266 if (expr == null)
267 ok = false;
268 else if (expr is BoolConstant){
269 bool res = ((BoolConstant) expr).Value;
271 if (res)
272 infinite = true;
275 ec.CurrentBranching.Infinite = infinite;
276 ec.EndFlowBranching ();
278 return ok;
281 protected override void DoEmit (EmitContext ec)
283 ILGenerator ig = ec.ig;
284 Label loop = ig.DefineLabel ();
285 Label old_begin = ec.LoopBegin;
286 Label old_end = ec.LoopEnd;
288 ec.LoopBegin = ig.DefineLabel ();
289 ec.LoopEnd = ig.DefineLabel ();
291 ig.MarkLabel (loop);
292 EmbeddedStatement.Emit (ec);
293 ig.MarkLabel (ec.LoopBegin);
296 // Dead code elimination
298 if (expr is BoolConstant){
299 bool res = ((BoolConstant) expr).Value;
301 if (res)
302 ec.ig.Emit (OpCodes.Br, loop);
303 } else
304 expr.EmitBranchable (ec, loop, true);
306 ig.MarkLabel (ec.LoopEnd);
308 ec.LoopBegin = old_begin;
309 ec.LoopEnd = old_end;
313 public class While : Statement {
314 public Expression expr;
315 public readonly Statement Statement;
316 bool infinite, empty;
318 public While (Expression boolExpr, Statement statement, Location l)
320 this.expr = boolExpr;
321 Statement = statement;
322 loc = l;
325 public override bool Resolve (EmitContext ec)
327 bool ok = true;
329 expr = Expression.ResolveBoolean (ec, expr, loc);
330 if (expr == null)
331 return false;
334 // Inform whether we are infinite or not
336 if (expr is BoolConstant){
337 BoolConstant bc = (BoolConstant) expr;
339 if (bc.Value == false){
340 if (!Statement.ResolveUnreachable (ec, true))
341 return false;
342 empty = true;
343 return true;
344 } else
345 infinite = true;
348 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
350 if (!Statement.Resolve (ec))
351 ok = false;
353 ec.CurrentBranching.Infinite = infinite;
354 ec.EndFlowBranching ();
356 return ok;
359 protected override void DoEmit (EmitContext ec)
361 if (empty)
362 return;
364 ILGenerator ig = ec.ig;
365 Label old_begin = ec.LoopBegin;
366 Label old_end = ec.LoopEnd;
368 ec.LoopBegin = ig.DefineLabel ();
369 ec.LoopEnd = ig.DefineLabel ();
372 // Inform whether we are infinite or not
374 if (expr is BoolConstant){
375 ig.MarkLabel (ec.LoopBegin);
376 Statement.Emit (ec);
377 ig.Emit (OpCodes.Br, ec.LoopBegin);
380 // Inform that we are infinite (ie, `we return'), only
381 // if we do not `break' inside the code.
383 ig.MarkLabel (ec.LoopEnd);
384 } else {
385 Label while_loop = ig.DefineLabel ();
387 ig.Emit (OpCodes.Br, ec.LoopBegin);
388 ig.MarkLabel (while_loop);
390 Statement.Emit (ec);
392 ig.MarkLabel (ec.LoopBegin);
394 expr.EmitBranchable (ec, while_loop, true);
396 ig.MarkLabel (ec.LoopEnd);
399 ec.LoopBegin = old_begin;
400 ec.LoopEnd = old_end;
404 public class For : Statement {
405 Expression Test;
406 readonly Statement InitStatement;
407 readonly Statement Increment;
408 readonly Statement Statement;
409 bool infinite, empty;
411 public For (Statement initStatement,
412 Expression test,
413 Statement increment,
414 Statement statement,
415 Location l)
417 InitStatement = initStatement;
418 Test = test;
419 Increment = increment;
420 Statement = statement;
421 loc = l;
424 public override bool Resolve (EmitContext ec)
426 bool ok = true;
428 if (InitStatement != null){
429 if (!InitStatement.Resolve (ec))
430 ok = false;
433 if (Test != null){
434 Test = Expression.ResolveBoolean (ec, Test, loc);
435 if (Test == null)
436 ok = false;
437 else if (Test is BoolConstant){
438 BoolConstant bc = (BoolConstant) Test;
440 if (bc.Value == false){
441 if (!Statement.ResolveUnreachable (ec, true))
442 return false;
443 if ((Increment != null) &&
444 !Increment.ResolveUnreachable (ec, false))
445 return false;
446 empty = true;
447 return true;
448 } else
449 infinite = true;
451 } else
452 infinite = true;
454 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
455 if (!infinite)
456 ec.CurrentBranching.CreateSibling ();
458 if (!Statement.Resolve (ec))
459 ok = false;
461 if (Increment != null){
462 if (!Increment.Resolve (ec))
463 ok = false;
466 ec.CurrentBranching.Infinite = infinite;
467 ec.EndFlowBranching ();
469 return ok;
472 protected override void DoEmit (EmitContext ec)
474 if (empty)
475 return;
477 ILGenerator ig = ec.ig;
478 Label old_begin = ec.LoopBegin;
479 Label old_end = ec.LoopEnd;
480 Label loop = ig.DefineLabel ();
481 Label test = ig.DefineLabel ();
483 if (InitStatement != null && InitStatement != EmptyStatement.Value)
484 InitStatement.Emit (ec);
486 ec.LoopBegin = ig.DefineLabel ();
487 ec.LoopEnd = ig.DefineLabel ();
489 ig.Emit (OpCodes.Br, test);
490 ig.MarkLabel (loop);
491 Statement.Emit (ec);
493 ig.MarkLabel (ec.LoopBegin);
494 if (Increment != EmptyStatement.Value)
495 Increment.Emit (ec);
497 ig.MarkLabel (test);
499 // If test is null, there is no test, and we are just
500 // an infinite loop
502 if (Test != null){
504 // The Resolve code already catches the case for
505 // Test == BoolConstant (false) so we know that
506 // this is true
508 if (Test is BoolConstant)
509 ig.Emit (OpCodes.Br, loop);
510 else
511 Test.EmitBranchable (ec, loop, true);
513 } else
514 ig.Emit (OpCodes.Br, loop);
515 ig.MarkLabel (ec.LoopEnd);
517 ec.LoopBegin = old_begin;
518 ec.LoopEnd = old_end;
522 public class StatementExpression : Statement {
523 public ExpressionStatement expr;
525 public StatementExpression (ExpressionStatement expr, Location l)
527 this.expr = expr;
528 loc = l;
531 public override bool Resolve (EmitContext ec)
533 expr = expr.ResolveStatement (ec);
534 return expr != null;
537 protected override void DoEmit (EmitContext ec)
539 expr.EmitStatement (ec);
542 public override string ToString ()
544 return "StatementExpression (" + expr + ")";
548 /// <summary>
549 /// Implements the return statement
550 /// </summary>
551 public class Return : Statement {
552 public Expression Expr;
554 public Return (Expression expr, Location l)
556 Expr = expr;
557 loc = l;
560 bool in_exc;
562 public override bool Resolve (EmitContext ec)
564 if (ec.ReturnType == null){
565 if (Expr != null){
566 if (ec.CurrentAnonymousMethod != null){
567 Report.Error (1662, loc, String.Format (
568 "Anonymous method could not be converted to delegate " +
569 "since the return value does not match the delegate value"));
571 Error (127, "Return with a value not allowed here");
572 return false;
574 } else {
575 if (Expr == null){
576 Error (126, "An object of type `{0}' is expected " +
577 "for the return statement",
578 TypeManager.CSharpName (ec.ReturnType));
579 return false;
582 Expr = Expr.Resolve (ec);
583 if (Expr == null)
584 return false;
586 if (Expr.Type != ec.ReturnType) {
587 Expr = Convert.ImplicitConversionRequired (
588 ec, Expr, ec.ReturnType, loc);
589 if (Expr == null)
590 return false;
594 if (ec.InIterator){
595 Error (-206, "Return statement not allowed inside iterators");
596 return false;
599 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
601 if (ec.CurrentBranching.InTryOrCatch (true)) {
602 ec.CurrentBranching.AddFinallyVector (vector);
603 in_exc = true;
604 } else if (ec.CurrentBranching.InFinally (true)) {
605 Error (157, "Control can not leave the body of the finally block");
606 return false;
607 } else
608 vector.CheckOutParameters (ec.CurrentBranching);
610 if (in_exc)
611 ec.NeedReturnLabel ();
613 ec.CurrentBranching.CurrentUsageVector.Return ();
614 return true;
617 protected override void DoEmit (EmitContext ec)
619 if (Expr != null) {
620 Expr.Emit (ec);
622 if (in_exc)
623 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
626 if (in_exc)
627 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
628 else
629 ec.ig.Emit (OpCodes.Ret);
633 public class Goto : Statement {
634 string target;
635 Block block;
636 LabeledStatement label;
638 public override bool Resolve (EmitContext ec)
640 label = ec.CurrentBranching.LookupLabel (target, loc);
641 if (label == null)
642 return false;
644 // If this is a forward goto.
645 if (!label.IsDefined)
646 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
648 ec.CurrentBranching.CurrentUsageVector.Goto ();
650 return true;
653 public Goto (Block parent_block, string label, Location l)
655 block = parent_block;
656 loc = l;
657 target = label;
660 public string Target {
661 get {
662 return target;
666 protected override void DoEmit (EmitContext ec)
668 Label l = label.LabelTarget (ec);
669 ec.ig.Emit (OpCodes.Br, l);
673 public class LabeledStatement : Statement {
674 public readonly Location Location;
675 bool defined;
676 bool referenced;
677 Label label;
678 ILGenerator ig;
680 FlowBranching.UsageVector vectors;
682 public LabeledStatement (string label_name, Location l)
684 this.Location = l;
687 public Label LabelTarget (EmitContext ec)
689 if (defined)
690 return label;
691 ig = ec.ig;
692 label = ec.ig.DefineLabel ();
693 defined = true;
695 return label;
698 public bool IsDefined {
699 get {
700 return defined;
704 public bool HasBeenReferenced {
705 get {
706 return referenced;
710 public void AddUsageVector (FlowBranching.UsageVector vector)
712 vector = vector.Clone ();
713 vector.Next = vectors;
714 vectors = vector;
717 public override bool Resolve (EmitContext ec)
719 ec.CurrentBranching.Label (vectors);
721 referenced = true;
723 return true;
726 protected override void DoEmit (EmitContext ec)
728 if (ig != null && ig != ec.ig) {
729 Report.Error (1632, "Control cannot leave body of anonymous method");
730 return;
732 LabelTarget (ec);
733 ec.ig.MarkLabel (label);
738 /// <summary>
739 /// `goto default' statement
740 /// </summary>
741 public class GotoDefault : Statement {
743 public GotoDefault (Location l)
745 loc = l;
748 public override bool Resolve (EmitContext ec)
750 ec.CurrentBranching.CurrentUsageVector.Goto ();
751 return true;
754 protected override void DoEmit (EmitContext ec)
756 if (ec.Switch == null){
757 Report.Error (153, loc, "goto default is only valid in a switch statement");
758 return;
761 if (!ec.Switch.GotDefault){
762 Report.Error (159, loc, "No default target on switch statement");
763 return;
765 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
769 /// <summary>
770 /// `goto case' statement
771 /// </summary>
772 public class GotoCase : Statement {
773 Expression expr;
774 SwitchLabel sl;
776 public GotoCase (Expression e, Location l)
778 expr = e;
779 loc = l;
782 public override bool Resolve (EmitContext ec)
784 if (ec.Switch == null){
785 Report.Error (153, loc, "goto case is only valid in a switch statement");
786 return false;
789 expr = expr.Resolve (ec);
790 if (expr == null)
791 return false;
793 if (!(expr is Constant)){
794 Report.Error (159, loc, "Target expression for goto case is not constant");
795 return false;
798 object val = Expression.ConvertIntLiteral (
799 (Constant) expr, ec.Switch.SwitchType, loc);
801 if (val == null)
802 return false;
804 sl = (SwitchLabel) ec.Switch.Elements [val];
806 if (sl == null){
807 Report.Error (
808 159, loc,
809 "No such label 'case " + val + "': for the goto case");
810 return false;
813 ec.CurrentBranching.CurrentUsageVector.Goto ();
814 return true;
817 protected override void DoEmit (EmitContext ec)
819 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
823 public class Throw : Statement {
824 Expression expr;
826 public Throw (Expression expr, Location l)
828 this.expr = expr;
829 loc = l;
832 public override bool Resolve (EmitContext ec)
834 ec.CurrentBranching.CurrentUsageVector.Throw ();
836 if (expr != null){
837 expr = expr.Resolve (ec);
838 if (expr == null)
839 return false;
841 ExprClass eclass = expr.eclass;
843 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
844 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
845 expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
846 return false;
849 Type t = expr.Type;
851 if ((t != TypeManager.exception_type) &&
852 !t.IsSubclassOf (TypeManager.exception_type) &&
853 !(expr is NullLiteral)) {
854 Error (155,
855 "The type caught or thrown must be derived " +
856 "from System.Exception");
857 return false;
859 return true;
862 if (ec.CurrentBranching.InFinally (true)) {
863 Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
864 return false;
867 if (!ec.CurrentBranching.InCatch ()) {
868 Error (156, "A throw statement with no argument is only allowed in a catch clause");
869 return false;
871 return true;
874 protected override void DoEmit (EmitContext ec)
876 if (expr == null)
877 ec.ig.Emit (OpCodes.Rethrow);
878 else {
879 expr.Emit (ec);
881 ec.ig.Emit (OpCodes.Throw);
886 public class Break : Statement {
888 public Break (Location l)
890 loc = l;
893 bool crossing_exc;
895 public override bool Resolve (EmitContext ec)
897 if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
898 Error (139, "No enclosing loop or switch to continue to");
899 return false;
900 } else if (ec.CurrentBranching.InFinally (false)) {
901 Error (157, "Control can not leave the body of the finally block");
902 return false;
903 } else if (ec.CurrentBranching.InTryOrCatch (false))
904 ec.CurrentBranching.AddFinallyVector (
905 ec.CurrentBranching.CurrentUsageVector);
906 else if (ec.CurrentBranching.InLoop ())
907 ec.CurrentBranching.AddBreakVector (
908 ec.CurrentBranching.CurrentUsageVector);
910 crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
912 if (!crossing_exc)
913 ec.NeedReturnLabel ();
915 ec.CurrentBranching.CurrentUsageVector.Break ();
916 return true;
919 protected override void DoEmit (EmitContext ec)
921 ILGenerator ig = ec.ig;
923 if (crossing_exc)
924 ig.Emit (OpCodes.Leave, ec.LoopEnd);
925 else {
926 ig.Emit (OpCodes.Br, ec.LoopEnd);
931 public class Continue : Statement {
933 public Continue (Location l)
935 loc = l;
938 bool crossing_exc;
940 public override bool Resolve (EmitContext ec)
942 if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
943 Error (139, "No enclosing loop to continue to");
944 return false;
945 } else if (ec.CurrentBranching.InFinally (false)) {
946 Error (157, "Control can not leave the body of the finally block");
947 return false;
948 } else if (ec.CurrentBranching.InTryOrCatch (false))
949 ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector);
951 crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
953 ec.CurrentBranching.CurrentUsageVector.Goto ();
954 return true;
957 protected override void DoEmit (EmitContext ec)
959 Label begin = ec.LoopBegin;
961 if (crossing_exc)
962 ec.ig.Emit (OpCodes.Leave, begin);
963 else
964 ec.ig.Emit (OpCodes.Br, begin);
969 // The information about a user-perceived local variable
971 public class LocalInfo {
972 public Expression Type;
975 // Most of the time a variable will be stored in a LocalBuilder
977 // But sometimes, it will be stored in a field (variables that have been
978 // hoisted by iterators or by anonymous methods). The context of the field will
979 // be stored in the EmitContext
982 public LocalBuilder LocalBuilder;
983 public FieldBuilder FieldBuilder;
985 public Type VariableType;
986 public readonly string Name;
987 public readonly Location Location;
988 public readonly Block Block;
990 public VariableInfo VariableInfo;
992 enum Flags : byte {
993 Used = 1,
994 ReadOnly = 2,
995 Pinned = 4,
996 IsThis = 8,
997 Captured = 16,
998 AddressTaken = 32
1001 Flags flags;
1003 public LocalInfo (Expression type, string name, Block block, Location l)
1005 Type = type;
1006 Name = name;
1007 Block = block;
1008 Location = l;
1011 public LocalInfo (TypeContainer tc, Block block, Location l)
1013 VariableType = tc.TypeBuilder;
1014 Block = block;
1015 Location = l;
1018 public bool IsThisAssigned (EmitContext ec, Location loc)
1020 if (VariableInfo == null)
1021 throw new Exception ();
1023 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1024 return true;
1026 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1029 public bool IsAssigned (EmitContext ec)
1031 if (VariableInfo == null)
1032 throw new Exception ();
1034 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1037 public bool Resolve (EmitContext ec)
1039 if (VariableType == null) {
1040 TypeExpr texpr = Type.ResolveAsTypeTerminal (ec);
1041 if (texpr == null)
1042 return false;
1044 VariableType = texpr.Type;
1047 if (VariableType == TypeManager.void_type) {
1048 Report.Error (1547, Location,
1049 "Keyword 'void' cannot be used in this context");
1050 return false;
1053 if (VariableType.IsAbstract && VariableType.IsSealed) {
1054 Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
1055 return false;
1057 // TODO: breaks the build
1058 // if (VariableType.IsPointer && !ec.InUnsafe)
1059 // Expression.UnsafeError (Location);
1061 return true;
1065 // Whether the variable is Fixed (because its Pinned or its a value type)
1067 public bool IsFixed {
1068 get {
1069 if (((flags & Flags.Pinned) != 0) || TypeManager.IsValueType (VariableType))
1070 return true;
1072 return false;
1076 public bool IsCaptured {
1077 get {
1078 return (flags & Flags.Captured) != 0;
1081 set {
1082 flags |= Flags.Captured;
1086 public bool AddressTaken {
1087 get {
1088 return (flags & Flags.AddressTaken) != 0;
1091 set {
1092 flags |= Flags.AddressTaken;
1096 public override string ToString ()
1098 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1099 Name, Type, VariableInfo, Location);
1102 public bool Used {
1103 get {
1104 return (flags & Flags.Used) != 0;
1106 set {
1107 flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
1111 public bool ReadOnly {
1112 get {
1113 return (flags & Flags.ReadOnly) != 0;
1115 set {
1116 flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
1121 // Whether the variable is pinned, if Pinned the variable has been
1122 // allocated in a pinned slot with DeclareLocal.
1124 public bool Pinned {
1125 get {
1126 return (flags & Flags.Pinned) != 0;
1128 set {
1129 flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
1133 public bool IsThis {
1134 get {
1135 return (flags & Flags.IsThis) != 0;
1137 set {
1138 flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
1143 /// <summary>
1144 /// Block represents a C# block.
1145 /// </summary>
1147 /// <remarks>
1148 /// This class is used in a number of places: either to represent
1149 /// explicit blocks that the programmer places or implicit blocks.
1151 /// Implicit blocks are used as labels or to introduce variable
1152 /// declarations.
1154 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1155 /// they contain extra information that is not necessary on normal blocks.
1156 /// </remarks>
1157 public class Block : Statement {
1158 public Block Parent;
1159 public readonly Location StartLocation;
1160 public Location EndLocation = Location.Null;
1162 [Flags]
1163 public enum Flags {
1164 Implicit = 1,
1165 Unchecked = 2,
1166 BlockUsed = 4,
1167 VariablesInitialized = 8,
1168 HasRet = 16,
1169 IsDestructor = 32,
1170 HasVarargs = 64,
1171 IsToplevel = 128,
1172 Unsafe = 256
1174 Flags flags;
1176 public bool Implicit {
1177 get {
1178 return (flags & Flags.Implicit) != 0;
1182 public bool Unchecked {
1183 get {
1184 return (flags & Flags.Unchecked) != 0;
1186 set {
1187 flags |= Flags.Unchecked;
1191 public bool Unsafe {
1192 get {
1193 return (flags & Flags.Unsafe) != 0;
1195 set {
1196 flags |= Flags.Unsafe;
1200 public bool HasVarargs {
1201 get {
1202 if (Parent != null)
1203 return Parent.HasVarargs;
1204 else
1205 return (flags & Flags.HasVarargs) != 0;
1207 set {
1208 flags |= Flags.HasVarargs;
1213 // The statements in this block
1215 public ArrayList statements;
1216 int num_statements;
1219 // An array of Blocks. We keep track of children just
1220 // to generate the local variable declarations.
1222 // Statements and child statements are handled through the
1223 // statements.
1225 ArrayList children;
1228 // Labels. (label, block) pairs.
1230 Hashtable labels;
1233 // Keeps track of (name, type) pairs
1235 Hashtable variables;
1238 // Keeps track of constants
1239 Hashtable constants;
1242 // The parameters for the block, this is only needed on the toplevel block really
1243 // TODO: move `parameters' into ToplevelBlock
1244 Parameters parameters;
1247 // If this is a switch section, the enclosing switch block.
1249 Block switch_block;
1251 protected static int id;
1253 int this_id;
1255 public Block (Block parent)
1256 : this (parent, (Flags) 0, Location.Null, Location.Null)
1259 public Block (Block parent, Flags flags)
1260 : this (parent, flags, Location.Null, Location.Null)
1263 public Block (Block parent, Flags flags, Parameters parameters)
1264 : this (parent, flags, parameters, Location.Null, Location.Null)
1267 public Block (Block parent, Location start, Location end)
1268 : this (parent, (Flags) 0, start, end)
1271 public Block (Block parent, Parameters parameters, Location start, Location end)
1272 : this (parent, (Flags) 0, parameters, start, end)
1275 public Block (Block parent, Flags flags, Location start, Location end)
1276 : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
1279 public Block (Block parent, Flags flags, Parameters parameters,
1280 Location start, Location end)
1282 if (parent != null)
1283 parent.AddChild (this);
1285 this.Parent = parent;
1286 this.flags = flags;
1287 this.parameters = parameters;
1288 this.StartLocation = start;
1289 this.EndLocation = end;
1290 this.loc = start;
1291 this_id = id++;
1292 statements = new ArrayList ();
1294 if (parent != null && Implicit) {
1295 if (parent.child_variable_names == null)
1296 parent.child_variable_names = new Hashtable();
1297 // share with parent
1298 child_variable_names = parent.child_variable_names;
1303 public Block CreateSwitchBlock (Location start)
1305 Block new_block = new Block (this, start, start);
1306 new_block.switch_block = this;
1307 return new_block;
1310 public int ID {
1311 get {
1312 return this_id;
1316 void AddChild (Block b)
1318 if (children == null)
1319 children = new ArrayList ();
1321 children.Add (b);
1324 public void SetEndLocation (Location loc)
1326 EndLocation = loc;
1329 /// <summary>
1330 /// Adds a label to the current block.
1331 /// </summary>
1333 /// <returns>
1334 /// false if the name already exists in this block. true
1335 /// otherwise.
1336 /// </returns>
1338 public bool AddLabel (string name, LabeledStatement target, Location loc)
1340 if (switch_block != null)
1341 return switch_block.AddLabel (name, target, loc);
1343 Block cur = this;
1344 while (cur != null) {
1345 if (cur.DoLookupLabel (name) != null) {
1346 Report.Error (
1347 140, loc, "The label '{0}' is a duplicate",
1348 name);
1349 return false;
1352 if (!Implicit)
1353 break;
1355 cur = cur.Parent;
1358 while (cur != null) {
1359 if (cur.DoLookupLabel (name) != null) {
1360 Report.Error (
1361 158, loc,
1362 "The label '{0}' shadows another label " +
1363 "by the same name in a containing scope.",
1364 name);
1365 return false;
1368 if (children != null) {
1369 foreach (Block b in children) {
1370 LabeledStatement s = b.DoLookupLabel (name);
1371 if (s == null)
1372 continue;
1374 Report.Error (
1375 158, s.Location,
1376 "The label '{0}' shadows another " +
1377 "label by the same name in a " +
1378 "containing scope.",
1379 name);
1380 return false;
1385 cur = cur.Parent;
1388 if (labels == null)
1389 labels = new Hashtable ();
1391 labels.Add (name, target);
1392 return true;
1395 public LabeledStatement LookupLabel (string name)
1397 LabeledStatement s = DoLookupLabel (name);
1398 if (s != null)
1399 return s;
1401 if (children == null)
1402 return null;
1404 foreach (Block child in children) {
1405 if (!child.Implicit)
1406 continue;
1408 s = child.LookupLabel (name);
1409 if (s != null)
1410 return s;
1413 return null;
1416 LabeledStatement DoLookupLabel (string name)
1418 if (switch_block != null)
1419 return switch_block.LookupLabel (name);
1421 if (labels != null)
1422 if (labels.Contains (name))
1423 return ((LabeledStatement) labels [name]);
1425 return null;
1428 LocalInfo this_variable = null;
1430 // <summary>
1431 // Returns the "this" instance variable of this block.
1432 // See AddThisVariable() for more information.
1433 // </summary>
1434 public LocalInfo ThisVariable {
1435 get {
1436 for (Block b = this; b != null; b = b.Parent) {
1437 if (b.this_variable != null)
1438 return b.this_variable;
1441 return null;
1445 Hashtable child_variable_names;
1447 // <summary>
1448 // Marks a variable with name @name as being used in a child block.
1449 // If a variable name has been used in a child block, it's illegal to
1450 // declare a variable with the same name in the current block.
1451 // </summary>
1452 public void AddChildVariableName (string name)
1454 if (child_variable_names == null)
1455 child_variable_names = new Hashtable ();
1457 child_variable_names [name] = null;
1460 // <summary>
1461 // Checks whether a variable name has already been used in a child block.
1462 // </summary>
1463 public bool IsVariableNameUsedInChildBlock (string name)
1465 if (child_variable_names == null)
1466 return false;
1468 return child_variable_names.Contains (name);
1471 // <summary>
1472 // This is used by non-static `struct' constructors which do not have an
1473 // initializer - in this case, the constructor must initialize all of the
1474 // struct's fields. To do this, we add a "this" variable and use the flow
1475 // analysis code to ensure that it's been fully initialized before control
1476 // leaves the constructor.
1477 // </summary>
1478 public LocalInfo AddThisVariable (TypeContainer tc, Location l)
1480 if (this_variable != null)
1481 return this_variable;
1483 if (variables == null)
1484 variables = new Hashtable ();
1486 this_variable = new LocalInfo (tc, this, l);
1487 this_variable.Used = true;
1488 this_variable.IsThis = true;
1490 variables.Add ("this", this_variable);
1492 return this_variable;
1495 public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
1497 if (variables == null)
1498 variables = new Hashtable ();
1500 LocalInfo vi = GetLocalInfo (name);
1501 if (vi != null) {
1502 if (vi.Block != this)
1503 Report.Error (136, l, "A local variable named `" + name + "' " +
1504 "cannot be declared in this scope since it would " +
1505 "give a different meaning to `" + name + "', which " +
1506 "is already used in a `parent or current' scope to " +
1507 "denote something else");
1508 else
1509 Report.Error (128, l, "A local variable `" + name + "' is already " +
1510 "defined in this scope");
1511 return null;
1514 if (IsVariableNameUsedInChildBlock (name)) {
1515 Report.Error (136, l, "A local variable named `" + name + "' " +
1516 "cannot be declared in this scope since it would " +
1517 "give a different meaning to `" + name + "', which " +
1518 "is already used in a `child' scope to denote something " +
1519 "else");
1520 return null;
1523 if (pars != null) {
1524 int idx;
1525 Parameter p = pars.GetParameterByName (name, out idx);
1526 if (p != null) {
1527 Report.Error (136, l, "A local variable named `" + name + "' " +
1528 "cannot be declared in this scope since it would " +
1529 "give a different meaning to `" + name + "', which " +
1530 "is already used in a `parent or current' scope to " +
1531 "denote something else");
1532 return null;
1536 vi = new LocalInfo (type, name, this, l);
1538 variables.Add (name, vi);
1540 // Mark 'name' as "used by a child block" in every surrounding block
1541 Block cur = this;
1542 while (cur != null && cur.Implicit)
1543 cur = cur.Parent;
1544 if (cur != null)
1545 for (Block par = cur.Parent; par != null; par = par.Parent)
1546 par.AddChildVariableName (name);
1548 if ((flags & Flags.VariablesInitialized) != 0)
1549 throw new Exception ();
1551 // Console.WriteLine ("Adding {0} to {1}", name, ID);
1552 return vi;
1555 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1557 if (AddVariable (type, name, pars, l) == null)
1558 return false;
1560 if (constants == null)
1561 constants = new Hashtable ();
1563 constants.Add (name, value);
1564 return true;
1567 public Hashtable Variables {
1568 get {
1569 return variables;
1573 public LocalInfo GetLocalInfo (string name)
1575 for (Block b = this; b != null; b = b.Parent) {
1576 if (b.variables != null) {
1577 LocalInfo ret = b.variables [name] as LocalInfo;
1578 if (ret != null)
1579 return ret;
1582 return null;
1585 public Expression GetVariableType (string name)
1587 LocalInfo vi = GetLocalInfo (name);
1589 if (vi != null)
1590 return vi.Type;
1592 return null;
1595 public Expression GetConstantExpression (string name)
1597 for (Block b = this; b != null; b = b.Parent) {
1598 if (b.constants != null) {
1599 Expression ret = b.constants [name] as Expression;
1600 if (ret != null)
1601 return ret;
1604 return null;
1607 /// <summary>
1608 /// True if the variable named @name is a constant
1609 /// </summary>
1610 public bool IsConstant (string name)
1612 Expression e = null;
1614 e = GetConstantExpression (name);
1616 return e != null;
1620 // Returns a `ParameterReference' for the given name, or null if there
1621 // is no such parameter
1623 public ParameterReference GetParameterReference (string name, Location loc)
1625 Block b = this;
1627 do {
1628 Parameters pars = b.parameters;
1630 if (pars != null){
1631 Parameter par;
1632 int idx;
1634 par = pars.GetParameterByName (name, out idx);
1635 if (par != null){
1636 ParameterReference pr;
1638 pr = new ParameterReference (pars, this, idx, name, loc);
1639 return pr;
1642 b = b.Parent;
1643 } while (b != null);
1644 return null;
1648 // Whether the parameter named `name' is local to this block,
1649 // or false, if the parameter belongs to an encompassing block.
1651 public bool IsLocalParameter (string name)
1653 Block b = this;
1654 int toplevel_count = 0;
1656 do {
1657 if (this is ToplevelBlock)
1658 toplevel_count++;
1660 Parameters pars = b.parameters;
1661 if (pars != null){
1662 if (pars.GetParameterByName (name) != null)
1663 return true;
1664 return false;
1666 if (toplevel_count > 0)
1667 return false;
1668 b = b.Parent;
1669 } while (b != null);
1670 return false;
1674 // Whether the `name' is a parameter reference
1676 public bool IsParameterReference (string name)
1678 Block b = this;
1680 do {
1681 Parameters pars = b.parameters;
1683 if (pars != null)
1684 if (pars.GetParameterByName (name) != null)
1685 return true;
1686 b = b.Parent;
1687 } while (b != null);
1688 return false;
1691 /// <returns>
1692 /// A list of labels that were not used within this block
1693 /// </returns>
1694 public string [] GetUnreferenced ()
1696 // FIXME: Implement me
1697 return null;
1700 public void AddStatement (Statement s)
1702 statements.Add (s);
1703 flags |= Flags.BlockUsed;
1706 public bool Used {
1707 get {
1708 return (flags & Flags.BlockUsed) != 0;
1712 public void Use ()
1714 flags |= Flags.BlockUsed;
1717 public bool HasRet {
1718 get {
1719 return (flags & Flags.HasRet) != 0;
1723 public bool IsDestructor {
1724 get {
1725 return (flags & Flags.IsDestructor) != 0;
1729 public void SetDestructor ()
1731 flags |= Flags.IsDestructor;
1734 VariableMap param_map, local_map;
1736 public VariableMap ParameterMap {
1737 get {
1738 if ((flags & Flags.VariablesInitialized) == 0)
1739 throw new Exception ("Variables have not been initialized yet");
1741 return param_map;
1745 public VariableMap LocalMap {
1746 get {
1747 if ((flags & Flags.VariablesInitialized) == 0)
1748 throw new Exception ("Variables have not been initialized yet");
1750 return local_map;
1754 /// <summary>
1755 /// Emits the variable declarations and labels.
1756 /// </summary>
1757 /// <remarks>
1758 /// tc: is our typecontainer (to resolve type references)
1759 /// ig: is the code generator:
1760 /// </remarks>
1761 public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
1763 bool old_unsafe = ec.InUnsafe;
1765 // If some parent block was unsafe, we remain unsafe even if this block
1766 // isn't explicitly marked as such.
1767 ec.InUnsafe |= Unsafe;
1770 // Compute the VariableMap's.
1772 // Unfortunately, we don't know the type when adding variables with
1773 // AddVariable(), so we need to compute this info here.
1776 LocalInfo[] locals;
1777 if (variables != null) {
1778 foreach (LocalInfo li in variables.Values)
1779 li.Resolve (ec);
1781 locals = new LocalInfo [variables.Count];
1782 variables.Values.CopyTo (locals, 0);
1783 } else
1784 locals = new LocalInfo [0];
1786 if (Parent != null)
1787 local_map = new VariableMap (Parent.LocalMap, locals);
1788 else
1789 local_map = new VariableMap (locals);
1791 param_map = new VariableMap (ip);
1792 flags |= Flags.VariablesInitialized;
1794 bool old_check_state = ec.ConstantCheckState;
1795 ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
1798 // Process this block variables
1800 if (variables != null){
1801 foreach (DictionaryEntry de in variables){
1802 string name = (string) de.Key;
1803 LocalInfo vi = (LocalInfo) de.Value;
1805 if (vi.VariableType == null)
1806 continue;
1808 Type variable_type = vi.VariableType;
1810 if (variable_type.IsPointer){
1812 // Am not really convinced that this test is required (Microsoft does it)
1813 // but the fact is that you would not be able to use the pointer variable
1814 // *anyways*
1816 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1817 vi.Location))
1818 continue;
1821 #if false
1822 if (remap_locals)
1823 vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
1824 else if (vi.Pinned)
1826 // This is needed to compile on both .NET 1.x and .NET 2.x
1827 // the later introduced `DeclareLocal (Type t, bool pinned)'
1829 vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
1830 else if (!vi.IsThis)
1831 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1832 #endif
1834 if (constants == null)
1835 continue;
1837 Expression cv = (Expression) constants [name];
1838 if (cv == null)
1839 continue;
1841 ec.CurrentBlock = this;
1842 Expression e = cv.Resolve (ec);
1843 if (e == null)
1844 continue;
1846 Constant ce = e as Constant;
1847 if (ce == null){
1848 Report.Error (133, vi.Location,
1849 "The expression being assigned to `" +
1850 name + "' must be constant (" + e + ")");
1851 continue;
1854 if (e.Type != variable_type){
1855 e = Const.ChangeType (vi.Location, ce, variable_type);
1856 if (e == null)
1857 continue;
1860 constants.Remove (name);
1861 constants.Add (name, e);
1864 ec.ConstantCheckState = old_check_state;
1867 // Now, handle the children
1869 if (children != null){
1870 foreach (Block b in children)
1871 b.ResolveMeta (toplevel, ec, ip);
1873 ec.InUnsafe = old_unsafe;
1877 // Emits the local variable declarations for a block
1879 public void EmitMeta (EmitContext ec)
1881 ILGenerator ig = ec.ig;
1883 if (variables != null){
1884 bool have_captured_vars = ec.HaveCapturedVariables ();
1885 bool remap_locals = ec.RemapToProxy;
1887 foreach (DictionaryEntry de in variables){
1888 LocalInfo vi = (LocalInfo) de.Value;
1890 if (have_captured_vars && ec.IsCaptured (vi))
1891 continue;
1893 if (remap_locals){
1894 vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType);
1895 } else {
1896 if (vi.Pinned)
1898 // This is needed to compile on both .NET 1.x and .NET 2.x
1899 // the later introduced `DeclareLocal (Type t, bool pinned)'
1901 vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
1902 else if (!vi.IsThis)
1903 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1908 if (children != null){
1909 foreach (Block b in children)
1910 b.EmitMeta (ec);
1914 void UsageWarning (FlowBranching.UsageVector vector)
1916 string name;
1918 if ((variables != null) && (RootContext.WarningLevel >= 3)) {
1919 foreach (DictionaryEntry de in variables){
1920 LocalInfo vi = (LocalInfo) de.Value;
1922 if (vi.Used)
1923 continue;
1925 name = (string) de.Key;
1927 if (vector.IsAssigned (vi.VariableInfo)){
1928 Report.Warning (219, vi.Location, "The variable '{0}' is assigned but its value is never used", name);
1929 } else {
1930 Report.Warning (168, vi.Location, "The variable '{0}' is declared but never used", name);
1936 bool unreachable_shown;
1938 public override bool Resolve (EmitContext ec)
1940 Block prev_block = ec.CurrentBlock;
1941 bool ok = true;
1943 int errors = Report.Errors;
1945 ec.CurrentBlock = this;
1946 ec.StartFlowBranching (this);
1948 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1950 bool unreachable = unreachable_shown;
1952 int statement_count = statements.Count;
1953 for (int ix = 0; ix < statement_count; ix++){
1954 Statement s = (Statement) statements [ix];
1956 if (unreachable && !(s is LabeledStatement)) {
1957 if (s == EmptyStatement.Value)
1958 s.loc = EndLocation;
1960 if (!s.ResolveUnreachable (ec, !unreachable_shown))
1961 ok = false;
1963 if (s != EmptyStatement.Value)
1964 unreachable_shown = true;
1965 else
1966 s.loc = Location.Null;
1968 if (ok && !(s is Block)) {
1969 statements [ix] = EmptyStatement.Value;
1970 continue;
1974 if (s.Resolve (ec) == false) {
1975 ok = false;
1976 statements [ix] = EmptyStatement.Value;
1977 continue;
1980 num_statements = ix + 1;
1982 if (s is LabeledStatement)
1983 unreachable = false;
1984 else
1985 unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
1988 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
1989 ec.CurrentBranching, statement_count, num_statements);
1992 FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
1994 ec.CurrentBlock = prev_block;
1996 // If we're a non-static `struct' constructor which doesn't have an
1997 // initializer, then we must initialize all of the struct's fields.
1998 if ((this_variable != null) &&
1999 (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) &&
2000 !this_variable.IsThisAssigned (ec, loc))
2001 ok = false;
2003 if ((labels != null) && (RootContext.WarningLevel >= 2)) {
2004 foreach (LabeledStatement label in labels.Values)
2005 if (!label.HasBeenReferenced)
2006 Report.Warning (164, label.Location,
2007 "This label has not been referenced");
2010 Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2012 if ((vector.Reachability.Returns == FlowBranching.FlowReturns.Always) ||
2013 (vector.Reachability.Throws == FlowBranching.FlowReturns.Always) ||
2014 (vector.Reachability.Reachable == FlowBranching.FlowReturns.Never))
2015 flags |= Flags.HasRet;
2017 if (ok && (errors == Report.Errors)) {
2018 if (RootContext.WarningLevel >= 3)
2019 UsageWarning (vector);
2022 return ok;
2025 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2027 unreachable_shown = true;
2029 if (warn && (RootContext.WarningLevel >= 2))
2030 Report.Warning (162, loc, "Unreachable code detected");
2032 if (Implicit)
2033 return Resolve (ec);
2035 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2036 bool ok = Resolve (ec);
2037 ec.KillFlowBranching ();
2039 return ok;
2042 protected override void DoEmit (EmitContext ec)
2044 for (int ix = 0; ix < num_statements; ix++){
2045 Statement s = (Statement) statements [ix];
2047 // Check whether we are the last statement in a
2048 // top-level block.
2050 if (((Parent == null) || Implicit) && (ix+1 == num_statements) && !(s is Block))
2051 ec.IsLastStatement = true;
2052 else
2053 ec.IsLastStatement = false;
2055 s.Emit (ec);
2059 public override void Emit (EmitContext ec)
2061 Block prev_block = ec.CurrentBlock;
2063 ec.CurrentBlock = this;
2065 bool emit_debug_info = (CodeGen.SymbolWriter != null);
2066 bool is_lexical_block = !Implicit && (Parent != null);
2068 if (emit_debug_info) {
2069 if (is_lexical_block)
2070 ec.ig.BeginScope ();
2072 if (variables != null) {
2073 foreach (DictionaryEntry de in variables) {
2074 string name = (string) de.Key;
2075 LocalInfo vi = (LocalInfo) de.Value;
2077 if (vi.LocalBuilder == null)
2078 continue;
2080 ec.DefineLocalVariable (name, vi.LocalBuilder);
2085 ec.Mark (StartLocation, true);
2086 DoEmit (ec);
2087 ec.Mark (EndLocation, true);
2089 if (emit_debug_info && is_lexical_block)
2090 ec.ig.EndScope ();
2092 ec.CurrentBlock = prev_block;
2095 public ToplevelBlock Toplevel {
2096 get {
2097 Block b = this;
2098 while (b.Parent != null){
2099 if ((b.flags & Flags.IsToplevel) != 0)
2100 break;
2101 b = b.Parent;
2104 return (ToplevelBlock) b;
2109 // Returns true if we ar ea child of `b'.
2111 public bool IsChildOf (Block b)
2113 Block current = this;
2115 do {
2116 if (current.Parent == b)
2117 return true;
2118 current = current.Parent;
2119 } while (current != null);
2120 return false;
2125 // A toplevel block contains extra information, the split is done
2126 // only to separate information that would otherwise bloat the more
2127 // lightweight Block.
2129 // In particular, this was introduced when the support for Anonymous
2130 // Methods was implemented.
2132 public class ToplevelBlock : Block {
2134 // Pointer to the host of this anonymous method, or null
2135 // if we are the topmost block
2137 public ToplevelBlock Container;
2138 CaptureContext capture_context;
2140 Hashtable capture_contexts;
2142 static int did = 0;
2145 public void RegisterCaptureContext (CaptureContext cc)
2147 if (capture_contexts == null)
2148 capture_contexts = new Hashtable ();
2149 capture_contexts [cc] = cc;
2152 public void CompleteContexts ()
2154 if (capture_contexts == null)
2155 return;
2157 foreach (CaptureContext cc in capture_contexts.Keys){
2158 cc.AdjustScopes ();
2162 public CaptureContext ToplevelBlockCaptureContext {
2163 get {
2164 return capture_context;
2169 // Parent is only used by anonymous blocks to link back to their
2170 // parents
2172 public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
2173 base (null, Flags.IsToplevel, parameters, start, Location.Null)
2175 Container = container;
2178 public ToplevelBlock (Parameters parameters, Location start) :
2179 base (null, Flags.IsToplevel, parameters, start, Location.Null)
2183 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2184 base (null, flags | Flags.IsToplevel, parameters, start, Location.Null)
2188 public ToplevelBlock (Location loc) : base (null, Flags.IsToplevel, loc, loc)
2192 public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
2194 if (capture_context == null)
2195 capture_context = new CaptureContext (this, loc, host);
2198 public CaptureContext CaptureContext {
2199 get {
2200 return capture_context;
2205 public class SwitchLabel {
2206 Expression label;
2207 object converted;
2208 public Location loc;
2210 Label il_label;
2211 bool il_label_set;
2212 Label il_label_code;
2213 bool il_label_code_set;
2216 // if expr == null, then it is the default case.
2218 public SwitchLabel (Expression expr, Location l)
2220 label = expr;
2221 loc = l;
2224 public Expression Label {
2225 get {
2226 return label;
2230 public object Converted {
2231 get {
2232 return converted;
2236 public Label GetILLabel (EmitContext ec)
2238 if (!il_label_set){
2239 il_label = ec.ig.DefineLabel ();
2240 il_label_set = true;
2242 return il_label;
2245 public Label GetILLabelCode (EmitContext ec)
2247 if (!il_label_code_set){
2248 il_label_code = ec.ig.DefineLabel ();
2249 il_label_code_set = true;
2251 return il_label_code;
2255 // Resolves the expression, reduces it to a literal if possible
2256 // and then converts it to the requested type.
2258 public bool ResolveAndReduce (EmitContext ec, Type required_type)
2260 if (label == null)
2261 return true;
2263 Expression e = label.Resolve (ec);
2265 if (e == null)
2266 return false;
2268 if (!(e is Constant)){
2269 Report.Error (150, loc, "A constant value is expected, got: " + e);
2270 return false;
2273 if (e is StringConstant || e is NullLiteral){
2274 if (required_type == TypeManager.string_type){
2275 converted = e;
2276 return true;
2280 converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
2281 if (converted == null)
2282 return false;
2284 return true;
2288 public class SwitchSection {
2289 // An array of SwitchLabels.
2290 public readonly ArrayList Labels;
2291 public readonly Block Block;
2293 public SwitchSection (ArrayList labels, Block block)
2295 Labels = labels;
2296 Block = block;
2300 public class Switch : Statement {
2301 public readonly ArrayList Sections;
2302 public Expression Expr;
2304 /// <summary>
2305 /// Maps constants whose type type SwitchType to their SwitchLabels.
2306 /// </summary>
2307 public Hashtable Elements;
2309 /// <summary>
2310 /// The governing switch type
2311 /// </summary>
2312 public Type SwitchType;
2315 // Computed
2317 bool got_default;
2318 Label default_target;
2319 Expression new_expr;
2320 bool is_constant;
2321 SwitchSection constant_section;
2324 // The types allowed to be implicitly cast from
2325 // on the governing type
2327 static Type [] allowed_types;
2329 public Switch (Expression e, ArrayList sects, Location l)
2331 Expr = e;
2332 Sections = sects;
2333 loc = l;
2336 public bool GotDefault {
2337 get {
2338 return got_default;
2342 public Label DefaultTarget {
2343 get {
2344 return default_target;
2349 // Determines the governing type for a switch. The returned
2350 // expression might be the expression from the switch, or an
2351 // expression that includes any potential conversions to the
2352 // integral types or to string.
2354 Expression SwitchGoverningType (EmitContext ec, Type t)
2356 if (t == TypeManager.int32_type ||
2357 t == TypeManager.uint32_type ||
2358 t == TypeManager.char_type ||
2359 t == TypeManager.byte_type ||
2360 t == TypeManager.sbyte_type ||
2361 t == TypeManager.ushort_type ||
2362 t == TypeManager.short_type ||
2363 t == TypeManager.uint64_type ||
2364 t == TypeManager.int64_type ||
2365 t == TypeManager.string_type ||
2366 t == TypeManager.bool_type ||
2367 t.IsSubclassOf (TypeManager.enum_type))
2368 return Expr;
2370 if (allowed_types == null){
2371 allowed_types = new Type [] {
2372 TypeManager.int32_type,
2373 TypeManager.uint32_type,
2374 TypeManager.sbyte_type,
2375 TypeManager.byte_type,
2376 TypeManager.short_type,
2377 TypeManager.ushort_type,
2378 TypeManager.int64_type,
2379 TypeManager.uint64_type,
2380 TypeManager.char_type,
2381 TypeManager.bool_type,
2382 TypeManager.string_type
2387 // Try to find a *user* defined implicit conversion.
2389 // If there is no implicit conversion, or if there are multiple
2390 // conversions, we have to report an error
2392 Expression converted = null;
2393 foreach (Type tt in allowed_types){
2394 Expression e;
2396 e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
2397 if (e == null)
2398 continue;
2401 // Ignore over-worked ImplicitUserConversions that do
2402 // an implicit conversion in addition to the user conversion.
2404 if (e is UserCast){
2405 UserCast ue = e as UserCast;
2407 if (ue.Source != Expr)
2408 e = null;
2411 if (converted != null){
2412 Report.ExtraInformation (
2413 loc,
2414 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
2415 TypeManager.CSharpName (Expr.Type)));
2416 return null;
2417 } else {
2418 converted = e;
2421 return converted;
2424 static string Error152 {
2425 get {
2426 return "The label '{0}:' already occurs in this switch statement";
2431 // Performs the basic sanity checks on the switch statement
2432 // (looks for duplicate keys and non-constant expressions).
2434 // It also returns a hashtable with the keys that we will later
2435 // use to compute the switch tables
2437 bool CheckSwitch (EmitContext ec)
2439 Type compare_type;
2440 bool error = false;
2441 Elements = new Hashtable ();
2443 got_default = false;
2445 if (TypeManager.IsEnumType (SwitchType)){
2446 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2447 } else
2448 compare_type = SwitchType;
2450 foreach (SwitchSection ss in Sections){
2451 foreach (SwitchLabel sl in ss.Labels){
2452 if (!sl.ResolveAndReduce (ec, SwitchType)){
2453 error = true;
2454 continue;
2457 if (sl.Label == null){
2458 if (got_default){
2459 Report.Error (152, sl.loc, Error152, "default");
2460 error = true;
2462 got_default = true;
2463 continue;
2466 object key = sl.Converted;
2468 if (key is Constant)
2469 key = ((Constant) key).GetValue ();
2471 if (key == null)
2472 key = NullLiteral.Null;
2474 string lname = null;
2475 if (compare_type == TypeManager.uint64_type){
2476 ulong v = (ulong) key;
2478 if (Elements.Contains (v))
2479 lname = v.ToString ();
2480 else
2481 Elements.Add (v, sl);
2482 } else if (compare_type == TypeManager.int64_type){
2483 long v = (long) key;
2485 if (Elements.Contains (v))
2486 lname = v.ToString ();
2487 else
2488 Elements.Add (v, sl);
2489 } else if (compare_type == TypeManager.uint32_type){
2490 uint v = (uint) key;
2492 if (Elements.Contains (v))
2493 lname = v.ToString ();
2494 else
2495 Elements.Add (v, sl);
2496 } else if (compare_type == TypeManager.char_type){
2497 char v = (char) key;
2499 if (Elements.Contains (v))
2500 lname = v.ToString ();
2501 else
2502 Elements.Add (v, sl);
2503 } else if (compare_type == TypeManager.byte_type){
2504 byte v = (byte) key;
2506 if (Elements.Contains (v))
2507 lname = v.ToString ();
2508 else
2509 Elements.Add (v, sl);
2510 } else if (compare_type == TypeManager.sbyte_type){
2511 sbyte v = (sbyte) key;
2513 if (Elements.Contains (v))
2514 lname = v.ToString ();
2515 else
2516 Elements.Add (v, sl);
2517 } else if (compare_type == TypeManager.short_type){
2518 short v = (short) key;
2520 if (Elements.Contains (v))
2521 lname = v.ToString ();
2522 else
2523 Elements.Add (v, sl);
2524 } else if (compare_type == TypeManager.ushort_type){
2525 ushort v = (ushort) key;
2527 if (Elements.Contains (v))
2528 lname = v.ToString ();
2529 else
2530 Elements.Add (v, sl);
2531 } else if (compare_type == TypeManager.string_type){
2532 if (key is NullLiteral){
2533 if (Elements.Contains (NullLiteral.Null))
2534 lname = "null";
2535 else
2536 Elements.Add (NullLiteral.Null, null);
2537 } else {
2538 string s = (string) key;
2540 if (Elements.Contains (s))
2541 lname = s;
2542 else
2543 Elements.Add (s, sl);
2545 } else if (compare_type == TypeManager.int32_type) {
2546 int v = (int) key;
2548 if (Elements.Contains (v))
2549 lname = v.ToString ();
2550 else
2551 Elements.Add (v, sl);
2552 } else if (compare_type == TypeManager.bool_type) {
2553 bool v = (bool) key;
2555 if (Elements.Contains (v))
2556 lname = v.ToString ();
2557 else
2558 Elements.Add (v, sl);
2560 else
2562 throw new Exception ("Unknown switch type!" +
2563 SwitchType + " " + compare_type);
2566 if (lname != null){
2567 Report.Error (152, sl.loc, Error152, "case " + lname);
2568 error = true;
2572 if (error)
2573 return false;
2575 return true;
2578 void EmitObjectInteger (ILGenerator ig, object k)
2580 if (k is int)
2581 IntConstant.EmitInt (ig, (int) k);
2582 else if (k is Constant) {
2583 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2585 else if (k is uint)
2586 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2587 else if (k is long)
2589 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2591 IntConstant.EmitInt (ig, (int) (long) k);
2592 ig.Emit (OpCodes.Conv_I8);
2594 else
2595 LongConstant.EmitLong (ig, (long) k);
2597 else if (k is ulong)
2599 if ((ulong) k < (1L<<32))
2601 IntConstant.EmitInt (ig, (int) (long) k);
2602 ig.Emit (OpCodes.Conv_U8);
2604 else
2606 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2609 else if (k is char)
2610 IntConstant.EmitInt (ig, (int) ((char) k));
2611 else if (k is sbyte)
2612 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2613 else if (k is byte)
2614 IntConstant.EmitInt (ig, (int) ((byte) k));
2615 else if (k is short)
2616 IntConstant.EmitInt (ig, (int) ((short) k));
2617 else if (k is ushort)
2618 IntConstant.EmitInt (ig, (int) ((ushort) k));
2619 else if (k is bool)
2620 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2621 else
2622 throw new Exception ("Unhandled case");
2625 // structure used to hold blocks of keys while calculating table switch
2626 class KeyBlock : IComparable
2628 public KeyBlock (long _nFirst)
2630 nFirst = nLast = _nFirst;
2632 public long nFirst;
2633 public long nLast;
2634 public ArrayList rgKeys = null;
2635 // how many items are in the bucket
2636 public int Size = 1;
2637 public int Length
2639 get { return (int) (nLast - nFirst + 1); }
2641 public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2643 return kbLast.nLast - kbFirst.nFirst + 1;
2645 public int CompareTo (object obj)
2647 KeyBlock kb = (KeyBlock) obj;
2648 int nLength = Length;
2649 int nLengthOther = kb.Length;
2650 if (nLengthOther == nLength)
2651 return (int) (kb.nFirst - nFirst);
2652 return nLength - nLengthOther;
2656 /// <summary>
2657 /// This method emits code for a lookup-based switch statement (non-string)
2658 /// Basically it groups the cases into blocks that are at least half full,
2659 /// and then spits out individual lookup opcodes for each block.
2660 /// It emits the longest blocks first, and short blocks are just
2661 /// handled with direct compares.
2662 /// </summary>
2663 /// <param name="ec"></param>
2664 /// <param name="val"></param>
2665 /// <returns></returns>
2666 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
2668 int cElements = Elements.Count;
2669 object [] rgKeys = new object [cElements];
2670 Elements.Keys.CopyTo (rgKeys, 0);
2671 Array.Sort (rgKeys);
2673 // initialize the block list with one element per key
2674 ArrayList rgKeyBlocks = new ArrayList ();
2675 foreach (object key in rgKeys)
2676 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
2678 KeyBlock kbCurr;
2679 // iteratively merge the blocks while they are at least half full
2680 // there's probably a really cool way to do this with a tree...
2681 while (rgKeyBlocks.Count > 1)
2683 ArrayList rgKeyBlocksNew = new ArrayList ();
2684 kbCurr = (KeyBlock) rgKeyBlocks [0];
2685 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2687 KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2688 if ((kbCurr.Size + kb.Size) * 2 >= KeyBlock.TotalLength (kbCurr, kb))
2690 // merge blocks
2691 kbCurr.nLast = kb.nLast;
2692 kbCurr.Size += kb.Size;
2694 else
2696 // start a new block
2697 rgKeyBlocksNew.Add (kbCurr);
2698 kbCurr = kb;
2701 rgKeyBlocksNew.Add (kbCurr);
2702 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2703 break;
2704 rgKeyBlocks = rgKeyBlocksNew;
2707 // initialize the key lists
2708 foreach (KeyBlock kb in rgKeyBlocks)
2709 kb.rgKeys = new ArrayList ();
2711 // fill the key lists
2712 int iBlockCurr = 0;
2713 if (rgKeyBlocks.Count > 0) {
2714 kbCurr = (KeyBlock) rgKeyBlocks [0];
2715 foreach (object key in rgKeys)
2717 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
2718 System.Convert.ToInt64 (key) > kbCurr.nLast;
2719 if (fNextBlock)
2720 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2721 kbCurr.rgKeys.Add (key);
2725 // sort the blocks so we can tackle the largest ones first
2726 rgKeyBlocks.Sort ();
2728 // okay now we can start...
2729 ILGenerator ig = ec.ig;
2730 Label lblEnd = ig.DefineLabel (); // at the end ;-)
2731 Label lblDefault = ig.DefineLabel ();
2733 Type typeKeys = null;
2734 if (rgKeys.Length > 0)
2735 typeKeys = rgKeys [0].GetType (); // used for conversions
2737 Type compare_type;
2739 if (TypeManager.IsEnumType (SwitchType))
2740 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2741 else
2742 compare_type = SwitchType;
2744 for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2746 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2747 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2748 if (kb.Length <= 2)
2750 foreach (object key in kb.rgKeys)
2752 ig.Emit (OpCodes.Ldloc, val);
2753 EmitObjectInteger (ig, key);
2754 SwitchLabel sl = (SwitchLabel) Elements [key];
2755 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
2758 else
2760 // TODO: if all the keys in the block are the same and there are
2761 // no gaps/defaults then just use a range-check.
2762 if (compare_type == TypeManager.int64_type ||
2763 compare_type == TypeManager.uint64_type)
2765 // TODO: optimize constant/I4 cases
2767 // check block range (could be > 2^31)
2768 ig.Emit (OpCodes.Ldloc, val);
2769 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2770 ig.Emit (OpCodes.Blt, lblDefault);
2771 ig.Emit (OpCodes.Ldloc, val);
2772 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
2773 ig.Emit (OpCodes.Bgt, lblDefault);
2775 // normalize range
2776 ig.Emit (OpCodes.Ldloc, val);
2777 if (kb.nFirst != 0)
2779 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2780 ig.Emit (OpCodes.Sub);
2782 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
2784 else
2786 // normalize range
2787 ig.Emit (OpCodes.Ldloc, val);
2788 int nFirst = (int) kb.nFirst;
2789 if (nFirst > 0)
2791 IntConstant.EmitInt (ig, nFirst);
2792 ig.Emit (OpCodes.Sub);
2794 else if (nFirst < 0)
2796 IntConstant.EmitInt (ig, -nFirst);
2797 ig.Emit (OpCodes.Add);
2801 // first, build the list of labels for the switch
2802 int iKey = 0;
2803 int cJumps = kb.Length;
2804 Label [] rgLabels = new Label [cJumps];
2805 for (int iJump = 0; iJump < cJumps; iJump++)
2807 object key = kb.rgKeys [iKey];
2808 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
2810 SwitchLabel sl = (SwitchLabel) Elements [key];
2811 rgLabels [iJump] = sl.GetILLabel (ec);
2812 iKey++;
2814 else
2815 rgLabels [iJump] = lblDefault;
2817 // emit the switch opcode
2818 ig.Emit (OpCodes.Switch, rgLabels);
2821 // mark the default for this block
2822 if (iBlock != 0)
2823 ig.MarkLabel (lblDefault);
2826 // TODO: find the default case and emit it here,
2827 // to prevent having to do the following jump.
2828 // make sure to mark other labels in the default section
2830 // the last default just goes to the end
2831 ig.Emit (OpCodes.Br, lblDefault);
2833 // now emit the code for the sections
2834 bool fFoundDefault = false;
2835 foreach (SwitchSection ss in Sections)
2837 foreach (SwitchLabel sl in ss.Labels)
2839 ig.MarkLabel (sl.GetILLabel (ec));
2840 ig.MarkLabel (sl.GetILLabelCode (ec));
2841 if (sl.Label == null)
2843 ig.MarkLabel (lblDefault);
2844 fFoundDefault = true;
2847 ss.Block.Emit (ec);
2848 //ig.Emit (OpCodes.Br, lblEnd);
2851 if (!fFoundDefault) {
2852 ig.MarkLabel (lblDefault);
2854 ig.MarkLabel (lblEnd);
2857 // This simple emit switch works, but does not take advantage of the
2858 // `switch' opcode.
2859 // TODO: remove non-string logic from here
2860 // TODO: binary search strings?
2862 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
2864 ILGenerator ig = ec.ig;
2865 Label end_of_switch = ig.DefineLabel ();
2866 Label next_test = ig.DefineLabel ();
2867 Label null_target = ig.DefineLabel ();
2868 bool default_found = false;
2869 bool first_test = true;
2870 bool pending_goto_end = false;
2871 bool null_found;
2872 bool default_at_end = false;
2874 ig.Emit (OpCodes.Ldloc, val);
2876 if (Elements.Contains (NullLiteral.Null)){
2877 ig.Emit (OpCodes.Brfalse, null_target);
2878 } else
2879 ig.Emit (OpCodes.Brfalse, default_target);
2881 ig.Emit (OpCodes.Ldloc, val);
2882 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
2883 ig.Emit (OpCodes.Stloc, val);
2885 int section_count = Sections.Count;
2886 for (int section = 0; section < section_count; section++){
2887 SwitchSection ss = (SwitchSection) Sections [section];
2888 Label sec_begin = ig.DefineLabel ();
2890 if (pending_goto_end)
2891 ig.Emit (OpCodes.Br, end_of_switch);
2893 int label_count = ss.Labels.Count;
2894 bool mark_default = false;
2895 null_found = false;
2896 for (int label = 0; label < label_count; label++){
2897 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
2898 ig.MarkLabel (sl.GetILLabel (ec));
2900 if (!first_test){
2901 ig.MarkLabel (next_test);
2902 next_test = ig.DefineLabel ();
2905 // If we are the default target
2907 if (sl.Label == null){
2908 if (label+1 == label_count)
2909 default_at_end = true;
2910 mark_default = true;
2911 default_found = true;
2912 } else {
2913 object lit = sl.Converted;
2915 if (lit is NullLiteral){
2916 null_found = true;
2917 if (label_count == 1)
2918 ig.Emit (OpCodes.Br, next_test);
2919 continue;
2922 StringConstant str = (StringConstant) lit;
2924 ig.Emit (OpCodes.Ldloc, val);
2925 ig.Emit (OpCodes.Ldstr, str.Value);
2926 if (label_count == 1)
2927 ig.Emit (OpCodes.Bne_Un, next_test);
2928 else {
2929 if (label+1 == label_count)
2930 ig.Emit (OpCodes.Bne_Un, next_test);
2931 else
2932 ig.Emit (OpCodes.Beq, sec_begin);
2936 if (null_found)
2937 ig.MarkLabel (null_target);
2938 ig.MarkLabel (sec_begin);
2939 foreach (SwitchLabel sl in ss.Labels)
2940 ig.MarkLabel (sl.GetILLabelCode (ec));
2942 if (mark_default)
2943 ig.MarkLabel (default_target);
2944 ss.Block.Emit (ec);
2945 pending_goto_end = !ss.Block.HasRet;
2946 first_test = false;
2948 ig.MarkLabel (next_test);
2949 if (default_found){
2950 if (!default_at_end)
2951 ig.Emit (OpCodes.Br, default_target);
2952 } else
2953 ig.MarkLabel (default_target);
2954 ig.MarkLabel (end_of_switch);
2957 SwitchSection FindSection (SwitchLabel label)
2959 foreach (SwitchSection ss in Sections){
2960 foreach (SwitchLabel sl in ss.Labels){
2961 if (label == sl)
2962 return ss;
2966 return null;
2969 bool ResolveConstantSwitch (EmitContext ec)
2971 object key = ((Constant) new_expr).GetValue ();
2972 SwitchLabel label = (SwitchLabel) Elements [key];
2974 if (label == null)
2975 return true;
2977 constant_section = FindSection (label);
2978 if (constant_section == null)
2979 return true;
2981 if (constant_section.Block.Resolve (ec) != true)
2982 return false;
2984 return true;
2987 public override bool Resolve (EmitContext ec)
2989 Expr = Expr.Resolve (ec);
2990 if (Expr == null)
2991 return false;
2993 new_expr = SwitchGoverningType (ec, Expr.Type);
2994 if (new_expr == null){
2995 Report.Error (151, loc, "An integer type or string was expected for switch");
2996 return false;
2999 // Validate switch.
3000 SwitchType = new_expr.Type;
3002 if (!CheckSwitch (ec))
3003 return false;
3005 Switch old_switch = ec.Switch;
3006 ec.Switch = this;
3007 ec.Switch.SwitchType = SwitchType;
3009 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3010 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3012 is_constant = new_expr is Constant;
3013 if (is_constant) {
3014 object key = ((Constant) new_expr).GetValue ();
3015 SwitchLabel label = (SwitchLabel) Elements [key];
3017 constant_section = FindSection (label);
3020 bool first = true;
3021 foreach (SwitchSection ss in Sections){
3022 if (!first)
3023 ec.CurrentBranching.CreateSibling (
3024 null, FlowBranching.SiblingType.SwitchSection);
3025 else
3026 first = false;
3028 if (is_constant && (ss != constant_section)) {
3029 // If we're a constant switch, we're only emitting
3030 // one single section - mark all the others as
3031 // unreachable.
3032 ec.CurrentBranching.CurrentUsageVector.Goto ();
3033 if (!ss.Block.ResolveUnreachable (ec, true))
3034 return false;
3035 } else {
3036 if (!ss.Block.Resolve (ec))
3037 return false;
3041 if (!got_default)
3042 ec.CurrentBranching.CreateSibling (
3043 null, FlowBranching.SiblingType.SwitchSection);
3045 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3046 ec.Switch = old_switch;
3048 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
3049 reachability);
3051 return true;
3054 protected override void DoEmit (EmitContext ec)
3056 ILGenerator ig = ec.ig;
3058 // Store variable for comparission purposes
3059 LocalBuilder value;
3060 if (!is_constant) {
3061 value = ig.DeclareLocal (SwitchType);
3062 new_expr.Emit (ec);
3063 ig.Emit (OpCodes.Stloc, value);
3064 } else
3065 value = null;
3067 default_target = ig.DefineLabel ();
3070 // Setup the codegen context
3072 Label old_end = ec.LoopEnd;
3073 Switch old_switch = ec.Switch;
3075 ec.LoopEnd = ig.DefineLabel ();
3076 ec.Switch = this;
3078 // Emit Code.
3079 if (is_constant) {
3080 if (constant_section != null)
3081 constant_section.Block.Emit (ec);
3082 } else if (SwitchType == TypeManager.string_type)
3083 SimpleSwitchEmit (ec, value);
3084 else
3085 TableSwitchEmit (ec, value);
3087 // Restore context state.
3088 ig.MarkLabel (ec.LoopEnd);
3091 // Restore the previous context
3093 ec.LoopEnd = old_end;
3094 ec.Switch = old_switch;
3098 public abstract class ExceptionStatement : Statement
3100 public abstract void EmitFinally (EmitContext ec);
3102 protected bool emit_finally = true;
3103 ArrayList parent_vectors;
3105 protected void DoEmitFinally (EmitContext ec)
3107 if (emit_finally)
3108 ec.ig.BeginFinallyBlock ();
3109 else
3110 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3111 EmitFinally (ec);
3114 protected void ResolveFinally (FlowBranchingException branching)
3116 emit_finally = branching.EmitFinally;
3117 if (!emit_finally)
3118 branching.Parent.StealFinallyClauses (ref parent_vectors);
3122 public class Lock : ExceptionStatement {
3123 Expression expr;
3124 Statement Statement;
3125 LocalBuilder temp;
3127 public Lock (Expression expr, Statement stmt, Location l)
3129 this.expr = expr;
3130 Statement = stmt;
3131 loc = l;
3134 public override bool Resolve (EmitContext ec)
3136 expr = expr.Resolve (ec);
3137 if (expr == null)
3138 return false;
3140 if (expr.Type.IsValueType){
3141 Error (185, "lock statement requires the expression to be " +
3142 " a reference type (type is: `{0}'",
3143 TypeManager.CSharpName (expr.Type));
3144 return false;
3147 FlowBranchingException branching = ec.StartFlowBranching (this);
3148 bool ok = Statement.Resolve (ec);
3149 if (!ok) {
3150 ec.KillFlowBranching ();
3151 return false;
3154 ResolveFinally (branching);
3156 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3157 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3158 // Unfortunately, System.Reflection.Emit automatically emits
3159 // a leave to the end of the finally block.
3160 // This is a problem if `returns' is true since we may jump
3161 // to a point after the end of the method.
3162 // As a workaround, emit an explicit ret here.
3163 ec.NeedReturnLabel ();
3166 return true;
3169 protected override void DoEmit (EmitContext ec)
3171 Type type = expr.Type;
3173 ILGenerator ig = ec.ig;
3174 temp = ig.DeclareLocal (type);
3176 expr.Emit (ec);
3177 ig.Emit (OpCodes.Dup);
3178 ig.Emit (OpCodes.Stloc, temp);
3179 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3181 // try
3182 if (emit_finally)
3183 ig.BeginExceptionBlock ();
3184 Statement.Emit (ec);
3186 // finally
3187 DoEmitFinally (ec);
3188 if (emit_finally)
3189 ig.EndExceptionBlock ();
3192 public override void EmitFinally (EmitContext ec)
3194 ILGenerator ig = ec.ig;
3195 ig.Emit (OpCodes.Ldloc, temp);
3196 ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3200 public class Unchecked : Statement {
3201 public readonly Block Block;
3203 public Unchecked (Block b)
3205 Block = b;
3206 b.Unchecked = true;
3209 public override bool Resolve (EmitContext ec)
3211 bool previous_state = ec.CheckState;
3212 bool previous_state_const = ec.ConstantCheckState;
3214 ec.CheckState = false;
3215 ec.ConstantCheckState = false;
3216 bool ret = Block.Resolve (ec);
3217 ec.CheckState = previous_state;
3218 ec.ConstantCheckState = previous_state_const;
3220 return ret;
3223 protected override void DoEmit (EmitContext ec)
3225 bool previous_state = ec.CheckState;
3226 bool previous_state_const = ec.ConstantCheckState;
3228 ec.CheckState = false;
3229 ec.ConstantCheckState = false;
3230 Block.Emit (ec);
3231 ec.CheckState = previous_state;
3232 ec.ConstantCheckState = previous_state_const;
3236 public class Checked : Statement {
3237 public readonly Block Block;
3239 public Checked (Block b)
3241 Block = b;
3242 b.Unchecked = false;
3245 public override bool Resolve (EmitContext ec)
3247 bool previous_state = ec.CheckState;
3248 bool previous_state_const = ec.ConstantCheckState;
3250 ec.CheckState = true;
3251 ec.ConstantCheckState = true;
3252 bool ret = Block.Resolve (ec);
3253 ec.CheckState = previous_state;
3254 ec.ConstantCheckState = previous_state_const;
3256 return ret;
3259 protected override void DoEmit (EmitContext ec)
3261 bool previous_state = ec.CheckState;
3262 bool previous_state_const = ec.ConstantCheckState;
3264 ec.CheckState = true;
3265 ec.ConstantCheckState = true;
3266 Block.Emit (ec);
3267 ec.CheckState = previous_state;
3268 ec.ConstantCheckState = previous_state_const;
3272 public class Unsafe : Statement {
3273 public readonly Block Block;
3275 public Unsafe (Block b)
3277 Block = b;
3278 Block.Unsafe = true;
3281 public override bool Resolve (EmitContext ec)
3283 bool previous_state = ec.InUnsafe;
3284 bool val;
3286 ec.InUnsafe = true;
3287 val = Block.Resolve (ec);
3288 ec.InUnsafe = previous_state;
3290 return val;
3293 protected override void DoEmit (EmitContext ec)
3295 bool previous_state = ec.InUnsafe;
3297 ec.InUnsafe = true;
3298 Block.Emit (ec);
3299 ec.InUnsafe = previous_state;
3304 // Fixed statement
3306 public class Fixed : Statement {
3307 Expression type;
3308 ArrayList declarators;
3309 Statement statement;
3310 Type expr_type;
3311 FixedData[] data;
3312 bool has_ret;
3314 struct FixedData {
3315 public bool is_object;
3316 public LocalInfo vi;
3317 public Expression expr;
3318 public Expression converted;
3321 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3323 this.type = type;
3324 declarators = decls;
3325 statement = stmt;
3326 loc = l;
3329 public override bool Resolve (EmitContext ec)
3331 if (!ec.InUnsafe){
3332 Expression.UnsafeError (loc);
3333 return false;
3336 TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
3337 if (texpr == null)
3338 return false;
3340 expr_type = texpr.Type;
3342 CheckObsolete (expr_type);
3344 if (ec.RemapToProxy){
3345 Report.Error (-210, loc, "Fixed statement not allowed in iterators");
3346 return false;
3349 data = new FixedData [declarators.Count];
3351 if (!expr_type.IsPointer){
3352 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
3353 return false;
3356 int i = 0;
3357 foreach (Pair p in declarators){
3358 LocalInfo vi = (LocalInfo) p.First;
3359 Expression e = (Expression) p.Second;
3361 vi.VariableInfo.SetAssigned (ec);
3362 vi.ReadOnly = true;
3365 // The rules for the possible declarators are pretty wise,
3366 // but the production on the grammar is more concise.
3368 // So we have to enforce these rules here.
3370 // We do not resolve before doing the case 1 test,
3371 // because the grammar is explicit in that the token &
3372 // is present, so we need to test for this particular case.
3375 if (e is Cast){
3376 Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement");
3377 return false;
3381 // Case 1: & object.
3383 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3384 Expression child = ((Unary) e).Expr;
3386 if (child is ParameterReference || child is LocalVariableReference){
3387 Report.Error (
3388 213, loc,
3389 "No need to use fixed statement for parameters or " +
3390 "local variable declarations (address is already " +
3391 "fixed)");
3392 return false;
3395 ec.InFixedInitializer = true;
3396 e = e.Resolve (ec);
3397 ec.InFixedInitializer = false;
3398 if (e == null)
3399 return false;
3401 child = ((Unary) e).Expr;
3403 if (!TypeManager.VerifyUnManaged (child.Type, loc))
3404 return false;
3406 data [i].is_object = true;
3407 data [i].expr = e;
3408 data [i].converted = null;
3409 data [i].vi = vi;
3410 i++;
3412 continue;
3415 ec.InFixedInitializer = true;
3416 e = e.Resolve (ec);
3417 ec.InFixedInitializer = false;
3418 if (e == null)
3419 return false;
3422 // Case 2: Array
3424 if (e.Type.IsArray){
3425 Type array_type = TypeManager.GetElementType (e.Type);
3428 // Provided that array_type is unmanaged,
3430 if (!TypeManager.VerifyUnManaged (array_type, loc))
3431 return false;
3434 // and T* is implicitly convertible to the
3435 // pointer type given in the fixed statement.
3437 ArrayPtr array_ptr = new ArrayPtr (e, loc);
3439 Expression converted = Convert.ImplicitConversionRequired (
3440 ec, array_ptr, vi.VariableType, loc);
3441 if (converted == null)
3442 return false;
3444 data [i].is_object = false;
3445 data [i].expr = e;
3446 data [i].converted = converted;
3447 data [i].vi = vi;
3448 i++;
3450 continue;
3454 // Case 3: string
3456 if (e.Type == TypeManager.string_type){
3457 data [i].is_object = false;
3458 data [i].expr = e;
3459 data [i].converted = null;
3460 data [i].vi = vi;
3461 i++;
3462 continue;
3466 // For other cases, flag a `this is already fixed expression'
3468 if (e is LocalVariableReference || e is ParameterReference ||
3469 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
3471 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
3472 return false;
3475 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
3476 return false;
3479 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
3481 if (!statement.Resolve (ec)) {
3482 ec.KillFlowBranching ();
3483 return false;
3486 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3487 has_ret = reachability.IsUnreachable;
3489 return true;
3492 protected override void DoEmit (EmitContext ec)
3494 ILGenerator ig = ec.ig;
3496 LocalBuilder [] clear_list = new LocalBuilder [data.Length];
3498 for (int i = 0; i < data.Length; i++) {
3499 LocalInfo vi = data [i].vi;
3502 // Case 1: & object.
3504 if (data [i].is_object) {
3506 // Store pointer in pinned location
3508 data [i].expr.Emit (ec);
3509 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3510 clear_list [i] = vi.LocalBuilder;
3511 continue;
3515 // Case 2: Array
3517 if (data [i].expr.Type.IsArray){
3519 // Store pointer in pinned location
3521 data [i].converted.Emit (ec);
3523 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3524 clear_list [i] = vi.LocalBuilder;
3525 continue;
3529 // Case 3: string
3531 if (data [i].expr.Type == TypeManager.string_type){
3532 LocalBuilder pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3533 clear_list [i] = pinned_string;
3535 data [i].expr.Emit (ec);
3536 ig.Emit (OpCodes.Stloc, pinned_string);
3538 Expression sptr = new StringPtr (pinned_string, loc);
3539 Expression converted = Convert.ImplicitConversionRequired (
3540 ec, sptr, vi.VariableType, loc);
3542 if (converted == null)
3543 continue;
3545 converted.Emit (ec);
3546 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3550 statement.Emit (ec);
3552 if (has_ret)
3553 return;
3556 // Clear the pinned variable
3558 for (int i = 0; i < data.Length; i++) {
3559 if (data [i].is_object || data [i].expr.Type.IsArray) {
3560 ig.Emit (OpCodes.Ldc_I4_0);
3561 ig.Emit (OpCodes.Conv_U);
3562 ig.Emit (OpCodes.Stloc, clear_list [i]);
3563 } else if (data [i].expr.Type == TypeManager.string_type){
3564 ig.Emit (OpCodes.Ldnull);
3565 ig.Emit (OpCodes.Stloc, clear_list [i]);
3571 public class Catch: Statement {
3572 public readonly string Name;
3573 public readonly Block Block;
3575 Expression type_expr;
3576 Type type;
3578 public Catch (Expression type, string name, Block block, Location l)
3580 type_expr = type;
3581 Name = name;
3582 Block = block;
3583 loc = l;
3586 public Type CatchType {
3587 get {
3588 return type;
3592 public bool IsGeneral {
3593 get {
3594 return type_expr == null;
3598 protected override void DoEmit(EmitContext ec)
3602 public override bool Resolve (EmitContext ec)
3604 if (type_expr != null) {
3605 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec);
3606 if (te == null)
3607 return false;
3609 type = te.Type;
3611 CheckObsolete (type);
3613 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3614 Error (155, "The type caught or thrown must be derived from System.Exception");
3615 return false;
3617 } else
3618 type = null;
3620 return Block.Resolve (ec);
3624 public class Try : ExceptionStatement {
3625 public readonly Block Fini, Block;
3626 public readonly ArrayList Specific;
3627 public readonly Catch General;
3629 bool need_exc_block;
3632 // specific, general and fini might all be null.
3634 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3636 if (specific == null && general == null){
3637 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3640 this.Block = block;
3641 this.Specific = specific;
3642 this.General = general;
3643 this.Fini = fini;
3644 loc = l;
3647 public override bool Resolve (EmitContext ec)
3649 bool ok = true;
3651 FlowBranchingException branching = ec.StartFlowBranching (this);
3653 Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3655 if (!Block.Resolve (ec))
3656 ok = false;
3658 FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3660 Report.Debug (1, "START OF CATCH BLOCKS", vector);
3662 Type[] prevCatches = new Type [Specific.Count];
3663 int last_index = 0;
3664 foreach (Catch c in Specific){
3665 ec.CurrentBranching.CreateSibling (
3666 c.Block, FlowBranching.SiblingType.Catch);
3668 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3670 if (c.Name != null) {
3671 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
3672 if (vi == null)
3673 throw new Exception ();
3675 vi.VariableInfo = null;
3678 if (!c.Resolve (ec))
3679 return false;
3681 Type resolvedType = c.CatchType;
3682 for (int ii = 0; ii < last_index; ++ii) {
3683 if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
3684 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName);
3685 return false;
3689 prevCatches [last_index++] = resolvedType;
3690 need_exc_block = true;
3693 Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
3695 if (General != null){
3696 ec.CurrentBranching.CreateSibling (
3697 General.Block, FlowBranching.SiblingType.Catch);
3699 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3701 if (!General.Resolve (ec))
3702 ok = false;
3704 need_exc_block = true;
3707 Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
3709 if (Fini != null) {
3710 if (ok)
3711 ec.CurrentBranching.CreateSibling (
3712 Fini, FlowBranching.SiblingType.Finally);
3714 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3716 if (!Fini.Resolve (ec))
3717 ok = false;
3720 ResolveFinally (branching);
3721 need_exc_block |= emit_finally;
3723 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3725 FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3727 Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
3729 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3730 // Unfortunately, System.Reflection.Emit automatically emits
3731 // a leave to the end of the finally block. This is a problem
3732 // if `returns' is true since we may jump to a point after the
3733 // end of the method.
3734 // As a workaround, emit an explicit ret here.
3735 ec.NeedReturnLabel ();
3738 return ok;
3741 protected override void DoEmit (EmitContext ec)
3743 ILGenerator ig = ec.ig;
3745 if (need_exc_block)
3746 ig.BeginExceptionBlock ();
3747 Block.Emit (ec);
3749 foreach (Catch c in Specific){
3750 LocalInfo vi;
3752 ig.BeginCatchBlock (c.CatchType);
3754 if (c.Name != null){
3755 vi = c.Block.GetLocalInfo (c.Name);
3756 if (vi == null)
3757 throw new Exception ("Variable does not exist in this block");
3759 ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3760 } else
3761 ig.Emit (OpCodes.Pop);
3763 c.Block.Emit (ec);
3766 if (General != null){
3767 ig.BeginCatchBlock (TypeManager.object_type);
3768 ig.Emit (OpCodes.Pop);
3769 General.Block.Emit (ec);
3772 DoEmitFinally (ec);
3773 if (need_exc_block)
3774 ig.EndExceptionBlock ();
3777 public override void EmitFinally (EmitContext ec)
3779 if (Fini != null){
3780 Fini.Emit (ec);
3785 public class Using : ExceptionStatement {
3786 object expression_or_block;
3787 Statement Statement;
3788 ArrayList var_list;
3789 Expression expr;
3790 Type expr_type;
3791 Expression conv;
3792 Expression [] resolved_vars;
3793 Expression [] converted_vars;
3794 ExpressionStatement [] assign;
3795 LocalBuilder local_copy;
3797 public Using (object expression_or_block, Statement stmt, Location l)
3799 this.expression_or_block = expression_or_block;
3800 Statement = stmt;
3801 loc = l;
3805 // Resolves for the case of using using a local variable declaration.
3807 bool ResolveLocalVariableDecls (EmitContext ec)
3809 int i = 0;
3811 TypeExpr texpr = expr.ResolveAsTypeTerminal (ec);
3812 if (texpr == null)
3813 return false;
3815 expr_type = texpr.Type;
3818 // The type must be an IDisposable or an implicit conversion
3819 // must exist.
3821 converted_vars = new Expression [var_list.Count];
3822 resolved_vars = new Expression [var_list.Count];
3823 assign = new ExpressionStatement [var_list.Count];
3825 bool need_conv = !TypeManager.ImplementsInterface (
3826 expr_type, TypeManager.idisposable_type);
3828 foreach (DictionaryEntry e in var_list){
3829 Expression var = (Expression) e.Key;
3831 var = var.ResolveLValue (ec, new EmptyExpression ());
3832 if (var == null)
3833 return false;
3835 resolved_vars [i] = var;
3837 if (!need_conv) {
3838 i++;
3839 continue;
3842 converted_vars [i] = Convert.ImplicitConversionRequired (
3843 ec, var, TypeManager.idisposable_type, loc);
3845 if (converted_vars [i] == null)
3846 return false;
3848 i++;
3851 i = 0;
3852 foreach (DictionaryEntry e in var_list){
3853 Expression var = resolved_vars [i];
3854 Expression new_expr = (Expression) e.Value;
3855 Expression a;
3857 a = new Assign (var, new_expr, loc);
3858 a = a.Resolve (ec);
3859 if (a == null)
3860 return false;
3862 if (!need_conv)
3863 converted_vars [i] = var;
3864 assign [i] = (ExpressionStatement) a;
3865 i++;
3868 return true;
3871 bool ResolveExpression (EmitContext ec)
3873 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3874 conv = Convert.ImplicitConversionRequired (
3875 ec, expr, TypeManager.idisposable_type, loc);
3877 if (conv == null)
3878 return false;
3881 return true;
3885 // Emits the code for the case of using using a local variable declaration.
3887 void EmitLocalVariableDecls (EmitContext ec)
3889 ILGenerator ig = ec.ig;
3890 int i = 0;
3892 for (i = 0; i < assign.Length; i++) {
3893 assign [i].EmitStatement (ec);
3895 if (emit_finally)
3896 ig.BeginExceptionBlock ();
3898 Statement.Emit (ec);
3900 var_list.Reverse ();
3902 DoEmitFinally (ec);
3905 void EmitLocalVariableDeclFinally (EmitContext ec)
3907 ILGenerator ig = ec.ig;
3909 int i = assign.Length;
3910 for (int ii = 0; ii < var_list.Count; ++ii){
3911 Expression var = resolved_vars [--i];
3912 Label skip = ig.DefineLabel ();
3914 ig.BeginFinallyBlock ();
3916 if (!var.Type.IsValueType) {
3917 var.Emit (ec);
3918 ig.Emit (OpCodes.Brfalse, skip);
3919 converted_vars [i].Emit (ec);
3920 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3921 } else {
3922 Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
3924 if (!(ml is MethodGroupExpr)) {
3925 var.Emit (ec);
3926 ig.Emit (OpCodes.Box, var.Type);
3927 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3928 } else {
3929 MethodInfo mi = null;
3931 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
3932 if (TypeManager.GetArgumentTypes (mk).Length == 0) {
3933 mi = mk;
3934 break;
3938 if (mi == null) {
3939 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
3940 return;
3943 IMemoryLocation mloc = (IMemoryLocation) var;
3945 mloc.AddressOf (ec, AddressOp.Load);
3946 ig.Emit (OpCodes.Call, mi);
3950 ig.MarkLabel (skip);
3952 if (emit_finally) {
3953 ig.EndExceptionBlock ();
3954 if (i > 0)
3955 ig.BeginFinallyBlock ();
3960 void EmitExpression (EmitContext ec)
3963 // Make a copy of the expression and operate on that.
3965 ILGenerator ig = ec.ig;
3966 local_copy = ig.DeclareLocal (expr_type);
3967 if (conv != null)
3968 conv.Emit (ec);
3969 else
3970 expr.Emit (ec);
3971 ig.Emit (OpCodes.Stloc, local_copy);
3973 if (emit_finally)
3974 ig.BeginExceptionBlock ();
3976 Statement.Emit (ec);
3978 DoEmitFinally (ec);
3979 if (emit_finally)
3980 ig.EndExceptionBlock ();
3983 void EmitExpressionFinally (EmitContext ec)
3985 ILGenerator ig = ec.ig;
3986 if (!local_copy.LocalType.IsValueType) {
3987 Label skip = ig.DefineLabel ();
3988 ig.Emit (OpCodes.Ldloc, local_copy);
3989 ig.Emit (OpCodes.Brfalse, skip);
3990 ig.Emit (OpCodes.Ldloc, local_copy);
3991 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3992 ig.MarkLabel (skip);
3993 } else {
3994 Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, local_copy.LocalType, "Dispose", Mono.CSharp.Location.Null);
3996 if (!(ml is MethodGroupExpr)) {
3997 ig.Emit (OpCodes.Ldloc, local_copy);
3998 ig.Emit (OpCodes.Box, local_copy.LocalType);
3999 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4000 } else {
4001 MethodInfo mi = null;
4003 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4004 if (TypeManager.GetArgumentTypes (mk).Length == 0) {
4005 mi = mk;
4006 break;
4010 if (mi == null) {
4011 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4012 return;
4015 ig.Emit (OpCodes.Ldloca, local_copy);
4016 ig.Emit (OpCodes.Call, mi);
4021 public override bool Resolve (EmitContext ec)
4023 if (expression_or_block is DictionaryEntry){
4024 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4025 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4027 if (!ResolveLocalVariableDecls (ec))
4028 return false;
4030 } else if (expression_or_block is Expression){
4031 expr = (Expression) expression_or_block;
4033 expr = expr.Resolve (ec);
4034 if (expr == null)
4035 return false;
4037 expr_type = expr.Type;
4039 if (!ResolveExpression (ec))
4040 return false;
4043 FlowBranchingException branching = ec.StartFlowBranching (this);
4045 bool ok = Statement.Resolve (ec);
4047 if (!ok) {
4048 ec.KillFlowBranching ();
4049 return false;
4052 ResolveFinally (branching);
4053 FlowBranching.Reachability reachability = ec.EndFlowBranching ();
4055 if (reachability.Returns != FlowBranching.FlowReturns.Always) {
4056 // Unfortunately, System.Reflection.Emit automatically emits a leave
4057 // to the end of the finally block. This is a problem if `returns'
4058 // is true since we may jump to a point after the end of the method.
4059 // As a workaround, emit an explicit ret here.
4060 ec.NeedReturnLabel ();
4063 return true;
4066 protected override void DoEmit (EmitContext ec)
4068 if (expression_or_block is DictionaryEntry)
4069 EmitLocalVariableDecls (ec);
4070 else if (expression_or_block is Expression)
4071 EmitExpression (ec);
4074 public override void EmitFinally (EmitContext ec)
4076 if (expression_or_block is DictionaryEntry)
4077 EmitLocalVariableDeclFinally (ec);
4078 else if (expression_or_block is Expression)
4079 EmitExpressionFinally (ec);
4083 /// <summary>
4084 /// Implementation of the foreach C# statement
4085 /// </summary>
4086 public class Foreach : ExceptionStatement {
4087 Expression type;
4088 Expression variable;
4089 Expression expr;
4090 Statement statement;
4091 ForeachHelperMethods hm;
4092 Expression empty, conv;
4093 Type array_type, element_type;
4094 Type var_type;
4095 VariableStorage enumerator;
4097 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4098 Statement stmt, Location l)
4100 this.type = type;
4101 this.variable = var;
4102 this.expr = expr;
4103 statement = stmt;
4104 loc = l;
4107 public override bool Resolve (EmitContext ec)
4109 expr = expr.Resolve (ec);
4110 if (expr == null)
4111 return false;
4113 if (expr is NullLiteral) {
4114 Report.Error (186, expr.Location, "Use of null is not valid in this context");
4115 return false;
4118 TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
4119 if (texpr == null)
4120 return false;
4122 var_type = texpr.Type;
4125 // We need an instance variable. Not sure this is the best
4126 // way of doing this.
4128 // FIXME: When we implement propertyaccess, will those turn
4129 // out to return values in ExprClass? I think they should.
4131 if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4132 expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4133 error1579 (expr.Type);
4134 return false;
4137 if (expr.Type.IsArray) {
4138 array_type = expr.Type;
4139 element_type = TypeManager.GetElementType (array_type);
4141 empty = new EmptyExpression (element_type);
4142 } else {
4143 hm = ProbeCollectionType (ec, expr.Type);
4144 if (hm == null){
4145 error1579 (expr.Type);
4146 return false;
4149 array_type = expr.Type;
4150 element_type = hm.element_type;
4152 empty = new EmptyExpression (hm.element_type);
4155 bool ok = true;
4157 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4158 ec.CurrentBranching.CreateSibling ();
4162 // FIXME: maybe we can apply the same trick we do in the
4163 // array handling to avoid creating empty and conv in some cases.
4165 // Although it is not as important in this case, as the type
4166 // will not likely be object (what the enumerator will return).
4168 conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
4169 if (conv == null)
4170 ok = false;
4172 variable = variable.ResolveLValue (ec, empty);
4173 if (variable == null)
4174 ok = false;
4176 bool disposable = (hm != null) && hm.is_disposable;
4177 FlowBranchingException branching = null;
4178 if (disposable)
4179 branching = ec.StartFlowBranching (this);
4181 if (!statement.Resolve (ec))
4182 ok = false;
4184 if (disposable) {
4185 ResolveFinally (branching);
4186 ec.EndFlowBranching ();
4187 } else
4188 emit_finally = true;
4190 ec.EndFlowBranching ();
4192 return ok;
4196 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4198 static MethodInfo FetchMethodMoveNext (Type t)
4200 MemberList move_next_list;
4202 move_next_list = TypeContainer.FindMembers (
4203 t, MemberTypes.Method,
4204 BindingFlags.Public | BindingFlags.Instance,
4205 Type.FilterName, "MoveNext");
4206 if (move_next_list.Count == 0)
4207 return null;
4209 foreach (MemberInfo m in move_next_list){
4210 MethodInfo mi = (MethodInfo) m;
4211 Type [] args;
4213 args = TypeManager.GetArgumentTypes (mi);
4214 if (args != null && args.Length == 0){
4215 if (TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type)
4216 return mi;
4219 return null;
4223 // Retrieves a `public T get_Current ()' method from the Type `t'
4225 static MethodInfo FetchMethodGetCurrent (Type t)
4227 MemberList get_current_list;
4229 get_current_list = TypeContainer.FindMembers (
4230 t, MemberTypes.Method,
4231 BindingFlags.Public | BindingFlags.Instance,
4232 Type.FilterName, "get_Current");
4233 if (get_current_list.Count == 0)
4234 return null;
4236 foreach (MemberInfo m in get_current_list){
4237 MethodInfo mi = (MethodInfo) m;
4238 Type [] args;
4240 args = TypeManager.GetArgumentTypes (mi);
4241 if (args != null && args.Length == 0)
4242 return mi;
4244 return null;
4248 // Retrieves a `public void Dispose ()' method from the Type `t'
4250 static MethodInfo FetchMethodDispose (Type t)
4252 MemberList dispose_list;
4254 dispose_list = TypeContainer.FindMembers (
4255 t, MemberTypes.Method,
4256 BindingFlags.Public | BindingFlags.Instance,
4257 Type.FilterName, "Dispose");
4258 if (dispose_list.Count == 0)
4259 return null;
4261 foreach (MemberInfo m in dispose_list){
4262 MethodInfo mi = (MethodInfo) m;
4263 Type [] args;
4265 args = TypeManager.GetArgumentTypes (mi);
4266 if (args != null && args.Length == 0){
4267 if (mi.ReturnType == TypeManager.void_type)
4268 return mi;
4271 return null;
4275 // This struct records the helper methods used by the Foreach construct
4277 class ForeachHelperMethods {
4278 public EmitContext ec;
4279 public MethodInfo get_enumerator;
4280 public MethodInfo move_next;
4281 public MethodInfo get_current;
4282 public Type element_type;
4283 public Type enumerator_type;
4284 public bool is_disposable;
4286 public ForeachHelperMethods (EmitContext ec)
4288 this.ec = ec;
4289 this.element_type = TypeManager.object_type;
4290 this.enumerator_type = TypeManager.ienumerator_type;
4291 this.is_disposable = true;
4295 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
4297 if (m == null)
4298 return false;
4300 if (!(m is MethodInfo))
4301 return false;
4303 if (m.Name != "GetEnumerator")
4304 return false;
4306 MethodInfo mi = (MethodInfo) m;
4307 Type [] args = TypeManager.GetArgumentTypes (mi);
4308 if (args != null){
4309 if (args.Length != 0)
4310 return false;
4312 ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
4314 // Check whether GetEnumerator is public
4315 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
4316 return false;
4318 if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4320 // Apply the same optimization as MS: skip the GetEnumerator
4321 // returning an IEnumerator, and use the one returning a
4322 // CharEnumerator instead. This allows us to avoid the
4323 // try-finally block and the boxing.
4325 return false;
4328 // Ok, we can access it, now make sure that we can do something
4329 // with this `GetEnumerator'
4332 Type return_type = mi.ReturnType;
4333 if (mi.ReturnType == TypeManager.ienumerator_type ||
4334 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4335 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4338 // If it is not an interface, lets try to find the methods ourselves.
4339 // For example, if we have:
4340 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4341 // We can avoid the iface call. This is a runtime perf boost.
4342 // even bigger if we have a ValueType, because we avoid the cost
4343 // of boxing.
4345 // We have to make sure that both methods exist for us to take
4346 // this path. If one of the methods does not exist, we will just
4347 // use the interface. Sadly, this complex if statement is the only
4348 // way I could do this without a goto
4351 if (return_type.IsInterface ||
4352 (hm.move_next = FetchMethodMoveNext (return_type)) == null ||
4353 (hm.get_current = FetchMethodGetCurrent (return_type)) == null) {
4355 hm.move_next = TypeManager.bool_movenext_void;
4356 hm.get_current = TypeManager.object_getcurrent_void;
4357 return true;
4360 } else {
4363 // Ok, so they dont return an IEnumerable, we will have to
4364 // find if they support the GetEnumerator pattern.
4367 hm.move_next = FetchMethodMoveNext (return_type);
4368 if (hm.move_next == null)
4369 return false;
4371 hm.get_current = FetchMethodGetCurrent (return_type);
4372 if (hm.get_current == null)
4373 return false;
4376 hm.element_type = hm.get_current.ReturnType;
4377 hm.enumerator_type = return_type;
4378 hm.is_disposable = !hm.enumerator_type.IsSealed ||
4379 TypeManager.ImplementsInterface (
4380 hm.enumerator_type, TypeManager.idisposable_type);
4382 return true;
4385 /// <summary>
4386 /// This filter is used to find the GetEnumerator method
4387 /// on which IEnumerator operates
4388 /// </summary>
4389 static MemberFilter FilterEnumerator;
4391 static Foreach ()
4393 FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
4396 void error1579 (Type t)
4398 Report.Error (1579, loc,
4399 "foreach statement cannot operate on variables of type `" +
4400 t.FullName + "' because that class does not provide a " +
4401 " GetEnumerator method or it is inaccessible");
4404 static bool TryType (Type t, ForeachHelperMethods hm)
4406 MemberList mi;
4408 mi = TypeContainer.FindMembers (t, MemberTypes.Method,
4409 BindingFlags.Public | BindingFlags.NonPublic |
4410 BindingFlags.Instance | BindingFlags.DeclaredOnly,
4411 FilterEnumerator, hm);
4413 if (mi.Count == 0)
4414 return false;
4416 hm.get_enumerator = (MethodInfo) mi [0];
4417 return true;
4421 // Looks for a usable GetEnumerator in the Type, and if found returns
4422 // the three methods that participate: GetEnumerator, MoveNext and get_Current
4424 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
4426 ForeachHelperMethods hm = new ForeachHelperMethods (ec);
4428 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
4429 if (TryType (tt, hm))
4430 return hm;
4431 tt = tt.BaseType;
4435 // Now try to find the method in the interfaces
4437 while (t != null){
4438 Type [] ifaces = t.GetInterfaces ();
4440 foreach (Type i in ifaces){
4441 if (TryType (i, hm))
4442 return hm;
4446 // Since TypeBuilder.GetInterfaces only returns the interface
4447 // types for this type, we have to keep looping, but once
4448 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4449 // done, because it returns all the types
4451 if ((t is TypeBuilder))
4452 t = t.BaseType;
4453 else
4454 break;
4457 return null;
4461 // FIXME: possible optimization.
4462 // We might be able to avoid creating `empty' if the type is the sam
4464 bool EmitCollectionForeach (EmitContext ec)
4466 ILGenerator ig = ec.ig;
4468 enumerator = new VariableStorage (ec, hm.enumerator_type);
4469 enumerator.EmitThis (ig);
4471 // Instantiate the enumerator
4473 if (expr.Type.IsValueType){
4474 IMemoryLocation ml = expr as IMemoryLocation;
4475 // Load the address of the value type.
4476 if (ml == null) {
4477 // This happens if, for example, you have a property
4478 // returning a struct which is IEnumerable
4479 LocalBuilder t = ec.GetTemporaryLocal (expr.Type);
4480 expr.Emit(ec);
4481 ig.Emit (OpCodes.Stloc, t);
4482 ig.Emit (OpCodes.Ldloca, t);
4483 ec.FreeTemporaryLocal (t, expr.Type);
4484 } else {
4485 ml.AddressOf (ec, AddressOp.Load);
4488 // Emit the call.
4489 if (hm.get_enumerator.DeclaringType.IsValueType) {
4490 // the method is declared on the value type
4491 ig.Emit (OpCodes.Call, hm.get_enumerator);
4492 } else {
4493 // it is an interface method, so we must box
4494 ig.Emit (OpCodes.Box, expr.Type);
4495 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
4497 } else {
4498 expr.Emit (ec);
4499 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
4501 enumerator.EmitStore (ig);
4504 // Protect the code in a try/finalize block, so that
4505 // if the beast implement IDisposable, we get rid of it
4507 if (hm.is_disposable && emit_finally)
4508 ig.BeginExceptionBlock ();
4510 Label end_try = ig.DefineLabel ();
4512 ig.MarkLabel (ec.LoopBegin);
4514 enumerator.EmitCall (ig, hm.move_next);
4516 ig.Emit (OpCodes.Brfalse, end_try);
4518 if (ec.InIterator)
4519 ig.Emit (OpCodes.Ldarg_0);
4521 enumerator.EmitCall (ig, hm.get_current);
4523 if (ec.InIterator){
4524 conv.Emit (ec);
4525 ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4526 } else
4527 ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4529 statement.Emit (ec);
4530 ig.Emit (OpCodes.Br, ec.LoopBegin);
4531 ig.MarkLabel (end_try);
4533 // The runtime provides this for us.
4534 // ig.Emit (OpCodes.Leave, end);
4537 // Now the finally block
4539 if (hm.is_disposable) {
4540 DoEmitFinally (ec);
4541 if (emit_finally)
4542 ig.EndExceptionBlock ();
4545 ig.MarkLabel (ec.LoopEnd);
4546 return false;
4549 public override void EmitFinally (EmitContext ec)
4551 ILGenerator ig = ec.ig;
4553 if (hm.enumerator_type.IsValueType) {
4554 enumerator.EmitThis (ig);
4556 MethodInfo mi = FetchMethodDispose (hm.enumerator_type);
4557 if (mi != null) {
4558 enumerator.EmitLoadAddress (ig);
4559 ig.Emit (OpCodes.Call, mi);
4560 } else {
4561 enumerator.EmitLoad (ig);
4562 ig.Emit (OpCodes.Box, hm.enumerator_type);
4563 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4565 } else {
4566 Label call_dispose = ig.DefineLabel ();
4568 enumerator.EmitThis (ig);
4569 enumerator.EmitLoad (ig);
4570 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
4571 ig.Emit (OpCodes.Dup);
4572 ig.Emit (OpCodes.Brtrue_S, call_dispose);
4573 ig.Emit (OpCodes.Pop);
4575 Label end_finally = ig.DefineLabel ();
4576 ig.Emit (OpCodes.Br, end_finally);
4578 ig.MarkLabel (call_dispose);
4579 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4580 ig.MarkLabel (end_finally);
4582 if (emit_finally)
4583 ig.Emit (OpCodes.Endfinally);
4588 // FIXME: possible optimization.
4589 // We might be able to avoid creating `empty' if the type is the sam
4591 bool EmitArrayForeach (EmitContext ec)
4593 int rank = array_type.GetArrayRank ();
4594 ILGenerator ig = ec.ig;
4596 VariableStorage copy = new VariableStorage (ec, array_type);
4599 // Make our copy of the array
4601 copy.EmitThis (ig);
4602 expr.Emit (ec);
4603 copy.EmitStore (ig);
4605 if (rank == 1){
4606 VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
4608 Label loop, test;
4610 counter.EmitThis (ig);
4611 ig.Emit (OpCodes.Ldc_I4_0);
4612 counter.EmitStore (ig);
4613 test = ig.DefineLabel ();
4614 ig.Emit (OpCodes.Br, test);
4616 loop = ig.DefineLabel ();
4617 ig.MarkLabel (loop);
4619 if (ec.InIterator)
4620 ig.Emit (OpCodes.Ldarg_0);
4622 copy.EmitThis (ig);
4623 copy.EmitLoad (ig);
4624 counter.EmitThis (ig);
4625 counter.EmitLoad (ig);
4628 // Load the value, we load the value using the underlying type,
4629 // then we use the variable.EmitAssign to load using the proper cast.
4631 ArrayAccess.EmitLoadOpcode (ig, element_type);
4632 if (ec.InIterator){
4633 conv.Emit (ec);
4634 ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4635 } else
4636 ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4638 statement.Emit (ec);
4640 ig.MarkLabel (ec.LoopBegin);
4641 counter.EmitThis (ig);
4642 counter.EmitThis (ig);
4643 counter.EmitLoad (ig);
4644 ig.Emit (OpCodes.Ldc_I4_1);
4645 ig.Emit (OpCodes.Add);
4646 counter.EmitStore (ig);
4648 ig.MarkLabel (test);
4649 counter.EmitThis (ig);
4650 counter.EmitLoad (ig);
4651 copy.EmitThis (ig);
4652 copy.EmitLoad (ig);
4653 ig.Emit (OpCodes.Ldlen);
4654 ig.Emit (OpCodes.Conv_I4);
4655 ig.Emit (OpCodes.Blt, loop);
4656 } else {
4657 VariableStorage [] dim_len = new VariableStorage [rank];
4658 VariableStorage [] dim_count = new VariableStorage [rank];
4659 Label [] loop = new Label [rank];
4660 Label [] test = new Label [rank];
4661 int dim;
4663 for (dim = 0; dim < rank; dim++){
4664 dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
4665 dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
4666 test [dim] = ig.DefineLabel ();
4667 loop [dim] = ig.DefineLabel ();
4670 for (dim = 0; dim < rank; dim++){
4671 dim_len [dim].EmitThis (ig);
4672 copy.EmitThis (ig);
4673 copy.EmitLoad (ig);
4674 IntLiteral.EmitInt (ig, dim);
4675 ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
4676 dim_len [dim].EmitStore (ig);
4680 for (dim = 0; dim < rank; dim++){
4681 dim_count [dim].EmitThis (ig);
4682 ig.Emit (OpCodes.Ldc_I4_0);
4683 dim_count [dim].EmitStore (ig);
4684 ig.Emit (OpCodes.Br, test [dim]);
4685 ig.MarkLabel (loop [dim]);
4688 if (ec.InIterator)
4689 ig.Emit (OpCodes.Ldarg_0);
4691 copy.EmitThis (ig);
4692 copy.EmitLoad (ig);
4693 for (dim = 0; dim < rank; dim++){
4694 dim_count [dim].EmitThis (ig);
4695 dim_count [dim].EmitLoad (ig);
4699 // FIXME: Maybe we can cache the computation of `get'?
4701 Type [] args = new Type [rank];
4702 MethodInfo get;
4704 for (int i = 0; i < rank; i++)
4705 args [i] = TypeManager.int32_type;
4707 ModuleBuilder mb = CodeGen.Module.Builder;
4708 get = mb.GetArrayMethod (
4709 array_type, "Get",
4710 CallingConventions.HasThis| CallingConventions.Standard,
4711 var_type, args);
4712 ig.Emit (OpCodes.Call, get);
4713 if (ec.InIterator){
4714 conv.Emit (ec);
4715 ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4716 } else
4717 ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4718 statement.Emit (ec);
4719 ig.MarkLabel (ec.LoopBegin);
4720 for (dim = rank - 1; dim >= 0; dim--){
4721 dim_count [dim].EmitThis (ig);
4722 dim_count [dim].EmitThis (ig);
4723 dim_count [dim].EmitLoad (ig);
4724 ig.Emit (OpCodes.Ldc_I4_1);
4725 ig.Emit (OpCodes.Add);
4726 dim_count [dim].EmitStore (ig);
4728 ig.MarkLabel (test [dim]);
4729 dim_count [dim].EmitThis (ig);
4730 dim_count [dim].EmitLoad (ig);
4731 dim_len [dim].EmitThis (ig);
4732 dim_len [dim].EmitLoad (ig);
4733 ig.Emit (OpCodes.Blt, loop [dim]);
4736 ig.MarkLabel (ec.LoopEnd);
4738 return false;
4741 protected override void DoEmit (EmitContext ec)
4743 ILGenerator ig = ec.ig;
4745 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4746 ec.LoopBegin = ig.DefineLabel ();
4747 ec.LoopEnd = ig.DefineLabel ();
4749 if (hm != null)
4750 EmitCollectionForeach (ec);
4751 else
4752 EmitArrayForeach (ec);
4754 ec.LoopBegin = old_begin;
4755 ec.LoopEnd = old_end;