10d263c4b1d735adcf8406f3bf8187b14ec032ab
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001, 2002 Ximian, Inc.
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
14
15 namespace Mono.CSharp {
16
17         using System.Collections;
18         
19         public abstract class Statement {
20                 public Location loc;
21
22                 /// <summary>
23                 ///   Return value indicates whether all code paths emitted return.
24                 /// </summary>
25                 public abstract bool Emit (EmitContext ec);
26
27                 /// <remarks>
28                 ///    Emits a bool expression.
29                 /// </remarks>
30                 public static Expression EmitBoolExpression (EmitContext ec, Expression e,
31                                                              Label target, bool isTrue, Location loc)
32                 {
33                         e = e.Resolve (ec);
34
35                         if (e == null)
36                                 return null;
37
38                         if (e.Type != TypeManager.bool_type){
39                                 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
40                                                                 new Location (-1));
41                         }
42
43                         if (e == null){
44                                 Report.Error (
45                                         31, loc, "Can not convert the expression to a boolean");
46                                 return null;
47                         }
48
49                         if (CodeGen.SymbolWriter != null)
50                                 ec.Mark (loc);
51
52                         bool invert = false;
53                         if (e is Unary){
54                                 Unary u = (Unary) e;
55                                 
56                                 if (u.Oper == Unary.Operator.LogicalNot){
57                                         invert = true;
58
59                                         u.EmitLogicalNot (ec);
60                                 }
61                         } 
62
63                         if (!invert)
64                                 e.Emit (ec);
65
66                         if (isTrue){
67                                 if (invert)
68                                         ec.ig.Emit (OpCodes.Brfalse, target);
69                                 else
70                                         ec.ig.Emit (OpCodes.Brtrue, target);
71                         } else {
72                                 if (invert)
73                                         ec.ig.Emit (OpCodes.Brtrue, target);
74                                 else
75                                         ec.ig.Emit (OpCodes.Brfalse, target);
76                         }
77                         
78                         return e;
79                 }
80         }
81
82         public class EmptyStatement : Statement {
83                 public override bool Emit (EmitContext ec)
84                 {
85                         return false;
86                 }
87         }
88         
89         public class If : Statement {
90                 public readonly Expression  Expr;
91                 public readonly Statement   TrueStatement;
92                 public readonly Statement   FalseStatement;
93                 
94                 public If (Expression expr, Statement trueStatement, Location l)
95                 {
96                         Expr = expr;
97                         TrueStatement = trueStatement;
98                         loc = l;
99                 }
100
101                 public If (Expression expr,
102                            Statement trueStatement,
103                            Statement falseStatement,
104                            Location l)
105                 {
106                         Expr = expr;
107                         TrueStatement = trueStatement;
108                         FalseStatement = falseStatement;
109                         loc = l;
110                 }
111
112                 public override bool Emit (EmitContext ec)
113                 {
114                         ILGenerator ig = ec.ig;
115                         Label false_target = ig.DefineLabel ();
116                         Label end;
117                         bool is_true_ret, is_false_ret;
118                         
119                         if (EmitBoolExpression (ec, Expr, false_target, false, loc) == null)
120                                 return false;
121                         
122                         is_true_ret = TrueStatement.Emit (ec);
123                         is_false_ret = is_true_ret;
124
125                         if (FalseStatement != null){
126                                 bool branch_emitted = false;
127                                 
128                                 end = ig.DefineLabel ();
129                                 if (!is_true_ret){
130                                         ig.Emit (OpCodes.Br, end);
131                                         branch_emitted = true;
132                                 }
133                         
134                                 ig.MarkLabel (false_target);
135                                 is_false_ret = FalseStatement.Emit (ec);
136
137                                 if (branch_emitted)
138                                         ig.MarkLabel (end);
139                         } else {
140                                 ig.MarkLabel (false_target);
141                                 is_false_ret = false;
142                         }
143
144                         return is_true_ret && is_false_ret;
145                 }
146         }
147
148         public class Do : Statement {
149                 public readonly Expression Expr;
150                 public readonly Statement  EmbeddedStatement;
151                 
152                 public Do (Statement statement, Expression boolExpr, Location l)
153                 {
154                         Expr = boolExpr;
155                         EmbeddedStatement = statement;
156                         loc = l;
157                 }
158
159                 public override bool Emit (EmitContext ec)
160                 {
161                         ILGenerator ig = ec.ig;
162                         Label loop = ig.DefineLabel ();
163                         Label old_begin = ec.LoopBegin;
164                         Label old_end = ec.LoopEnd;
165                         bool  old_inloop = ec.InLoop;
166                         Expression e;
167                         
168                         ec.LoopBegin = ig.DefineLabel ();
169                         ec.LoopEnd = ig.DefineLabel ();
170                         ec.InLoop = true;
171                                 
172                         ig.MarkLabel (loop);
173                         EmbeddedStatement.Emit (ec);
174                         ig.MarkLabel (ec.LoopBegin);
175                         e = EmitBoolExpression (ec, Expr, loop, true, loc);
176                         ig.MarkLabel (ec.LoopEnd);
177
178                         ec.LoopBegin = old_begin;
179                         ec.LoopEnd = old_end;
180                         ec.InLoop = old_inloop;
181
182                         //
183                         // Inform whether we are infinite or not
184                         //
185                         if (e is BoolConstant){
186                                 BoolConstant bc = (BoolConstant) e;
187
188                                 if (bc.Value == true)
189                                         return true;
190                         }
191                         
192                         return false;
193                 }
194         }
195
196         public class While : Statement {
197                 public readonly Expression Expr;
198                 public readonly Statement Statement;
199                 
200                 public While (Expression boolExpr, Statement statement, Location l)
201                 {
202                         Expr = boolExpr;
203                         Statement = statement;
204                         loc = l;
205                 }
206
207                 public override bool Emit (EmitContext ec)
208                 {
209                         ILGenerator ig = ec.ig;
210                         Label old_begin = ec.LoopBegin;
211                         Label old_end = ec.LoopEnd;
212                         bool old_inloop = ec.InLoop;
213                         Expression e;
214                         
215                         ec.LoopBegin = ig.DefineLabel ();
216                         ec.LoopEnd = ig.DefineLabel ();
217                         ec.InLoop = true;
218                         
219                         ig.MarkLabel (ec.LoopBegin);
220                         e = EmitBoolExpression (ec, Expr, ec.LoopEnd, false, loc);
221                         Statement.Emit (ec);
222                         ig.Emit (OpCodes.Br, ec.LoopBegin);
223                         ig.MarkLabel (ec.LoopEnd);
224
225                         ec.LoopBegin = old_begin;
226                         ec.LoopEnd = old_end;
227                         ec.InLoop = old_inloop;
228
229                         //
230                         // Inform whether we are infinite or not
231                         //
232                         if (e is BoolConstant){
233                                 BoolConstant bc = (BoolConstant) e;
234
235                                 if (bc.Value == true)
236                                         return true;
237                         }
238                         return false;
239                 }
240         }
241
242         public class For : Statement {
243                 public readonly Statement InitStatement;
244                 public readonly Expression Test;
245                 public readonly Statement Increment;
246                 public readonly Statement Statement;
247                 
248                 public For (Statement initStatement,
249                             Expression test,
250                             Statement increment,
251                             Statement statement,
252                             Location l)
253                 {
254                         InitStatement = initStatement;
255                         Test = test;
256                         Increment = increment;
257                         Statement = statement;
258                         loc = l;
259                 }
260
261                 public override bool Emit (EmitContext ec)
262                 {
263                         ILGenerator ig = ec.ig;
264                         Label old_begin = ec.LoopBegin;
265                         Label old_end = ec.LoopEnd;
266                         bool old_inloop = ec.InLoop;
267                         Label loop = ig.DefineLabel ();
268                         Expression e = null;
269
270                         if (InitStatement != null)
271                                 if (! (InitStatement is EmptyStatement))
272                                         InitStatement.Emit (ec);
273
274                         ec.LoopBegin = ig.DefineLabel ();
275                         ec.LoopEnd = ig.DefineLabel ();
276                         ec.InLoop = true;
277
278                         ig.MarkLabel (loop);
279
280                         //
281                         // If test is null, there is no test, and we are just
282                         // an infinite loop
283                         //
284                         if (Test != null)
285                                 e = EmitBoolExpression (ec, Test, ec.LoopEnd, false, loc);
286                 
287                         Statement.Emit (ec);
288                         ig.MarkLabel (ec.LoopBegin);
289                         if (!(Increment is EmptyStatement))
290                                 Increment.Emit (ec);
291                         ig.Emit (OpCodes.Br, loop);
292                         ig.MarkLabel (ec.LoopEnd);
293
294                         ec.LoopBegin = old_begin;
295                         ec.LoopEnd = old_end;
296                         ec.InLoop = old_inloop;
297
298                         //
299                         // Inform whether we are infinite or not
300                         //
301                         if (Test != null){
302                                 if (e is BoolConstant){
303                                         BoolConstant bc = (BoolConstant) e;
304
305                                         if (bc.Value)
306                                                 return true;
307                                 }
308                                 return false;
309                         } else
310                                 return true;
311                 }
312         }
313         
314         public class StatementExpression : Statement {
315                 public readonly ExpressionStatement Expr;
316                 
317                 public StatementExpression (ExpressionStatement expr, Location l)
318                 {
319                         Expr = expr;
320                         loc = l;
321                 }
322
323                 public override bool Emit (EmitContext ec)
324                 {
325                         ILGenerator ig = ec.ig;
326                         Expression ne;
327                         
328                         ne = Expr.Resolve (ec);
329                         if (ne != null){
330                                 if (ne is ExpressionStatement)
331                                         ((ExpressionStatement) ne).EmitStatement (ec);
332                                 else {
333                                         ne.Emit (ec);
334                                         ig.Emit (OpCodes.Pop);
335                                 }
336                         }
337
338                         return false;
339                 }
340
341                 public override string ToString ()
342                 {
343                         return "StatementExpression (" + Expr + ")";
344                 }
345         }
346
347         /// <summary>
348         ///   Implements the return statement
349         /// </summary>
350         public class Return : Statement {
351                 public Expression Expr;
352                 
353                 public Return (Expression expr, Location l)
354                 {
355                         Expr = expr;
356                         loc = l;
357                 }
358
359                 public override bool Emit (EmitContext ec)
360                 {
361                         if (ec.InFinally){
362                                 Report.Error (157,loc,"Control can not leave the body of the finally block");
363                                 return false;
364                         }
365                         
366                         if (ec.ReturnType == null){
367                                 if (Expr != null){
368                                         Report.Error (127, loc, "Return with a value not allowed here");
369                                         return false;
370                                 }
371                         } else {
372                                 if (Expr == null){
373                                         Report.Error (126, loc, "An object of type `" +
374                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
375                                                       "expected for the return statement");
376                                         return false;
377                                 }
378
379                                 Expr = Expr.Resolve (ec);
380                                 if (Expr == null)
381                                         return false;
382
383                                 if (Expr.Type != ec.ReturnType)
384                                         Expr = Expression.ConvertImplicitRequired (
385                                                 ec, Expr, ec.ReturnType, loc);
386
387                                 if (Expr == null)
388                                         return false;
389
390                                 Expr.Emit (ec);
391
392                                 if (ec.InTry || ec.InCatch)
393                                         ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
394                         }
395
396                         if (ec.InTry || ec.InCatch)
397                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
398                         else
399                                 ec.ig.Emit (OpCodes.Ret);
400
401                         return true; 
402                 }
403         }
404
405         public class Goto : Statement {
406                 string target;
407                 Block block;
408                 
409                 public Goto (Block parent_block, string label, Location l)
410                 {
411                         block = parent_block;
412                         loc = l;
413                         target = label;
414                 }
415
416                 public string Target {
417                         get {
418                                 return target;
419                         }
420                 }
421
422                 public override bool Emit (EmitContext ec)
423                 {
424                         LabeledStatement label = block.LookupLabel (target);
425
426                         if (label == null){
427                                 //
428                                 // Maybe we should catch this before?
429                                 //
430                                 Report.Error (
431                                         159, loc,
432                                         "No such label `" + target + "' in this scope");
433                                 return false;
434                         }
435                         Label l = label.LabelTarget (ec);
436                         ec.ig.Emit (OpCodes.Br, l);
437                         
438                         return false;
439                 }
440         }
441
442         public class LabeledStatement : Statement {
443                 string label_name;
444                 bool defined;
445                 Label label;
446                 
447                 public LabeledStatement (string label_name)
448                 {
449                         this.label_name = label_name;
450                 }
451
452                 public Label LabelTarget (EmitContext ec)
453                 {
454                         if (defined)
455                                 return label;
456                         label = ec.ig.DefineLabel ();
457                         defined = true;
458
459                         return label;
460                 }
461                 
462                 public override bool Emit (EmitContext ec)
463                 {
464                         LabelTarget (ec);
465                         ec.ig.MarkLabel (label);
466
467                         return false;
468                 }
469         }
470         
471
472         /// <summary>
473         ///   `goto default' statement
474         /// </summary>
475         public class GotoDefault : Statement {
476                 
477                 public GotoDefault (Location l)
478                 {
479                         loc = l;
480                 }
481
482                 public override bool Emit (EmitContext ec)
483                 {
484                         if (ec.Switch == null){
485                                 Report.Error (153, loc, "goto default is only valid in a switch statement");
486                                 return false;
487                         }
488
489                         if (!ec.Switch.GotDefault){
490                                 Report.Error (159, loc, "No default target on switch statement");
491                                 return false;
492                         }
493                         ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
494                         return false;
495                 }
496         }
497
498         /// <summary>
499         ///   `goto case' statement
500         /// </summary>
501         public class GotoCase : Statement {
502                 Expression expr;
503                 
504                 public GotoCase (Expression e, Location l)
505                 {
506                         expr = e;
507                         loc = l;
508                 }
509
510                 public override bool Emit (EmitContext ec)
511                 {
512                         if (ec.Switch == null){
513                                 Report.Error (153, loc, "goto case is only valid in a switch statement");
514                                 return false;
515                         }
516
517                         expr = expr.Resolve (ec);
518                         if (expr == null)
519                                 return false;
520
521                         if (!(expr is Constant)){
522                                 Report.Error (159, loc, "Target expression for goto case is not constant");
523                                 return false;
524                         }
525
526                         object val = Expression.ConvertIntLiteral (
527                                 (Constant) expr, ec.Switch.SwitchType, loc);
528
529                         if (val == null)
530                                 return false;
531                                         
532                         SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
533
534                         if (sl == null){
535                                 Report.Error (
536                                         159, loc,
537                                         "No such label 'case " + val + "': for the goto case");
538                         }
539
540                         ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
541                         return false;
542                 }
543         }
544         
545         public class Throw : Statement {
546                 public readonly Expression Expr;
547                 
548                 public Throw (Expression expr, Location l)
549                 {
550                         Expr = expr;
551                         loc = l;
552                 }
553
554                 public override bool Emit (EmitContext ec)
555                 {
556                         if (Expr == null){
557                                 if (ec.InCatch)
558                                         ec.ig.Emit (OpCodes.Rethrow);
559                                 else {
560                                         Report.Error (
561                                                 156, loc,
562                                                 "A throw statement with no argument is only " +
563                                                 "allowed in a catch clause");
564                                 }
565                                 return false;
566                         }
567                         
568                         Expression e = Expr.Resolve (ec);
569
570                         if (e == null)
571                                 return false;
572                         
573                         e.Emit (ec);
574
575                         ec.ig.Emit (OpCodes.Throw);
576
577                         return true;
578                 }
579         }
580
581         public class Break : Statement {
582                 
583                 public Break (Location l)
584                 {
585                         loc = l;
586                 }
587
588                 public override bool Emit (EmitContext ec)
589                 {
590                         ILGenerator ig = ec.ig;
591
592                         if (ec.InLoop == false && ec.Switch == null){
593                                 Report.Error (139, loc, "No enclosing loop or switch to continue to");
594                                 return false;
595                         }
596                         
597                         ig.Emit (OpCodes.Br, ec.LoopEnd);
598                         return false;
599                 }
600         }
601
602         public class Continue : Statement {
603                 
604                 public Continue (Location l)
605                 {
606                         loc = l;
607                 }
608
609                 public override bool Emit (EmitContext ec)
610                 {
611                         Label begin = ec.LoopBegin;
612                         
613                         if (!ec.InLoop){
614                                 Report.Error (139, loc, "No enclosing loop to continue to");
615                                 return false;
616                         } 
617
618                         //
619                         // UGH: Non trivial.  This Br might cross a try/catch boundary
620                         // How can we tell?
621                         //
622                         // while () {
623                         //   try { ... } catch { continue; }
624                         // }
625                         //
626                         // From:
627                         // try {} catch { while () { continue; }}
628                         //
629                         ec.ig.Emit (OpCodes.Br, begin);
630                         return false;
631                 }
632         }
633         
634         public class VariableInfo {
635                 public readonly string Type;
636                 public LocalBuilder LocalBuilder;
637                 public Type VariableType;
638                 public readonly Location Location;
639                 
640                 public bool Used;
641                 public bool Assigned;
642                 public bool ReadOnly;
643                 
644                 public VariableInfo (string type, Location l)
645                 {
646                         Type = type;
647                         LocalBuilder = null;
648                         Location = l;
649                 }
650
651                 public void MakePinned ()
652                 {
653                         TypeManager.MakePinned (LocalBuilder);
654                 }                               
655         }
656                 
657         /// <summary>
658         ///   Block represents a C# block.
659         /// </summary>
660         ///
661         /// <remarks>
662         ///   This class is used in a number of places: either to represent
663         ///   explicit blocks that the programmer places or implicit blocks.
664         ///
665         ///   Implicit blocks are used as labels or to introduce variable
666         ///   declarations.
667         /// </remarks>
668         public class Block : Statement {
669                 public readonly Block     Parent;
670                 public readonly bool      Implicit;
671                 public readonly Location  StartLocation;
672                 public Location           EndLocation;
673
674                 //
675                 // The statements in this block
676                 //
677                 ArrayList statements;
678
679                 //
680                 // An array of Blocks.  We keep track of children just
681                 // to generate the local variable declarations.
682                 //
683                 // Statements and child statements are handled through the
684                 // statements.
685                 //
686                 ArrayList children;
687                 
688                 //
689                 // Labels.  (label, block) pairs.
690                 //
691                 Hashtable labels;
692
693                 //
694                 // Keeps track of (name, type) pairs
695                 //
696                 Hashtable variables;
697
698                 //
699                 // Keeps track of constants
700                 Hashtable constants;
701
702                 //
703                 // Maps variable names to ILGenerator.LocalBuilders
704                 //
705                 Hashtable local_builders;
706
707                 bool used = false;
708
709                 static int id;
710
711                 int this_id;
712                 
713                 public Block (Block parent)
714                         : this (parent, false, Location.Null, Location.Null)
715                 { }
716
717                 public Block (Block parent, bool implicit_block)
718                         : this (parent, implicit_block, Location.Null, Location.Null)
719                 { }
720
721                 public Block (Block parent, Location start, Location end)
722                         : this (parent, false, start, end)
723                 { }
724
725                 public Block (Block parent, bool implicit_block, Location start, Location end)
726                 {
727                         if (parent != null)
728                                 parent.AddChild (this);
729                         
730                         this.Parent = parent;
731                         this.Implicit = implicit_block;
732                         this.StartLocation = start;
733                         this.EndLocation = end;
734                         this.loc = start;
735                         this_id = id++;
736                         statements = new ArrayList ();
737                 }
738
739                 public int ID {
740                         get {
741                                 return this_id;
742                         }
743                 }
744                 
745                 void AddChild (Block b)
746                 {
747                         if (children == null)
748                                 children = new ArrayList ();
749                         
750                         children.Add (b);
751                 }
752
753                 public void SetEndLocation (Location loc)
754                 {
755                         EndLocation = loc;
756                 }
757
758                 /// <summary>
759                 ///   Adds a label to the current block. 
760                 /// </summary>
761                 ///
762                 /// <returns>
763                 ///   false if the name already exists in this block. true
764                 ///   otherwise.
765                 /// </returns>
766                 ///
767                 public bool AddLabel (string name, LabeledStatement target)
768                 {
769                         if (labels == null)
770                                 labels = new Hashtable ();
771                         if (labels.Contains (name))
772                                 return false;
773                         
774                         labels.Add (name, target);
775                         return true;
776                 }
777
778                 public LabeledStatement LookupLabel (string name)
779                 {
780                         if (labels != null){
781                                 if (labels.Contains (name))
782                                         return ((LabeledStatement) labels [name]);
783                         }
784
785                         if (Parent != null)
786                                 return Parent.LookupLabel (name);
787
788                         return null;
789                 }
790
791                 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
792                 {
793                         if (variables == null)
794                                 variables = new Hashtable ();
795
796                         if (GetVariableType (name) != null)
797                                 return null;
798
799                         if (pars != null) {
800                                 int idx = 0;
801                                 Parameter p = pars.GetParameterByName (name, out idx);
802                                 if (p != null) 
803                                         return null;
804                         }
805                         
806                         VariableInfo vi = new VariableInfo (type, l);
807
808                         variables.Add (name, vi);
809
810                         // Console.WriteLine ("Adding {0} to {1}", name, ID);
811                         return vi;
812                 }
813
814                 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
815                 {
816                         if (AddVariable (type, name, pars, l) == null)
817                                 return false;
818                         
819                         if (constants == null)
820                                 constants = new Hashtable ();
821
822                         constants.Add (name, value);
823                         return true;
824                 }
825
826                 public Hashtable Variables {
827                         get {
828                                 return variables;
829                         }
830                 }
831
832                 public VariableInfo GetVariableInfo (string name)
833                 {
834                         if (variables != null) {
835                                 object temp;
836                                 temp = variables [name];
837
838                                 if (temp != null){
839                                         return (VariableInfo) temp;
840                                 }
841                         }
842
843                         if (Parent != null)
844                                 return Parent.GetVariableInfo (name);
845
846                         return null;
847                 }
848                 
849                 public string GetVariableType (string name)
850                 {
851                         VariableInfo vi = GetVariableInfo (name);
852
853                         if (vi != null)
854                                 return vi.Type;
855
856                         return null;
857                 }
858
859                 public Expression GetConstantExpression (string name)
860                 {
861                         if (constants != null) {
862                                 object temp;
863                                 temp = constants [name];
864                                 
865                                 if (temp != null)
866                                         return (Expression) temp;
867                         }
868                         
869                         if (Parent != null)
870                                 return Parent.GetConstantExpression (name);
871
872                         return null;
873                 }
874                 
875                 /// <summary>
876                 ///   True if the variable named @name has been defined
877                 ///   in this block
878                 /// </summary>
879                 public bool IsVariableDefined (string name)
880                 {
881                         // Console.WriteLine ("Looking up {0} in {1}", name, ID);
882                         if (variables != null) {
883                                 if (variables.Contains (name))
884                                         return true;
885                         }
886                         
887                         if (Parent != null)
888                                 return Parent.IsVariableDefined (name);
889
890                         return false;
891                 }
892
893                 /// <summary>
894                 ///   True if the variable named @name is a constant
895                 ///  </summary>
896                 public bool IsConstant (string name)
897                 {
898                         Expression e = null;
899                         
900                         e = GetConstantExpression (name);
901                         
902                         return e != null;
903                 }
904                 
905                 /// <summary>
906                 ///   Use to fetch the statement associated with this label
907                 /// </summary>
908                 public Statement this [string name] {
909                         get {
910                                 return (Statement) labels [name];
911                         }
912                 }
913
914                 /// <returns>
915                 ///   A list of labels that were not used within this block
916                 /// </returns>
917                 public string [] GetUnreferenced ()
918                 {
919                         // FIXME: Implement me
920                         return null;
921                 }
922
923                 public void AddStatement (Statement s)
924                 {
925                         statements.Add (s);
926                         used = true;
927                 }
928
929                 public bool Used {
930                         get {
931                                 return used;
932                         }
933                 }
934
935                 public void Use ()
936                 {
937                         used = true;
938                 }
939                 
940                 /// <summary>
941                 ///   Emits the variable declarations and labels.
942                 /// </summary>
943                 /// <remarks>
944                 ///   tc: is our typecontainer (to resolve type references)
945                 ///   ig: is the code generator:
946                 ///   toplevel: the toplevel block.  This is used for checking 
947                 ///             that no two labels with the same name are used.
948                 /// </remarks>
949                 public void EmitMeta (EmitContext ec, Block toplevel)
950                 {
951                         DeclSpace ds = ec.DeclSpace;
952                         ILGenerator ig = ec.ig;
953                                 
954                         //
955                         // Process this block variables
956                         //
957                         if (variables != null){
958                                 local_builders = new Hashtable ();
959                                 
960                                 foreach (DictionaryEntry de in variables){
961                                         string name = (string) de.Key;
962                                         VariableInfo vi = (VariableInfo) de.Value;
963                                         Type t;
964
965                                         t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
966                                         if (t == null)
967                                                 continue;
968
969                                         vi.VariableType = t;
970                                         vi.LocalBuilder = ig.DeclareLocal (t);
971
972                                         if (CodeGen.SymbolWriter != null)
973                                                 vi.LocalBuilder.SetLocalSymInfo (name);
974
975                                         if (constants == null)
976                                                 continue;
977
978                                         Expression cv = (Expression) constants [name];
979                                         if (cv == null)
980                                                 continue;
981
982                                         Expression e = cv.Resolve (ec);
983                                         if (e == null)
984                                                 continue;
985
986                                         if (!(e is Constant)){
987                                                 Report.Error (133, vi.Location,
988                                                               "The expression being assigned to `" +
989                                                               name + "' must be constant (" + e + ")");
990                                                 continue;
991                                         }
992
993                                         constants.Remove (name);
994                                         constants.Add (name, e);
995                                 }
996                         }
997
998                         //
999                         // Now, handle the children
1000                         //
1001                         if (children != null){
1002                                 foreach (Block b in children)
1003                                         b.EmitMeta (ec, toplevel);
1004                         }
1005                 }
1006
1007                 public void UsageWarning ()
1008                 {
1009                         string name;
1010                         
1011                         if (variables != null){
1012                                 foreach (DictionaryEntry de in variables){
1013                                         VariableInfo vi = (VariableInfo) de.Value;
1014                                         
1015                                         if (vi.Used)
1016                                                 continue;
1017                                         
1018                                         name = (string) de.Key;
1019                                                 
1020                                         if (vi.Assigned){
1021                                                 Report.Warning (
1022                                                         219, vi.Location, "The variable `" + name +
1023                                                         "' is assigned but its value is never used");
1024                                         } else {
1025                                                 Report.Warning (
1026                                                         168, vi.Location, "The variable `" +
1027                                                         name +
1028                                                         "' is declared but never used");
1029                                         } 
1030                                 }
1031                         }
1032
1033                         if (children != null)
1034                                 foreach (Block b in children)
1035                                         b.UsageWarning ();
1036                 }
1037
1038                 public override bool Emit (EmitContext ec)
1039                 {
1040                         bool is_ret = false;
1041                         Block prev_block = ec.CurrentBlock;
1042
1043                         ec.CurrentBlock = this;
1044
1045                         if (CodeGen.SymbolWriter != null) {
1046                                 ec.Mark (StartLocation);
1047
1048                                 foreach (Statement s in statements) {
1049                                         ec.Mark (s.loc);
1050
1051                                         is_ret = s.Emit (ec);
1052                                 }
1053
1054                                 ec.Mark (EndLocation); 
1055                         } else {
1056                                 foreach (Statement s in statements)
1057                                         is_ret = s.Emit (ec);
1058                         }
1059                         
1060                         ec.CurrentBlock = prev_block;
1061                         return is_ret;
1062                 }
1063         }
1064
1065         public class SwitchLabel {
1066                 Expression label;
1067                 object converted;
1068                 public Location loc;
1069                 public Label ILLabel;
1070                 public Label ILLabelCode;
1071                 
1072                 //
1073                 // if expr == null, then it is the default case.
1074                 //
1075                 public SwitchLabel (Expression expr, Location l)
1076                 {
1077                         label = expr;
1078                         loc = l;
1079                 }
1080
1081                 public Expression Label {
1082                         get {
1083                                 return label;
1084                         }
1085                 }
1086
1087                 public object Converted {
1088                         get {
1089                                 return converted;
1090                         }
1091                 }
1092                 
1093                 //
1094                 // Resolves the expression, reduces it to a literal if possible
1095                 // and then converts it to the requested type.
1096                 //
1097                 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1098                 {
1099                         ILLabel = ec.ig.DefineLabel ();
1100                         ILLabelCode = ec.ig.DefineLabel ();
1101
1102                         if (label == null)
1103                                 return true;
1104                         
1105                         Expression e = label.Resolve (ec);
1106
1107                         if (e == null)
1108                                 return false;
1109
1110                         if (!(e is Constant)){
1111                                 Console.WriteLine ("Value is: " + label);
1112                                 Report.Error (150, loc, "A constant value is expected");
1113                                 return false;
1114                         }
1115
1116                         if (e is StringConstant || e is NullLiteral){
1117                                 if (required_type == TypeManager.string_type){
1118                                         converted = label;
1119                                         ILLabel = ec.ig.DefineLabel ();
1120                                         return true;
1121                                 }
1122                         }
1123
1124                         converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1125                         if (converted == null)
1126                                 return false;
1127
1128                         return true;
1129                 }
1130         }
1131
1132         public class SwitchSection {
1133                 // An array of SwitchLabels.
1134                 public readonly ArrayList Labels;
1135                 public readonly Block Block;
1136                 
1137                 public SwitchSection (ArrayList labels, Block block)
1138                 {
1139                         Labels = labels;
1140                         Block = block;
1141                 }
1142         }
1143         
1144         public class Switch : Statement {
1145                 public readonly ArrayList Sections;
1146                 public Expression Expr;
1147
1148                 /// <summary>
1149                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
1150                 /// </summary>
1151                 public Hashtable Elements;
1152
1153                 /// <summary>
1154                 ///   The governing switch type
1155                 /// </summary>
1156                 public Type SwitchType;
1157
1158                 //
1159                 // Computed
1160                 //
1161                 bool got_default;
1162                 Label default_target;
1163                 
1164                 //
1165                 // The types allowed to be implicitly cast from
1166                 // on the governing type
1167                 //
1168                 static Type [] allowed_types;
1169                 
1170                 public Switch (Expression e, ArrayList sects, Location l)
1171                 {
1172                         Expr = e;
1173                         Sections = sects;
1174                         loc = l;
1175                 }
1176
1177                 public bool GotDefault {
1178                         get {
1179                                 return got_default;
1180                         }
1181                 }
1182
1183                 public Label DefaultTarget {
1184                         get {
1185                                 return default_target;
1186                         }
1187                 }
1188
1189                 //
1190                 // Determines the governing type for a switch.  The returned
1191                 // expression might be the expression from the switch, or an
1192                 // expression that includes any potential conversions to the
1193                 // integral types or to string.
1194                 //
1195                 Expression SwitchGoverningType (EmitContext ec, Type t)
1196                 {
1197                         if (t == TypeManager.int32_type ||
1198                             t == TypeManager.uint32_type ||
1199                             t == TypeManager.char_type ||
1200                             t == TypeManager.byte_type ||
1201                             t == TypeManager.sbyte_type ||
1202                             t == TypeManager.ushort_type ||
1203                             t == TypeManager.short_type ||
1204                             t == TypeManager.uint64_type ||
1205                             t == TypeManager.int64_type ||
1206                             t == TypeManager.string_type ||
1207                                 t == TypeManager.bool_type ||
1208                                 t.IsSubclassOf (TypeManager.enum_type))
1209                                 return Expr;
1210
1211                         if (allowed_types == null){
1212                                 allowed_types = new Type [] {
1213                                         TypeManager.sbyte_type,
1214                                         TypeManager.byte_type,
1215                                         TypeManager.short_type,
1216                                         TypeManager.ushort_type,
1217                                         TypeManager.int32_type,
1218                                         TypeManager.uint32_type,
1219                                         TypeManager.int64_type,
1220                                         TypeManager.uint64_type,
1221                                         TypeManager.char_type,
1222                                         TypeManager.bool_type,
1223                                         TypeManager.string_type
1224                                 };
1225                         }
1226
1227                         //
1228                         // Try to find a *user* defined implicit conversion.
1229                         //
1230                         // If there is no implicit conversion, or if there are multiple
1231                         // conversions, we have to report an error
1232                         //
1233                         Expression converted = null;
1234                         foreach (Type tt in allowed_types){
1235                                 Expression e;
1236                                 
1237                                 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1238                                 if (e == null)
1239                                         continue;
1240
1241                                 if (converted != null){
1242                                         Report.Error (-12, loc, "More than one conversion to an integral " +
1243                                                       " type exists for type `" +
1244                                                       TypeManager.CSharpName (Expr.Type)+"'");
1245                                         return null;
1246                                 } else
1247                                         converted = e;
1248                         }
1249                         return converted;
1250                 }
1251
1252                 void error152 (string n)
1253                 {
1254                         Report.Error (
1255                                 152, "The label `" + n + ":' " +
1256                                 "is already present on this switch statement");
1257                 }
1258                 
1259                 //
1260                 // Performs the basic sanity checks on the switch statement
1261                 // (looks for duplicate keys and non-constant expressions).
1262                 //
1263                 // It also returns a hashtable with the keys that we will later
1264                 // use to compute the switch tables
1265                 //
1266                 bool CheckSwitch (EmitContext ec)
1267                 {
1268                         Type compare_type;
1269                         bool error = false;
1270                         Elements = new Hashtable ();
1271                                 
1272                         got_default = false;
1273
1274                         if (TypeManager.IsEnumType (SwitchType)){
1275                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1276                         } else
1277                                 compare_type = SwitchType;
1278                         
1279                         foreach (SwitchSection ss in Sections){
1280                                 foreach (SwitchLabel sl in ss.Labels){
1281                                         if (!sl.ResolveAndReduce (ec, SwitchType)){
1282                                                 error = true;
1283                                                 continue;
1284                                         }
1285
1286                                         if (sl.Label == null){
1287                                                 if (got_default){
1288                                                         error152 ("default");
1289                                                         error = true;
1290                                                 }
1291                                                 got_default = true;
1292                                                 continue;
1293                                         }
1294                                         
1295                                         object key = sl.Converted;
1296
1297                                         if (key is Constant)
1298                                                 key = ((Constant) key).GetValue ();
1299
1300                                         if (key == null)
1301                                                 key = NullLiteral.Null;
1302                                         
1303                                         string lname = null;
1304                                         if (compare_type == TypeManager.uint64_type){
1305                                                 ulong v = (ulong) key;
1306
1307                                                 if (Elements.Contains (v))
1308                                                         lname = v.ToString ();
1309                                                 else
1310                                                         Elements.Add (v, sl);
1311                                         } else if (compare_type == TypeManager.int64_type){
1312                                                 long v = (long) key;
1313
1314                                                 if (Elements.Contains (v))
1315                                                         lname = v.ToString ();
1316                                                 else
1317                                                         Elements.Add (v, sl);
1318                                         } else if (compare_type == TypeManager.uint32_type){
1319                                                 uint v = (uint) key;
1320
1321                                                 if (Elements.Contains (v))
1322                                                         lname = v.ToString ();
1323                                                 else
1324                                                         Elements.Add (v, sl);
1325                                         } else if (compare_type == TypeManager.char_type){
1326                                                 char v = (char) key;
1327                                                 
1328                                                 if (Elements.Contains (v))
1329                                                         lname = v.ToString ();
1330                                                 else
1331                                                         Elements.Add (v, sl);
1332                                         } else if (compare_type == TypeManager.byte_type){
1333                                                 byte v = (byte) key;
1334                                                 
1335                                                 if (Elements.Contains (v))
1336                                                         lname = v.ToString ();
1337                                                 else
1338                                                         Elements.Add (v, sl);
1339                                         } else if (compare_type == TypeManager.sbyte_type){
1340                                                 sbyte v = (sbyte) key;
1341                                                 
1342                                                 if (Elements.Contains (v))
1343                                                         lname = v.ToString ();
1344                                                 else
1345                                                         Elements.Add (v, sl);
1346                                         } else if (compare_type == TypeManager.short_type){
1347                                                 short v = (short) key;
1348                                                 
1349                                                 if (Elements.Contains (v))
1350                                                         lname = v.ToString ();
1351                                                 else
1352                                                         Elements.Add (v, sl);
1353                                         } else if (compare_type == TypeManager.ushort_type){
1354                                                 ushort v = (ushort) key;
1355                                                 
1356                                                 if (Elements.Contains (v))
1357                                                         lname = v.ToString ();
1358                                                 else
1359                                                         Elements.Add (v, sl);
1360                                         } else if (compare_type == TypeManager.string_type){
1361                                                 if (key is NullLiteral){
1362                                                         if (Elements.Contains (NullLiteral.Null))
1363                                                                 lname = "null";
1364                                                         else
1365                                                                 Elements.Add (NullLiteral.Null, null);
1366                                                 } else {
1367                                                         string s = (string) key;
1368
1369                                                         if (Elements.Contains (s))
1370                                                                 lname = s;
1371                                                         else
1372                                                                 Elements.Add (s, sl);
1373                                                 }
1374                                         } else if (compare_type == TypeManager.int32_type) {
1375                                                 int v = (int) key;
1376
1377                                                 if (Elements.Contains (v))
1378                                                         lname = v.ToString ();
1379                                                 else
1380                                                         Elements.Add (v, sl);
1381                                         } else if (compare_type == TypeManager.bool_type) {
1382                                                 bool v = (bool) key;
1383
1384                                                 if (Elements.Contains (v))
1385                                                         lname = v.ToString ();
1386                                                 else
1387                                                         Elements.Add (v, sl);
1388                                         }
1389                                         else
1390                                         {
1391                                                 throw new Exception ("Unknown switch type!" +
1392                                                                      SwitchType + " " + compare_type);
1393                                         }
1394
1395                                         if (lname != null){
1396                                                 error152 ("case + " + lname);
1397                                                 error = true;
1398                                         }
1399                                 }
1400                         }
1401                         if (error)
1402                                 return false;
1403                         
1404                         return true;
1405                 }
1406
1407                 void EmitObjectInteger (ILGenerator ig, object k)
1408                 {
1409                         if (k is int)
1410                                 IntConstant.EmitInt (ig, (int) k);
1411                         else if (k is Constant) {
1412                                 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1413                         } 
1414                         else if (k is uint)
1415                                 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1416                         else if (k is long)
1417                         {
1418                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1419                                 {
1420                                         IntConstant.EmitInt (ig, (int) (long) k);
1421                                         ig.Emit (OpCodes.Conv_I8);
1422                                 }
1423                                 else
1424                                         LongConstant.EmitLong (ig, (long) k);
1425                         }
1426                         else if (k is ulong)
1427                         {
1428                                 if ((ulong) k < (1L<<32))
1429                                 {
1430                                         IntConstant.EmitInt (ig, (int) (long) k);
1431                                         ig.Emit (OpCodes.Conv_U8);
1432                                 }
1433                                 else
1434                                 {
1435                                         LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1436                                 }
1437                         }
1438                         else if (k is char)
1439                                 IntConstant.EmitInt (ig, (int) ((char) k));
1440                         else if (k is sbyte)
1441                                 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1442                         else if (k is byte)
1443                                 IntConstant.EmitInt (ig, (int) ((byte) k));
1444                         else if (k is short)
1445                                 IntConstant.EmitInt (ig, (int) ((short) k));
1446                         else if (k is ushort)
1447                                 IntConstant.EmitInt (ig, (int) ((ushort) k));
1448                         else if (k is bool)
1449                                 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1450                         else
1451                                 throw new Exception ("Unhandled case");
1452                 }
1453                 
1454                 // structure used to hold blocks of keys while calculating table switch
1455                 class KeyBlock : IComparable
1456                 {
1457                         public KeyBlock (long _nFirst)
1458                         {
1459                                 nFirst = nLast = _nFirst;
1460                         }
1461                         public long nFirst;
1462                         public long nLast;
1463                         public ArrayList rgKeys = null;
1464                         public int Length
1465                         {
1466                                 get { return (int) (nLast - nFirst + 1); }
1467                         }
1468                         public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1469                         {
1470                                 return kbLast.nLast - kbFirst.nFirst + 1;
1471                         }
1472                         public int CompareTo (object obj)
1473                         {
1474                                 KeyBlock kb = (KeyBlock) obj;
1475                                 int nLength = Length;
1476                                 int nLengthOther = kb.Length;
1477                                 if (nLengthOther == nLength)
1478                                         return (int) (kb.nFirst - nFirst);
1479                                 return nLength - nLengthOther;
1480                         }
1481                 }
1482
1483                 /// <summary>
1484                 /// This method emits code for a lookup-based switch statement (non-string)
1485                 /// Basically it groups the cases into blocks that are at least half full,
1486                 /// and then spits out individual lookup opcodes for each block.
1487                 /// It emits the longest blocks first, and short blocks are just
1488                 /// handled with direct compares.
1489                 /// </summary>
1490                 /// <param name="ec"></param>
1491                 /// <param name="val"></param>
1492                 /// <returns></returns>
1493                 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1494                 {
1495                         int cElements = Elements.Count;
1496                         object [] rgKeys = new object [cElements];
1497                         Elements.Keys.CopyTo (rgKeys, 0);
1498                         Array.Sort (rgKeys);
1499
1500                         // initialize the block list with one element per key
1501                         ArrayList rgKeyBlocks = new ArrayList ();
1502                         foreach (object key in rgKeys)
1503                                 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1504
1505                         KeyBlock kbCurr;
1506                         // iteratively merge the blocks while they are at least half full
1507                         // there's probably a really cool way to do this with a tree...
1508                         while (rgKeyBlocks.Count > 1)
1509                         {
1510                                 ArrayList rgKeyBlocksNew = new ArrayList ();
1511                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
1512                                 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1513                                 {
1514                                         KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1515                                         if ((kbCurr.Length + kb.Length) * 2 >=  KeyBlock.TotalLength (kbCurr, kb))
1516                                         {
1517                                                 // merge blocks
1518                                                 kbCurr.nLast = kb.nLast;
1519                                         }
1520                                         else
1521                                         {
1522                                                 // start a new block
1523                                                 rgKeyBlocksNew.Add (kbCurr);
1524                                                 kbCurr = kb;
1525                                         }
1526                                 }
1527                                 rgKeyBlocksNew.Add (kbCurr);
1528                                 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1529                                         break;
1530                                 rgKeyBlocks = rgKeyBlocksNew;
1531                         }
1532
1533                         // initialize the key lists
1534                         foreach (KeyBlock kb in rgKeyBlocks)
1535                                 kb.rgKeys = new ArrayList ();
1536
1537                         // fill the key lists
1538                         int iBlockCurr = 0;
1539                         kbCurr = (KeyBlock) rgKeyBlocks [0];
1540                         foreach (object key in rgKeys)
1541                         {
1542                                 bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1543                                 if (fNextBlock)
1544                                         kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1545                                 kbCurr.rgKeys.Add (key);
1546                         }
1547
1548                         // sort the blocks so we can tackle the largest ones first
1549                         rgKeyBlocks.Sort ();
1550
1551                         // okay now we can start...
1552                         ILGenerator ig = ec.ig;
1553                         Label lblEnd = ig.DefineLabel ();       // at the end ;-)
1554                         Label lblDefault = new Label ();
1555                         Type typeKeys = rgKeys [0].GetType ();  // used for conversions
1556
1557                         for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1558                         {
1559                                 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1560                                 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1561                                 if (kb.Length <= 2)
1562                                 {
1563                                         foreach (object key in kb.rgKeys)
1564                                         {
1565                                                 ig.Emit (OpCodes.Ldloc, val);
1566                                                 EmitObjectInteger (ig, key);
1567                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
1568                                                 ig.Emit (OpCodes.Beq, sl.ILLabel);
1569                                         }
1570                                 }
1571                                 else
1572                                 {
1573                                         // TODO: if all the keys in the block are the same and there are
1574                                         //       no gaps/defaults then just use a range-check.
1575                                         if (SwitchType == TypeManager.int64_type ||
1576                                                 SwitchType == TypeManager.uint64_type)
1577                                         {
1578                                                 // TODO: optimize constant/I4 cases
1579
1580                                                 // check block range (could be > 2^31)
1581                                                 ig.Emit (OpCodes.Ldloc, val);
1582                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1583                                                 ig.Emit (OpCodes.Blt, lblDefault);
1584                                                 ig.Emit (OpCodes.Ldloc, val);
1585                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1586                                                 ig.Emit (OpCodes.Bgt, lblDefault);
1587
1588                                                 // normalize range
1589                                                 ig.Emit (OpCodes.Ldloc, val);
1590                                                 if (kb.nFirst != 0)
1591                                                 {
1592                                                         EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1593                                                         ig.Emit (OpCodes.Sub);
1594                                                 }
1595                                                 ig.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
1596                                         }
1597                                         else
1598                                         {
1599                                                 // normalize range
1600                                                 ig.Emit (OpCodes.Ldloc, val);
1601                                                 int nFirst = (int) kb.nFirst;
1602                                                 if (nFirst > 0)
1603                                                 {
1604                                                         IntConstant.EmitInt (ig, nFirst);
1605                                                         ig.Emit (OpCodes.Sub);
1606                                                 }
1607                                                 else if (nFirst < 0)
1608                                                 {
1609                                                         IntConstant.EmitInt (ig, -nFirst);
1610                                                         ig.Emit (OpCodes.Add);
1611                                                 }
1612                                         }
1613
1614                                         // first, build the list of labels for the switch
1615                                         int iKey = 0;
1616                                         int cJumps = kb.Length;
1617                                         Label [] rgLabels = new Label [cJumps];
1618                                         for (int iJump = 0; iJump < cJumps; iJump++)
1619                                         {
1620                                                 object key = kb.rgKeys [iKey];
1621                                                 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1622                                                 {
1623                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
1624                                                         rgLabels [iJump] = sl.ILLabel;
1625                                                         iKey++;
1626                                                 }
1627                                                 else
1628                                                         rgLabels [iJump] = lblDefault;
1629                                         }
1630                                         // emit the switch opcode
1631                                         ig.Emit (OpCodes.Switch, rgLabels);
1632                                 }
1633
1634                                 // mark the default for this block
1635                                 if (iBlock != 0)
1636                                         ig.MarkLabel (lblDefault);
1637                         }
1638
1639                         // TODO: find the default case and emit it here,
1640                         //       to prevent having to do the following jump.
1641                         //       make sure to mark other labels in the default section
1642
1643                         // the last default just goes to the end
1644                         ig.Emit (OpCodes.Br, lblDefault);
1645
1646                         // now emit the code for the sections
1647                         bool fFoundDefault = false;
1648                         bool fAllReturn = true;
1649                         foreach (SwitchSection ss in Sections)
1650                         {
1651                                 foreach (SwitchLabel sl in ss.Labels)
1652                                 {
1653                                         ig.MarkLabel (sl.ILLabel);
1654                                         ig.MarkLabel (sl.ILLabelCode);
1655                                         if (sl.Label == null)
1656                                         {
1657                                                 ig.MarkLabel (lblDefault);
1658                                                 fFoundDefault = true;
1659                                         }
1660                                 }
1661                                 fAllReturn &= ss.Block.Emit (ec);
1662                                 //ig.Emit (OpCodes.Br, lblEnd);
1663                         }
1664                         
1665                         if (!fFoundDefault)
1666                                 ig.MarkLabel (lblDefault);
1667                         ig.MarkLabel (lblEnd);
1668
1669                         return fAllReturn;
1670                 }
1671                 //
1672                 // This simple emit switch works, but does not take advantage of the
1673                 // `switch' opcode. 
1674                 // TODO: remove non-string logic from here
1675                 // TODO: binary search strings?
1676                 //
1677                 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1678                 {
1679                         ILGenerator ig = ec.ig;
1680                         Label end_of_switch = ig.DefineLabel ();
1681                         Label next_test = ig.DefineLabel ();
1682                         Label null_target = ig.DefineLabel ();
1683                         bool default_found = false;
1684                         bool first_test = true;
1685                         bool pending_goto_end = false;
1686                         bool all_return = true;
1687                         bool is_string = false;
1688                         bool null_found;
1689                         
1690                         //
1691                         // Special processing for strings: we cant compare
1692                         // against null.
1693                         //
1694                         if (SwitchType == TypeManager.string_type){
1695                                 ig.Emit (OpCodes.Ldloc, val);
1696                                 is_string = true;
1697                                 
1698                                 if (Elements.Contains (NullLiteral.Null)){
1699                                         ig.Emit (OpCodes.Brfalse, null_target);
1700                                 } else
1701                                         ig.Emit (OpCodes.Brfalse, default_target);
1702
1703                                 ig.Emit (OpCodes.Ldloc, val);
1704                                 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1705                                 ig.Emit (OpCodes.Stloc, val);
1706                         }
1707
1708                         SwitchSection last_section;
1709                         last_section = (SwitchSection) Sections [Sections.Count-1];
1710                         
1711                         foreach (SwitchSection ss in Sections){
1712                                 Label sec_begin = ig.DefineLabel ();
1713
1714                                 if (pending_goto_end)
1715                                         ig.Emit (OpCodes.Br, end_of_switch);
1716
1717                                 int label_count = ss.Labels.Count;
1718                                 null_found = false;
1719                                 foreach (SwitchLabel sl in ss.Labels){
1720                                         ig.MarkLabel (sl.ILLabel);
1721                                         
1722                                         if (!first_test){
1723                                                 ig.MarkLabel (next_test);
1724                                                 next_test = ig.DefineLabel ();
1725                                         }
1726                                         //
1727                                         // If we are the default target
1728                                         //
1729                                         if (sl.Label == null){
1730                                                 ig.MarkLabel (default_target);
1731                                                 default_found = true;
1732                                         } else {
1733                                                 object lit = sl.Converted;
1734
1735                                                 if (lit is NullLiteral){
1736                                                         null_found = true;
1737                                                         if (label_count == 1)
1738                                                                 ig.Emit (OpCodes.Br, next_test);
1739                                                         continue;
1740                                                                               
1741                                                 }
1742                                                 if (is_string){
1743                                                         StringConstant str = (StringConstant) lit;
1744
1745                                                         ig.Emit (OpCodes.Ldloc, val);
1746                                                         ig.Emit (OpCodes.Ldstr, str.Value);
1747                                                         if (label_count == 1)
1748                                                                 ig.Emit (OpCodes.Bne_Un, next_test);
1749                                                         else
1750                                                                 ig.Emit (OpCodes.Beq, sec_begin);
1751                                                 } else {
1752                                                         ig.Emit (OpCodes.Ldloc, val);
1753                                                         EmitObjectInteger (ig, lit);
1754                                                         ig.Emit (OpCodes.Ceq);
1755                                                         if (label_count == 1)
1756                                                                 ig.Emit (OpCodes.Brfalse, next_test);
1757                                                         else
1758                                                                 ig.Emit (OpCodes.Brtrue, sec_begin);
1759                                                 }
1760                                         }
1761                                 }
1762                                 if (label_count != 1 && ss != last_section)
1763                                         ig.Emit (OpCodes.Br, next_test);
1764                                 
1765                                 if (null_found)
1766                                         ig.MarkLabel (null_target);
1767                                 ig.MarkLabel (sec_begin);
1768                                 foreach (SwitchLabel sl in ss.Labels)\r
1769                                         ig.MarkLabel (sl.ILLabelCode);
1770                                 if (ss.Block.Emit (ec))
1771                                         pending_goto_end = false;
1772                                 else {
1773                                         all_return = false;
1774                                         pending_goto_end = true;
1775                                 }
1776                                 first_test = false;
1777                         }
1778                         if (!default_found){
1779                                 ig.MarkLabel (default_target);
1780                                 all_return = false;
1781                         }
1782                         ig.MarkLabel (next_test);
1783                         ig.MarkLabel (end_of_switch);
1784                         
1785                         return all_return;
1786                 }
1787                 
1788                 public override bool Emit (EmitContext ec)
1789                 {
1790                         Expr = Expr.Resolve (ec);
1791                         if (Expr == null)
1792                                 return false;
1793
1794                         Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1795                         if (new_expr == null){
1796                                 Report.Error (151, loc, "An integer type or string was expected for switch");
1797                                 return false;
1798                         }
1799
1800                         // Validate switch.
1801                         SwitchType = new_expr.Type;
1802
1803                         if (!CheckSwitch (ec))
1804                                 return false;
1805
1806                         // Store variable for comparission purposes
1807                         LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1808                         new_expr.Emit (ec);
1809                         ec.ig.Emit (OpCodes.Stloc, value);
1810
1811                         ILGenerator ig = ec.ig;
1812
1813                         default_target = ig.DefineLabel ();
1814
1815                         //
1816                         // Setup the codegen context
1817                         //
1818                         Label old_end = ec.LoopEnd;
1819                         Switch old_switch = ec.Switch;
1820                         
1821                         ec.LoopEnd = ig.DefineLabel ();
1822                         ec.Switch = this;
1823
1824                         // Emit Code.
1825                         bool all_return;
1826                         if (SwitchType == TypeManager.string_type)
1827                                 all_return = SimpleSwitchEmit (ec, value);
1828                         else
1829                                 all_return = TableSwitchEmit (ec, value);
1830
1831                         // Restore context state. 
1832                         ig.MarkLabel (ec.LoopEnd);
1833
1834                         //
1835                         // Restore the previous context
1836                         //
1837                         ec.LoopEnd = old_end;
1838                         ec.Switch = old_switch;
1839                         
1840                         return all_return;
1841                 }
1842         }
1843
1844         public class Lock : Statement {
1845                 public readonly Expression Expr;
1846                 public readonly Statement Statement;
1847                         
1848                 public Lock (Expression expr, Statement stmt, Location l)
1849                 {
1850                         Expr = expr;
1851                         Statement = stmt;
1852                         loc = l;
1853                 }
1854
1855                 public override bool Emit (EmitContext ec)
1856                 {
1857                         Expression e = Expr.Resolve (ec);
1858                         if (e == null)
1859                                 return false;
1860
1861                         Type type = e.Type;
1862                         
1863                         if (type.IsValueType){
1864                                 Report.Error (185, loc, "lock statement requires the expression to be " +
1865                                               " a reference type (type is: `" +
1866                                               TypeManager.CSharpName (type) + "'");
1867                                 return false;
1868                         }
1869
1870                         ILGenerator ig = ec.ig;
1871                         LocalBuilder temp = ig.DeclareLocal (type);
1872                                 
1873                         e.Emit (ec);
1874                         ig.Emit (OpCodes.Dup);
1875                         ig.Emit (OpCodes.Stloc, temp);
1876                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1877
1878                         // try
1879                         Label end = ig.BeginExceptionBlock ();
1880                         bool old_in_try = ec.InTry;
1881                         ec.InTry = true;
1882                         Label finish = ig.DefineLabel ();
1883                         Statement.Emit (ec);
1884                         ec.InTry = old_in_try;
1885                         // ig.Emit (OpCodes.Leave, finish);
1886
1887                         ig.MarkLabel (finish);
1888                         
1889                         // finally
1890                         ig.BeginFinallyBlock ();
1891                         ig.Emit (OpCodes.Ldloc, temp);
1892                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1893                         ig.EndExceptionBlock ();
1894                         
1895                         return false;
1896                 }
1897         }
1898
1899         public class Unchecked : Statement {
1900                 public readonly Block Block;
1901                 
1902                 public Unchecked (Block b)
1903                 {
1904                         Block = b;
1905                 }
1906
1907                 public override bool Emit (EmitContext ec)
1908                 {
1909                         bool previous_state = ec.CheckState;
1910                         bool previous_state_const = ec.ConstantCheckState;
1911                         bool val;
1912                         
1913                         ec.CheckState = false;
1914                         ec.ConstantCheckState = false;
1915                         val = Block.Emit (ec);
1916                         ec.CheckState = previous_state;
1917                         ec.ConstantCheckState = previous_state_const;
1918
1919                         return val;
1920                 }
1921         }
1922
1923         public class Checked : Statement {
1924                 public readonly Block Block;
1925                 
1926                 public Checked (Block b)
1927                 {
1928                         Block = b;
1929                 }
1930
1931                 public override bool Emit (EmitContext ec)
1932                 {
1933                         bool previous_state = ec.CheckState;
1934                         bool previous_state_const = ec.ConstantCheckState;
1935                         bool val;
1936                         
1937                         ec.CheckState = true;
1938                         ec.ConstantCheckState = true;
1939                         val = Block.Emit (ec);
1940                         ec.CheckState = previous_state;
1941                         ec.ConstantCheckState = previous_state_const;
1942
1943                         return val;
1944                 }
1945         }
1946
1947         public class Unsafe : Statement {
1948                 public readonly Block Block;
1949
1950                 public Unsafe (Block b)
1951                 {
1952                         Block = b;
1953                 }
1954
1955                 public override bool Emit (EmitContext ec)
1956                 {
1957                         bool previous_state = ec.InUnsafe;
1958                         bool val;
1959                         
1960                         ec.InUnsafe = true;
1961                         val = Block.Emit (ec);
1962                         ec.InUnsafe = previous_state;
1963
1964                         return val;
1965                 }
1966         }
1967
1968         // 
1969         // Fixed statement
1970         //
1971         public class Fixed : Statement {
1972                 string    type;
1973                 ArrayList declarators;
1974                 Statement statement;
1975
1976                 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
1977                 {
1978                         this.type = type;
1979                         declarators = decls;
1980                         statement = stmt;
1981                         loc = l;
1982                 }
1983
1984                 public override bool Emit (EmitContext ec)
1985                 {
1986                         ILGenerator ig = ec.ig;
1987                         Type t;
1988                         
1989                         t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
1990                         if (t == null)
1991                                 return false;
1992
1993                         foreach (Pair p in declarators){
1994                                 VariableInfo vi = (VariableInfo) p.First;
1995                                 Expression e = (Expression) p.Second;
1996
1997                                 //
1998                                 // The rules for the possible declarators are pretty wise,
1999                                 // but the production on the grammar is more concise.
2000                                 //
2001                                 // So we have to enforce these rules here.
2002                                 //
2003                                 // We do not resolve before doing the case 1 test,
2004                                 // because the grammar is explicit in that the token &
2005                                 // is present, so we need to test for this particular case.
2006                                 //
2007
2008                                 //
2009                                 // Case 1: & object.
2010                                 //
2011                                 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2012                                         Expression child = ((Unary) e).Expr;
2013
2014                                         vi.MakePinned ();
2015                                         if (child is ParameterReference || child is LocalVariableReference){
2016                                                 Report.Error (
2017                                                         213, loc, 
2018                                                         "No need to use fixed statement for parameters or " +
2019                                                         "local variable declarations (address is already " +
2020                                                         "fixed)");
2021                                                 continue;
2022                                         }
2023                                         
2024                                         e = e.Resolve (ec);
2025                                         if (e == null)
2026                                                 continue;
2027
2028                                         child = ((Unary) e).Expr;
2029                                         
2030                                         if (!TypeManager.VerifyUnManaged (child.Type, loc))
2031                                                 continue;
2032
2033                                         //
2034                                         // Store pointer in pinned location
2035                                         //
2036                                         e.Emit (ec);
2037                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2038
2039                                         statement.Emit (ec);
2040
2041                                         // Clear the pinned variable.
2042                                         ig.Emit (OpCodes.Ldc_I4_0);
2043                                         ig.Emit (OpCodes.Conv_U);
2044                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2045
2046                                         continue;
2047                                 }
2048
2049                                 e = e.Resolve (ec);
2050                                 if (e == null)
2051                                         continue;
2052
2053                                 //
2054                                 // Case 2: Array
2055                                 //
2056                                 if (e.Type.IsArray){
2057                                         Type array_type = e.Type.GetElementType ();
2058                                         
2059                                         vi.MakePinned ();
2060                                         //
2061                                         // Provided that array_type is unmanaged,
2062                                         //
2063                                         if (!TypeManager.VerifyUnManaged (array_type, loc))
2064                                                 continue;
2065
2066                                         //
2067                                         // and T* is implicitly convertible to the
2068                                         // pointer type given in the fixed statement.
2069                                         //
2070                                         ArrayPtr array_ptr = new ArrayPtr (e);
2071                                         
2072                                         Expression converted = Expression.ConvertImplicitRequired (
2073                                                 ec, array_ptr, vi.VariableType, loc);
2074                                         if (converted == null)
2075                                                 continue;
2076
2077                                         //
2078                                         // Store pointer in pinned location
2079                                         //
2080                                         converted.Emit (ec);
2081                                         
2082                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2083
2084                                         statement.Emit (ec);
2085                                         
2086                                         // Clear the pinned variable.
2087                                         ig.Emit (OpCodes.Ldc_I4_0);
2088                                         ig.Emit (OpCodes.Conv_U);
2089                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2090
2091                                         continue;
2092                                 }
2093
2094                                 //
2095                                 // Case 3: string
2096                                 //
2097                                 if (e.Type == TypeManager.string_type){
2098                                         LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2099                                         TypeManager.MakePinned (pinned_string);
2100                                         
2101                                         e.Emit (ec);
2102                                         ig.Emit (OpCodes.Stloc, pinned_string);
2103
2104                                         Expression sptr = new StringPtr (pinned_string);
2105                                         Expression converted = Expression.ConvertImplicitRequired (
2106                                                 ec, sptr, vi.VariableType, loc);
2107                                         
2108                                         if (converted == null)
2109                                                 continue;
2110
2111                                         converted.Emit (ec);
2112                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2113                                         
2114                                         statement.Emit (ec);
2115
2116                                         // Clear the pinned variable
2117                                         ig.Emit (OpCodes.Ldnull);
2118                                         ig.Emit (OpCodes.Stloc, pinned_string);
2119                                 }
2120                         }
2121
2122                         return false;
2123                 }
2124         }
2125         
2126         public class Catch {
2127                 public readonly string Type;
2128                 public readonly string Name;
2129                 public readonly Block  Block;
2130                 public readonly Location Location;
2131                 
2132                 public Catch (string type, string name, Block block, Location l)
2133                 {
2134                         Type = type;
2135                         Name = name;
2136                         Block = block;
2137                         Location = l;
2138                 }
2139         }
2140
2141         public class Try : Statement {
2142                 public readonly Block Fini, Block;
2143                 public readonly ArrayList Specific;
2144                 public readonly Catch General;
2145                 
2146                 //
2147                 // specific, general and fini might all be null.
2148                 //
2149                 public Try (Block block, ArrayList specific, Catch general, Block fini)
2150                 {
2151                         if (specific == null && general == null){
2152                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2153                         }
2154                         
2155                         this.Block = block;
2156                         this.Specific = specific;
2157                         this.General = general;
2158                         this.Fini = fini;
2159                 }
2160
2161                 public override bool Emit (EmitContext ec)
2162                 {
2163                         ILGenerator ig = ec.ig;
2164                         Label end;
2165                         Label finish = ig.DefineLabel ();;
2166                         bool returns;
2167                         
2168                         end = ig.BeginExceptionBlock ();
2169                         bool old_in_try = ec.InTry;
2170                         ec.InTry = true;
2171                         returns = Block.Emit (ec);
2172                         ec.InTry = old_in_try;
2173
2174                         //
2175                         // System.Reflection.Emit provides this automatically:
2176                         // ig.Emit (OpCodes.Leave, finish);
2177
2178                         bool old_in_catch = ec.InCatch;
2179                         ec.InCatch = true;
2180                         DeclSpace ds = ec.DeclSpace;
2181
2182                         foreach (Catch c in Specific){
2183                                 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2184                                 VariableInfo vi;
2185                                 
2186                                 if (catch_type == null)
2187                                         return false;
2188
2189                                 ig.BeginCatchBlock (catch_type);
2190
2191                                 if (c.Name != null){
2192                                         vi = c.Block.GetVariableInfo (c.Name);
2193                                         if (vi == null){
2194                                                 Console.WriteLine ("This should not happen! variable does not exist in this block");
2195                                                 Environment.Exit (0);
2196                                         }
2197                                 
2198                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2199                                 } else
2200                                         ig.Emit (OpCodes.Pop);
2201                                 
2202                                 if (!c.Block.Emit (ec))
2203                                         returns = false;
2204                         }
2205
2206                         if (General != null){
2207                                 ig.BeginCatchBlock (TypeManager.object_type);
2208                                 ig.Emit (OpCodes.Pop);
2209                                 if (!General.Block.Emit (ec))
2210                                         returns = false;
2211                         }
2212                         ec.InCatch = old_in_catch;
2213
2214                         ig.MarkLabel (finish);
2215                         if (Fini != null){
2216                                 ig.BeginFinallyBlock ();
2217                                 bool old_in_finally = ec.InFinally;
2218                                 ec.InFinally = true;
2219                                 Fini.Emit (ec);
2220                                 ec.InFinally = old_in_finally;
2221                         }
2222                         
2223                         ig.EndExceptionBlock ();
2224
2225                         //
2226                         // FIXME: Is this correct?
2227                         // Replace with `returns' and check test-18, maybe we can
2228                         // perform an optimization here.
2229                         //
2230                         return returns;
2231                 }
2232         }
2233
2234         //
2235         // FIXME: We still do not support the expression variant of the using
2236         // statement.
2237         //
2238         public class Using : Statement {
2239                 object expression_or_block;
2240                 Statement Statement;
2241                 
2242                 public Using (object expression_or_block, Statement stmt, Location l)
2243                 {
2244                         this.expression_or_block = expression_or_block;
2245                         Statement = stmt;
2246                         loc = l;
2247                 }
2248
2249                 //
2250                 // Emits the code for the case of using using a local variable declaration.
2251                 //
2252                 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2253                 {
2254                         ILGenerator ig = ec.ig;
2255                         Expression [] converted_vars;
2256                         bool need_conv = false;
2257                         Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2258                         int i = 0;
2259
2260                         if (type == null)
2261                                 return false;
2262                         
2263                         //
2264                         // The type must be an IDisposable or an implicit conversion
2265                         // must exist.
2266                         //
2267                         converted_vars = new Expression [var_list.Count];
2268                         if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2269                                 foreach (DictionaryEntry e in var_list){
2270                                         Expression var = (Expression) e.Key;
2271
2272                                         var = var.Resolve (ec);
2273                                         if (var == null)
2274                                                 return false;
2275                                         
2276                                         converted_vars [i] = Expression.ConvertImplicit (
2277                                                 ec, var, TypeManager.idisposable_type, loc);
2278
2279                                         if (converted_vars [i] == null)
2280                                                 return false;
2281                                         i++;
2282                                 }
2283                                 need_conv = true;
2284                         }
2285                         
2286                         i = 0;
2287                         bool old_in_try = ec.InTry;
2288                         ec.InTry = true;
2289                         foreach (DictionaryEntry e in var_list){
2290                                 LocalVariableReference var = (LocalVariableReference) e.Key;
2291                                 Expression expr = (Expression) e.Value;
2292                                 Expression a;
2293
2294                                 a = new Assign (var, expr, loc);
2295                                 a.Resolve (ec);
2296                                 if (!need_conv)
2297                                         converted_vars [i] = var;
2298                                 i++;
2299                                 if (a == null)
2300                                         continue;
2301                                 ((ExpressionStatement) a).EmitStatement (ec);
2302                                 
2303                                 ig.BeginExceptionBlock ();
2304
2305                         }
2306                         Statement.Emit (ec);
2307                         ec.InTry = old_in_try;
2308
2309                         bool old_in_finally = ec.InFinally;
2310                         ec.InFinally = true;
2311                         var_list.Reverse ();
2312                         foreach (DictionaryEntry e in var_list){
2313                                 LocalVariableReference var = (LocalVariableReference) e.Key;
2314                                 Label skip = ig.DefineLabel ();
2315                                 i--;
2316                                 
2317                                 ig.BeginFinallyBlock ();
2318                                 
2319                                 var.Emit (ec);
2320                                 ig.Emit (OpCodes.Brfalse, skip);
2321                                 converted_vars [i].Emit (ec);
2322                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2323                                 ig.MarkLabel (skip);
2324                                 ig.EndExceptionBlock ();
2325                         }
2326                         ec.InFinally = old_in_finally;
2327
2328                         return false;
2329                 }
2330
2331                 bool EmitExpression (EmitContext ec, Expression expr)
2332                 {
2333                         Type expr_type = expr.Type;
2334                         Expression conv = null;
2335                         
2336                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2337                                 conv = Expression.ConvertImplicit (
2338                                         ec, expr, TypeManager.idisposable_type, loc);
2339
2340                                 if (conv == null)
2341                                         return false;
2342                         }
2343
2344                         //
2345                         // Make a copy of the expression and operate on that.
2346                         //
2347                         ILGenerator ig = ec.ig;
2348                         LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2349                         if (conv != null)
2350                                 conv.Emit (ec);
2351                         else
2352                                 expr.Emit (ec);
2353                         ig.Emit (OpCodes.Stloc, local_copy);
2354
2355                         bool old_in_try = ec.InTry;
2356                         ec.InTry = true;
2357                         ig.BeginExceptionBlock ();
2358                         Statement.Emit (ec);
2359                         ec.InTry = old_in_try;
2360                         
2361                         Label skip = ig.DefineLabel ();
2362                         bool old_in_finally = ec.InFinally;
2363                         ig.BeginFinallyBlock ();
2364                         ig.Emit (OpCodes.Ldloc, local_copy);
2365                         ig.Emit (OpCodes.Brfalse, skip);
2366                         ig.Emit (OpCodes.Ldloc, local_copy);
2367                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2368                         ig.MarkLabel (skip);
2369                         ec.InFinally = old_in_finally;
2370                         ig.EndExceptionBlock ();
2371
2372                         return false;
2373                 }
2374                 
2375                 public override bool Emit (EmitContext ec)
2376                 {
2377                         if (expression_or_block is DictionaryEntry){
2378                                 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2379                                 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2380
2381                                 return EmitLocalVariableDecls (ec, t, var_list);
2382                         } if (expression_or_block is Expression){
2383                                 Expression e = (Expression) expression_or_block;
2384
2385                                 e = e.Resolve (ec);
2386                                 if (e == null)
2387                                         return false;
2388
2389                                 return EmitExpression (ec, e);
2390                         }
2391                         return false;
2392                 }
2393         }
2394
2395         /// <summary>
2396         ///   Implementation of the foreach C# statement
2397         /// </summary>
2398         public class Foreach : Statement {
2399                 string type;
2400                 LocalVariableReference variable;
2401                 Expression expr;
2402                 Statement statement;
2403                 
2404                 public Foreach (string type, LocalVariableReference var, Expression expr,
2405                                 Statement stmt, Location l)
2406                 {
2407                         this.type = type;
2408                         this.variable = var;
2409                         this.expr = expr;
2410                         statement = stmt;
2411                         loc = l;
2412                 }
2413                 
2414                 //
2415                 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2416                 //
2417                 static MethodInfo FetchMethodMoveNext (Type t)
2418                 {
2419                         MemberInfo [] move_next_list;
2420                         
2421                         move_next_list = TypeContainer.FindMembers (
2422                                 t, MemberTypes.Method,
2423                                 BindingFlags.Public | BindingFlags.Instance,
2424                                 Type.FilterName, "MoveNext");
2425                         if (move_next_list == null || move_next_list.Length == 0)
2426                                 return null;
2427
2428                         foreach (MemberInfo m in move_next_list){
2429                                 MethodInfo mi = (MethodInfo) m;
2430                                 Type [] args;
2431                                 
2432                                 args = TypeManager.GetArgumentTypes (mi);
2433                                 if (args != null && args.Length == 0){
2434                                         if (mi.ReturnType == TypeManager.bool_type)
2435                                                 return mi;
2436                                 }
2437                         }
2438                         return null;
2439                 }
2440                 
2441                 //
2442                 // Retrieves a `public T get_Current ()' method from the Type `t'
2443                 //
2444                 static MethodInfo FetchMethodGetCurrent (Type t)
2445                 {
2446                         MemberInfo [] move_next_list;
2447                         
2448                         move_next_list = TypeContainer.FindMembers (
2449                                 t, MemberTypes.Method,
2450                                 BindingFlags.Public | BindingFlags.Instance,
2451                                 Type.FilterName, "get_Current");
2452                         if (move_next_list == null || move_next_list.Length == 0)
2453                                 return null;
2454
2455                         foreach (MemberInfo m in move_next_list){
2456                                 MethodInfo mi = (MethodInfo) m;
2457                                 Type [] args;
2458                                 
2459                                 args = TypeManager.GetArgumentTypes (mi);
2460                                 if (args != null && args.Length == 0)
2461                                         return mi;
2462                         }
2463                         return null;
2464                 }
2465
2466                 // 
2467                 // This struct records the helper methods used by the Foreach construct
2468                 //
2469                 class ForeachHelperMethods {
2470                         public EmitContext ec;
2471                         public MethodInfo get_enumerator;
2472                         public MethodInfo move_next;
2473                         public MethodInfo get_current;
2474
2475                         public ForeachHelperMethods (EmitContext ec)
2476                         {
2477                                 this.ec = ec;
2478                         }
2479                 }
2480                 
2481                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2482                 {
2483                         if (m == null)
2484                                 return false;
2485                         
2486                         if (!(m is MethodInfo))
2487                                 return false;
2488                         
2489                         if (m.Name != "GetEnumerator")
2490                                 return false;
2491
2492                         MethodInfo mi = (MethodInfo) m;
2493                         Type [] args = TypeManager.GetArgumentTypes (mi);
2494                         if (args != null){
2495                                 if (args.Length != 0)
2496                                         return false;
2497                         }
2498                         ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2499                         EmitContext ec = hm.ec;
2500                         
2501                         //
2502                         // Check whether GetEnumerator is accessible to us
2503                         //
2504                         MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2505
2506                         Type declaring = mi.DeclaringType;
2507                         if (prot == MethodAttributes.Private){
2508                                 if (declaring != ec.ContainerType)
2509                                         return false;
2510                         } else if (prot == MethodAttributes.FamANDAssem){
2511                                 // If from a different assembly, false
2512                                 if (!(mi is MethodBuilder))
2513                                         return false;
2514                                 //
2515                                 // Are we being invoked from the same class, or from a derived method?
2516                                 //
2517                                 if (ec.ContainerType != declaring){
2518                                         if (!ec.ContainerType.IsSubclassOf (declaring))
2519                                                 return false;
2520                                 }
2521                         } else if (prot == MethodAttributes.FamORAssem){
2522                                 if (!(mi is MethodBuilder ||
2523                                       ec.ContainerType == declaring ||
2524                                       ec.ContainerType.IsSubclassOf (declaring)))
2525                                         return false;
2526                         } if (prot == MethodAttributes.Family){
2527                                 if (!(ec.ContainerType == declaring ||
2528                                       ec.ContainerType.IsSubclassOf (declaring)))
2529                                         return false;
2530                         }
2531
2532                         //
2533                         // Ok, we can access it, now make sure that we can do something
2534                         // with this `GetEnumerator'
2535                         //
2536                         if (mi.ReturnType == TypeManager.ienumerator_type || 
2537                             TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2538                                 hm.move_next = TypeManager.bool_movenext_void;
2539                                 hm.get_current = TypeManager.object_getcurrent_void;
2540                                 return true;
2541                         }
2542
2543                         //
2544                         // Ok, so they dont return an IEnumerable, we will have to
2545                         // find if they support the GetEnumerator pattern.
2546                         //
2547                         Type return_type = mi.ReturnType;
2548
2549                         hm.move_next = FetchMethodMoveNext (return_type);
2550                         if (hm.move_next == null)
2551                                 return false;
2552                         hm.get_current = FetchMethodGetCurrent (return_type);
2553                         if (hm.get_current == null)
2554                                 return false;
2555
2556                         return true;
2557                 }
2558                 
2559                 /// <summary>
2560                 ///   This filter is used to find the GetEnumerator method
2561                 ///   on which IEnumerator operates
2562                 /// </summary>
2563                 static MemberFilter FilterEnumerator;
2564                 
2565                 static Foreach ()
2566                 {
2567                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2568                 }
2569
2570                 void error1579 (Type t)
2571                 {
2572                         Report.Error (1579, loc,
2573                                       "foreach statement cannot operate on variables of type `" +
2574                                       t.FullName + "' because that class does not provide a " +
2575                                       " GetEnumerator method or it is inaccessible");
2576                 }
2577
2578                 static bool TryType (Type t, ForeachHelperMethods hm)
2579                 {
2580                         MemberInfo [] mi;
2581                         
2582                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2583                                                         BindingFlags.Public | BindingFlags.NonPublic |
2584                                                         BindingFlags.Instance,
2585                                                         FilterEnumerator, hm);
2586
2587                         if (mi == null || mi.Length == 0)
2588                                 return false;
2589
2590                         hm.get_enumerator = (MethodInfo) mi [0];
2591                         return true;    
2592                 }
2593                 
2594                 //
2595                 // Looks for a usable GetEnumerator in the Type, and if found returns
2596                 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2597                 //
2598                 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2599                 {
2600                         ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2601
2602                         if (TryType (t, hm))
2603                                 return hm;
2604
2605                         //
2606                         // Now try to find the method in the interfaces
2607                         //
2608                         while (t != null){
2609                                 Type [] ifaces = t.GetInterfaces ();
2610
2611                                 foreach (Type i in ifaces){
2612                                         if (TryType (i, hm))
2613                                                 return hm;
2614                                 }
2615                                 
2616                                 //
2617                                 // Since TypeBuilder.GetInterfaces only returns the interface
2618                                 // types for this type, we have to keep looping, but once
2619                                 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2620                                 // done, because it returns all the types
2621                                 //
2622                                 if ((t is TypeBuilder))
2623                                         t = t.BaseType;
2624                                 else
2625                                         break;
2626                         } 
2627
2628                         return null;
2629                 }
2630
2631                 //
2632                 // FIXME: possible optimization.
2633                 // We might be able to avoid creating `empty' if the type is the sam
2634                 //
2635                 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2636                 {
2637                         ILGenerator ig = ec.ig;
2638                         LocalBuilder enumerator, disposable;
2639                         Expression empty = new EmptyExpression ();
2640                         Expression conv;
2641
2642                         //
2643                         // FIXME: maybe we can apply the same trick we do in the
2644                         // array handling to avoid creating empty and conv in some cases.
2645                         //
2646                         // Although it is not as important in this case, as the type
2647                         // will not likely be object (what the enumerator will return).
2648                         //
2649                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2650                         if (conv == null)
2651                                 return false;
2652                         
2653                         enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2654                         disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2655                         
2656                         //
2657                         // Instantiate the enumerator
2658                         //
2659                         if (expr.Type.IsValueType){
2660                                 if (expr is IMemoryLocation){
2661                                         IMemoryLocation ml = (IMemoryLocation) expr;
2662
2663                                         ml.AddressOf (ec, AddressOp.Load);
2664                                 } else
2665                                         throw new Exception ("Expr " + expr + " of type " + expr.Type +
2666                                                              " does not implement IMemoryLocation");
2667                                 ig.Emit (OpCodes.Call, hm.get_enumerator);
2668                         } else {
2669                                 expr.Emit (ec);
2670                                 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2671                         }
2672                         ig.Emit (OpCodes.Stloc, enumerator);
2673
2674                         //
2675                         // Protect the code in a try/finalize block, so that
2676                         // if the beast implement IDisposable, we get rid of it
2677                         //
2678                         Label l = ig.BeginExceptionBlock ();
2679                         bool old_in_try = ec.InTry;
2680                         ec.InTry = true;
2681                         
2682                         Label end_try = ig.DefineLabel ();
2683                         
2684                         ig.MarkLabel (ec.LoopBegin);
2685                         ig.Emit (OpCodes.Ldloc, enumerator);
2686                         ig.Emit (OpCodes.Callvirt, hm.move_next);
2687                         ig.Emit (OpCodes.Brfalse, end_try);
2688                         ig.Emit (OpCodes.Ldloc, enumerator);
2689                         ig.Emit (OpCodes.Callvirt, hm.get_current);
2690                         variable.EmitAssign (ec, conv);
2691                         statement.Emit (ec);
2692                         ig.Emit (OpCodes.Br, ec.LoopBegin);
2693                         ig.MarkLabel (end_try);
2694                         ec.InTry = old_in_try;
2695                         
2696                         // The runtime provides this for us.
2697                         // ig.Emit (OpCodes.Leave, end);
2698
2699                         //
2700                         // Now the finally block
2701                         //
2702                         Label end_finally = ig.DefineLabel ();
2703                         bool old_in_finally = ec.InFinally;
2704                         ec.InFinally = true;
2705                         ig.BeginFinallyBlock ();
2706                         
2707                         ig.Emit (OpCodes.Ldloc, enumerator);
2708                         ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2709                         ig.Emit (OpCodes.Stloc, disposable);
2710                         ig.Emit (OpCodes.Ldloc, disposable);
2711                         ig.Emit (OpCodes.Brfalse, end_finally);
2712                         ig.Emit (OpCodes.Ldloc, disposable);
2713                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2714                         ig.MarkLabel (end_finally);
2715                         ec.InFinally = old_in_finally;
2716
2717                         // The runtime generates this anyways.
2718                         // ig.Emit (OpCodes.Endfinally);
2719
2720                         ig.EndExceptionBlock ();
2721
2722                         ig.MarkLabel (ec.LoopEnd);
2723                         return false;
2724                 }
2725
2726                 //
2727                 // FIXME: possible optimization.
2728                 // We might be able to avoid creating `empty' if the type is the sam
2729                 //
2730                 bool EmitArrayForeach (EmitContext ec, Type var_type)
2731                 {
2732                         Type array_type = expr.Type;
2733                         Type element_type = array_type.GetElementType ();
2734                         Expression conv = null;
2735                         Expression empty = new EmptyExpression (element_type);
2736                         
2737                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2738                         if (conv == null)
2739                                 return false;
2740
2741                         int rank = array_type.GetArrayRank ();
2742                         ILGenerator ig = ec.ig;
2743
2744                         LocalBuilder copy = ig.DeclareLocal (array_type);
2745                         
2746                         //
2747                         // Make our copy of the array
2748                         //
2749                         expr.Emit (ec);
2750                         ig.Emit (OpCodes.Stloc, copy);
2751                         
2752                         if (rank == 1){
2753                                 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2754
2755                                 Label loop, test;
2756                                 
2757                                 ig.Emit (OpCodes.Ldc_I4_0);
2758                                 ig.Emit (OpCodes.Stloc, counter);
2759                                 test = ig.DefineLabel ();
2760                                 ig.Emit (OpCodes.Br, test);
2761
2762                                 loop = ig.DefineLabel ();
2763                                 ig.MarkLabel (loop);
2764
2765                                 ig.Emit (OpCodes.Ldloc, copy);
2766                                 ig.Emit (OpCodes.Ldloc, counter);
2767                                 ArrayAccess.EmitLoadOpcode (ig, var_type);
2768
2769                                 variable.EmitAssign (ec, conv);
2770
2771                                 statement.Emit (ec);
2772
2773                                 ig.MarkLabel (ec.LoopBegin);
2774                                 ig.Emit (OpCodes.Ldloc, counter);
2775                                 ig.Emit (OpCodes.Ldc_I4_1);
2776                                 ig.Emit (OpCodes.Add);
2777                                 ig.Emit (OpCodes.Stloc, counter);
2778
2779                                 ig.MarkLabel (test);
2780                                 ig.Emit (OpCodes.Ldloc, counter);
2781                                 ig.Emit (OpCodes.Ldloc, copy);
2782                                 ig.Emit (OpCodes.Ldlen);
2783                                 ig.Emit (OpCodes.Conv_I4);
2784                                 ig.Emit (OpCodes.Blt, loop);
2785                         } else {
2786                                 LocalBuilder [] dim_len   = new LocalBuilder [rank];
2787                                 LocalBuilder [] dim_count = new LocalBuilder [rank];
2788                                 Label [] loop = new Label [rank];
2789                                 Label [] test = new Label [rank];
2790                                 int dim;
2791                                 
2792                                 for (dim = 0; dim < rank; dim++){
2793                                         dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2794                                         dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2795                                         test [dim] = ig.DefineLabel ();
2796                                         loop [dim] = ig.DefineLabel ();
2797                                 }
2798                                         
2799                                 for (dim = 0; dim < rank; dim++){
2800                                         ig.Emit (OpCodes.Ldloc, copy);
2801                                         IntLiteral.EmitInt (ig, dim);
2802                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2803                                         ig.Emit (OpCodes.Stloc, dim_len [dim]);
2804                                 }
2805
2806                                 for (dim = 0; dim < rank; dim++){
2807                                         ig.Emit (OpCodes.Ldc_I4_0);
2808                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
2809                                         ig.Emit (OpCodes.Br, test [dim]);
2810                                         ig.MarkLabel (loop [dim]);
2811                                 }
2812
2813                                 ig.Emit (OpCodes.Ldloc, copy);
2814                                 for (dim = 0; dim < rank; dim++)
2815                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2816
2817                                 //
2818                                 // FIXME: Maybe we can cache the computation of `get'?
2819                                 //
2820                                 Type [] args = new Type [rank];
2821                                 MethodInfo get;
2822
2823                                 for (int i = 0; i < rank; i++)
2824                                         args [i] = TypeManager.int32_type;
2825
2826                                 ModuleBuilder mb = CodeGen.ModuleBuilder;
2827                                 get = mb.GetArrayMethod (
2828                                         array_type, "Get",
2829                                         CallingConventions.HasThis| CallingConventions.Standard,
2830                                         var_type, args);
2831                                 ig.Emit (OpCodes.Call, get);
2832                                 variable.EmitAssign (ec, conv);
2833                                 statement.Emit (ec);
2834                                 ig.MarkLabel (ec.LoopBegin);
2835                                 for (dim = rank - 1; dim >= 0; dim--){
2836                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2837                                         ig.Emit (OpCodes.Ldc_I4_1);
2838                                         ig.Emit (OpCodes.Add);
2839                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
2840
2841                                         ig.MarkLabel (test [dim]);
2842                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2843                                         ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2844                                         ig.Emit (OpCodes.Blt, loop [dim]);
2845                                 }
2846                         }
2847                         ig.MarkLabel (ec.LoopEnd);
2848                         
2849                         return false;
2850                 }
2851                 
2852                 public override bool Emit (EmitContext ec)
2853                 {
2854                         Type var_type;
2855                         bool ret_val;
2856                         
2857                         expr = expr.Resolve (ec);
2858                         if (expr == null)
2859                                 return false;
2860
2861                         var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2862                         if (var_type == null)
2863                                 return false;
2864                         
2865                         //
2866                         // We need an instance variable.  Not sure this is the best
2867                         // way of doing this.
2868                         //
2869                         // FIXME: When we implement propertyaccess, will those turn
2870                         // out to return values in ExprClass?  I think they should.
2871                         //
2872                         if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2873                               expr.eclass == ExprClass.PropertyAccess)){
2874                                 error1579 (expr.Type);
2875                                 return false;
2876                         }
2877
2878                         ILGenerator ig = ec.ig;
2879                         
2880                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2881                         bool old_inloop = ec.InLoop;
2882                         ec.LoopBegin = ig.DefineLabel ();
2883                         ec.LoopEnd = ig.DefineLabel ();
2884                         ec.InLoop = true;
2885                         
2886                         if (expr.Type.IsArray)
2887                                 ret_val = EmitArrayForeach (ec, var_type);
2888                         else {
2889                                 ForeachHelperMethods hm;
2890                                 
2891                                 hm = ProbeCollectionType (ec, expr.Type);
2892                                 if (hm == null)
2893                                         return false;
2894
2895                                 ret_val = EmitCollectionForeach (ec, var_type, hm);
2896                         }
2897                         
2898                         ec.LoopBegin = old_begin;
2899                         ec.LoopEnd = old_end;
2900                         ec.InLoop = old_inloop;
2901
2902                         return ret_val;
2903                 }
2904         }
2905 }
2906