2005-03-16 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / mcs / iterators.cs
1 //
2 // iterators.cs: Support for implementing iterators
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc.
8 //
9 // TODO:
10 //    Flow analysis for Yield.
11 //    Emit calls to base object constructor.
12 //
13 // Generics note:
14 //    Current should be defined to return T, and IEnumerator.Current returns object
15 //
16
17 using System;
18 using System.Collections;
19 using System.Reflection;
20 using System.Reflection.Emit;
21
22 namespace Mono.CSharp {
23
24         public interface IIteratorContainer {
25
26                 //
27                 // Invoked if a yield statement is found in the body
28                 //
29                 void SetYields ();
30         }
31         
32         public class Yield : Statement {
33                 public Expression expr;
34                 ArrayList finally_blocks;
35                 
36                 public Yield (Expression expr, Location l)
37                 {
38                         this.expr = expr;
39                         loc = l;
40                 }
41
42                 public static bool CheckContext (EmitContext ec, Location loc)
43                 {
44                         if (ec.CurrentBranching.InFinally (true)){
45                                 Report.Error (1625, loc, "Cannot yield in the body of a " +
46                                               "finally clause");
47                                 return false;
48                         }
49                         if (ec.InUnsafe) {
50                                 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
51                                 return false;
52                         }
53                         if (ec.CurrentBranching.InCatch ()){
54                                 Report.Error (1631, loc, "Cannot yield in the body of a " +
55                                               "catch clause");
56                                 return false;
57                         }
58                         if (ec.CurrentAnonymousMethod != null){
59                                 Report.Error (1621, loc, "yield statement can not appear inside an anonymoud method");
60                                 return false;
61                         }
62
63                         //
64                         // FIXME: Missing check for Yield inside try block that contains catch clauses
65                         //
66                         return true;
67                 }
68                 
69                 public override bool Resolve (EmitContext ec)
70                 {
71                         expr = expr.Resolve (ec);
72                         if (expr == null)
73                                 return false;
74                         if (!CheckContext (ec, loc))
75                                 return false;
76
77                         Iterator iterator = ec.CurrentIterator;
78                         if (expr.Type != iterator.IteratorType){
79                                 expr = Convert.ImplicitConversionRequired (
80                                         ec, expr, iterator.IteratorType, loc);
81                                 if (expr == null)
82                                         return false;
83                         }
84
85                         ec.CurrentBranching.StealFinallyClauses (ref finally_blocks);
86                         return true;
87                 }
88
89                 protected override void DoEmit (EmitContext ec)
90                 {
91                         ec.CurrentIterator.MarkYield (ec, expr, finally_blocks);
92                 }
93         }
94
95         public class YieldBreak : Statement {
96
97                 public YieldBreak (Location l)
98                 {
99                         loc = l;
100                 }
101
102                 public override bool Resolve (EmitContext ec)
103                 {
104                         if (!Yield.CheckContext (ec, loc))
105                                 return false;
106
107                         ec.CurrentBranching.CurrentUsageVector.Goto ();
108                         return true;
109                 }
110
111                 protected override void DoEmit (EmitContext ec)
112                 {
113                         ec.CurrentIterator.EmitYieldBreak (ec.ig);
114                 }
115         }
116
117         public class Iterator : Class {
118                 ToplevelBlock original_block;
119                 ToplevelBlock block;
120                 string original_name;
121
122                 Type iterator_type;
123                 TypeExpr iterator_type_expr;
124                 bool is_enumerable;
125                 bool is_static;
126
127                 Hashtable fields;
128
129                 //
130                 // The state as we generate the iterator
131                 //
132                 Label move_next_ok, move_next_error;
133                 ArrayList resume_points = new ArrayList ();
134                 int pc;
135                 
136                 //
137                 // Context from the original method
138                 //
139                 TypeContainer container;
140                 Type return_type;
141                 Type [] param_types;
142                 InternalParameters parameters;
143
144                 protected enum State {
145                         Uninitialized   = -2,
146                         After,
147                         Running
148                 }
149
150                 static int proxy_count;
151
152                 public void EmitYieldBreak (ILGenerator ig)
153                 {
154                         ig.Emit (OpCodes.Ldarg_0);
155                         IntConstant.EmitInt (ig, (int) State.After);
156                         ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
157                         ig.Emit (OpCodes.Br, move_next_error);
158                 }
159
160                 public void EmitMoveNext (EmitContext ec)
161                 {
162                         ILGenerator ig = ec.ig;
163
164                         move_next_ok = ig.DefineLabel ();
165                         move_next_error = ig.DefineLabel ();
166
167                         LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type);
168
169                         ig.BeginExceptionBlock ();
170
171                         Label dispatcher = ig.DefineLabel ();
172                         ig.Emit (OpCodes.Br, dispatcher);
173
174                         ResumePoint entry_point = new ResumePoint (null);
175                         resume_points.Add (entry_point);
176                         entry_point.Define (ig);
177
178                         ec.EmitTopBlock (original_block, parameters, Location);
179                         EmitYieldBreak (ig);
180
181                         ig.MarkLabel (dispatcher);
182
183                         Label [] labels = new Label [resume_points.Count];
184                         for (int i = 0; i < labels.Length; i++)
185                                 labels [i] = ((ResumePoint) resume_points [i]).Label;
186
187                         ig.Emit (OpCodes.Ldarg_0);
188                         ig.Emit (OpCodes.Ldfld, pc_field.FieldBuilder);
189                         ig.Emit (OpCodes.Switch, labels);
190
191                         Label end = ig.DefineLabel ();
192
193                         ig.MarkLabel (move_next_error);
194                         ig.Emit (OpCodes.Ldc_I4_0); 
195                         ig.Emit (OpCodes.Stloc, retval);
196                         ig.Emit (OpCodes.Leave, end);
197
198                         ig.MarkLabel (move_next_ok);
199                         ig.Emit (OpCodes.Ldc_I4_1);
200                         ig.Emit (OpCodes.Stloc, retval);
201                         ig.Emit (OpCodes.Leave, end);
202
203                         ig.BeginFaultBlock ();
204
205                         ig.Emit (OpCodes.Ldarg_0);
206                         ig.Emit (OpCodes.Callvirt, dispose.MethodBuilder);
207
208                         ig.EndExceptionBlock ();
209
210                         ig.MarkLabel (end);
211                         ig.Emit (OpCodes.Ldloc, retval);
212                         ig.Emit (OpCodes.Ret);
213                 }
214
215                 public void EmitDispose (EmitContext ec)
216                 {
217                         ILGenerator ig = ec.ig;
218
219                         Label end = ig.DefineLabel ();
220                         Label dispatcher = ig.DefineLabel ();
221                         ig.Emit (OpCodes.Br, dispatcher);
222
223                         ec.RemapToProxy = true;
224                         Label [] labels = new Label [resume_points.Count];
225                         for (int i = 0; i < labels.Length; i++) {
226                                 ResumePoint point = (ResumePoint) resume_points [i];
227
228                                 if (point.FinallyBlocks == null) {
229                                         labels [i] = end;
230                                         continue;
231                                 }
232
233                                 labels [i] = ig.DefineLabel ();
234                                 ig.MarkLabel (labels [i]);
235
236                                 ig.BeginExceptionBlock ();
237                                 ig.BeginFinallyBlock ();
238
239                                 foreach (ExceptionStatement stmt in point.FinallyBlocks) {
240                                         if (stmt != null)
241                                                 stmt.EmitFinally (ec);
242                                 }
243
244                                 ig.EndExceptionBlock ();
245                                 ig.Emit (OpCodes.Br, end);
246                         }
247                         ec.RemapToProxy = false;
248                         
249                         ig.MarkLabel (dispatcher);
250                         ig.Emit (OpCodes.Ldarg_0);
251                         ig.Emit (OpCodes.Ldfld, pc_field.FieldBuilder);
252                         ig.Emit (OpCodes.Switch, labels);
253
254                         ig.Emit (OpCodes.Ldarg_0);
255                         IntConstant.EmitInt (ig, (int) State.After);
256                         ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
257
258                         ig.MarkLabel (end);
259                 }
260
261                 protected class ResumePoint
262                 {
263                         public Label Label;
264                         public readonly ExceptionStatement[] FinallyBlocks;
265
266                         public ResumePoint (ArrayList list)
267                         {
268                                 if (list != null) {
269                                         FinallyBlocks = new ExceptionStatement [list.Count];
270                                         list.CopyTo (FinallyBlocks, 0);
271                                 }
272                         }
273
274                         public void Define (ILGenerator ig)
275                         {
276                                 Label = ig.DefineLabel ();
277                                 ig.MarkLabel (Label);
278                         }
279                 }
280
281                 // 
282                 // Invoked when a local variable declaration needs to be mapped to
283                 // a field in our proxy class
284                 //
285                 // Prefixes registered:
286                 //   v_   for EmitContext.MapVariable
287                 //   s_   for Storage
288                 //
289                 public FieldBuilder MapVariable (string pfx, string name, Type t)
290                 {
291                         string full_name = pfx + name;
292                         FieldBuilder fb = (FieldBuilder) fields [full_name];
293                         if (fb != null)
294                                 return fb;
295
296                         fb = TypeBuilder.DefineField (full_name, t, FieldAttributes.Private);
297                         fields.Add (full_name, fb);
298                         return fb;
299                 }
300
301                 //
302                 // Called back from Yield
303                 //
304                 public void MarkYield (EmitContext ec, Expression expr,
305                                        ArrayList finally_blocks)
306                 {
307                         ILGenerator ig = ec.ig;
308
309                         // Store the new current
310                         ig.Emit (OpCodes.Ldarg_0);
311                         expr.Emit (ec);
312                         ig.Emit (OpCodes.Stfld, current_field.FieldBuilder);
313
314                         // increment pc
315                         pc++;
316                         ig.Emit (OpCodes.Ldarg_0);
317                         IntConstant.EmitInt (ig, pc);
318                         ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
319
320                         // Return ok
321                         ig.Emit (OpCodes.Br, move_next_ok);
322
323                         ResumePoint point = new ResumePoint (finally_blocks);
324                         resume_points.Add (point);
325                         point.Define (ig);
326                 }
327
328                 public void MarkFinally (EmitContext ec, ArrayList finally_blocks)
329                 {
330                         ILGenerator ig = ec.ig;
331
332                         // increment pc
333                         pc++;
334                         ig.Emit (OpCodes.Ldarg_0);
335                         IntConstant.EmitInt (ig, pc);
336                         ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
337
338                         ResumePoint point = new ResumePoint (finally_blocks);
339                         resume_points.Add (point);
340                         point.Define (ig);
341                 }
342
343                 private static MemberName MakeProxyName (string name)
344                 {
345                         int pos = name.LastIndexOf ('.');
346                         if (pos > 0)
347                                 name = name.Substring (pos + 1);
348
349                         return new MemberName ("<" + name + ">__" + (proxy_count++));
350                 }
351
352                 //
353                 // Our constructor
354                 //
355                 public Iterator (TypeContainer container, string name, Type return_type,
356                                  Type [] param_types, InternalParameters parameters,
357                                  int modifiers, ToplevelBlock block, Location loc)
358                         : base (container.NamespaceEntry, container, MakeProxyName (name),
359                                 (modifiers & Modifiers.UNSAFE) | Modifiers.PRIVATE, null, loc)
360                 {
361                         this.container = container;
362                         this.return_type = return_type;
363                         this.param_types = param_types;
364                         this.parameters = parameters;
365                         this.original_name = name;
366                         this.original_block = block;
367                         this.block = new ToplevelBlock (loc);
368
369                         fields = new Hashtable ();
370
371                         is_static = (modifiers & Modifiers.STATIC) != 0;
372                 }
373
374                 public bool DefineIterator ()
375                 {
376                         ec = new EmitContext (this, Mono.CSharp.Location.Null, null, null, ModFlags);
377
378                         if (!CheckType (return_type)) {
379                                 Report.Error (
380                                         1624, Location,
381                                         "The body of `{0}' cannot be an iterator block " +
382                                         "because '{1}' is not an iterator interface type",
383                                         original_name, TypeManager.CSharpName (return_type));
384                                 return false;
385                         }
386
387                         for (int i = 0; i < parameters.Count; i++){
388                                 Parameter.Modifier mod = parameters.ParameterModifier (i);
389                                 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
390                                         Report.Error (
391                                                 1623, Location,
392                                                 "Iterators cannot have ref or out parameters");
393                                         return false;
394                                 }
395                         }
396
397                         ArrayList list = new ArrayList ();
398                         if (is_enumerable)
399                                 list.Add (new TypeExpression (
400                                                   TypeManager.ienumerable_type, Location));
401                         list.Add (new TypeExpression (TypeManager.ienumerator_type, Location));
402                         list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
403
404                         iterator_type_expr = new TypeExpression (iterator_type, Location);
405
406                         container.AddIterator (this);
407
408                         Bases = list;
409                         return true;
410                 }
411
412                 //
413                 // Returns the new block for the method, or null on failure
414                 //
415                 protected override bool DefineNestedTypes ()
416                 {
417                         Define_Fields ();
418                         Define_Constructor ();
419                         Define_Current ();
420                         Define_MoveNext ();
421                         Define_Reset ();
422                         Define_Dispose ();
423
424                         if (is_enumerable)
425                                 Define_GetEnumerator ();
426
427                         Create_Block ();
428
429                         return base.DefineNestedTypes ();
430                 }
431
432
433                 Field pc_field;
434                 Field current_field;
435                 Method dispose;
436
437                 public Field this_field;
438                 public Field[] parameter_fields;
439
440                 void Create_Block ()
441                 {
442                         int first = is_static ? 0 : 1;
443
444                         ArrayList args = new ArrayList ();
445                         if (!is_static) {
446                                 Type t = container.TypeBuilder;
447                                 args.Add (new Argument (
448                                         new ThisParameterReference (t, 0, Location)));
449                         }
450
451                         args.Add (new Argument (new BoolLiteral (false)));
452
453                         for (int i = 0; i < parameters.Count; i++) {
454                                 Type t = parameters.ParameterType (i);
455                                 args.Add (new Argument (
456                                         new SimpleParameterReference (t, first + i, Location)));
457                         }
458
459                         Expression new_expr = new New (
460                                 new TypeExpression (TypeBuilder, Location), args, Location);
461
462                         block.AddStatement (new NoCheckReturn (new_expr, Location));
463                 }
464
465                 void Define_Fields ()
466                 {
467                         Location loc = Location.Null;
468
469                         pc_field = new Field (
470                                 this, TypeManager.system_int32_expr, Modifiers.PRIVATE, "PC",
471                                 null, null, loc);
472                         AddField (pc_field);
473
474                         current_field = new Field (
475                                 this, iterator_type_expr, Modifiers.PRIVATE, "current",
476                                 null, null, loc);
477                         AddField (current_field);
478
479                         if (!is_static) {
480                                 this_field = new Field (
481                                         this,
482                                         new TypeExpression (container.TypeBuilder, Location),
483                                         Modifiers.PRIVATE, "this", null, null, loc);
484                                 AddField (this_field);
485                         }
486
487                         parameter_fields = new Field [parameters.Count];
488                         for (int i = 0; i < parameters.Count; i++) {
489                                 string fname = String.Format (
490                                         "field{0}_{1}", i, parameters.ParameterName (i));
491
492                                 parameter_fields [i] = new Field (
493                                         this,
494                                         new TypeExpression (parameters.ParameterType (i), loc),
495                                         Modifiers.PRIVATE, fname, null, null, loc);
496                                 AddField (parameter_fields [i]);
497                         }
498                 }
499
500                 void Define_Constructor ()
501                 {
502                         Parameters ctor_params;
503
504                         ArrayList list = new ArrayList ();
505
506                         if (!is_static)
507                                 list.Add (new Parameter (
508                                         new TypeExpression (container.TypeBuilder, Location),
509                                         "this", Parameter.Modifier.NONE, null));
510                         list.Add (new Parameter (
511                                 TypeManager.system_boolean_expr, "initialized",
512                                 Parameter.Modifier.NONE, null));
513
514                         Parameter[] old_fixed = parameters.Parameters.FixedParameters;
515                         if (old_fixed != null)
516                                 list.AddRange (old_fixed);
517
518                         Parameter[] fixed_params = new Parameter [list.Count];
519                         list.CopyTo (fixed_params);
520
521                         ctor_params = new Parameters (
522                                 fixed_params, parameters.Parameters.ArrayParameter,
523                                 Location);
524
525                         Constructor ctor = new Constructor (
526                                 this, Name, Modifiers.PUBLIC, ctor_params,
527                                 new ConstructorBaseInitializer (
528                                         null, Parameters.EmptyReadOnlyParameters, Location),
529                                 Location);
530                         AddConstructor (ctor);
531
532                         ToplevelBlock block = ctor.Block = new ToplevelBlock (Location);
533
534                         if (!is_static) {
535                                 Type t = container.TypeBuilder;
536
537                                 Assign assign = new Assign (
538                                         new FieldExpression (this_field),
539                                         new SimpleParameterReference (t, 1, Location),
540                                         Location);
541
542                                 block.AddStatement (new StatementExpression (assign, Location));
543                         }
544
545                         int first = is_static ? 2 : 3;
546
547                         for (int i = 0; i < parameters.Count; i++) {
548                                 Type t = parameters.ParameterType (i);
549
550                                 Assign assign = new Assign (
551                                         new FieldExpression (parameter_fields [i]),
552                                         new SimpleParameterReference (t, first + i, Location),
553                                         Location);
554
555                                 block.AddStatement (new StatementExpression (assign, Location));
556                         }
557
558                         State initial = is_enumerable ? State.Uninitialized : State.Running;
559                         block.AddStatement (new SetState (this, initial, Location));
560
561                         block.AddStatement (new If (
562                                 new SimpleParameterReference (
563                                         TypeManager.bool_type, first - 1, Location),
564                                 new SetState (this, State.Running, Location),
565                                 Location));
566                 }
567
568                 Statement Create_ThrowInvalidOperation ()
569                 {
570                         TypeExpr ex_type = new TypeExpression (
571                                 TypeManager.invalid_operation_exception_type, Location);
572
573                         return new Throw (new New (ex_type, null, Location), Location);
574                 }
575
576                 Statement Create_ThrowNotSupported ()
577                 {
578                         TypeExpr ex_type = new TypeExpression (
579                                 TypeManager.not_supported_exception_type, Location);
580
581                         return new Throw (new New (ex_type, null, Location), Location);
582                 }
583
584                 void Define_Current ()
585                 {
586                         ToplevelBlock get_block = new ToplevelBlock (Location);
587                         MemberName left = new MemberName ("System.Collections.IEnumerator");
588                         MemberName name = new MemberName (left, "Current");
589
590                         get_block.AddStatement (new If (
591                                 new Binary (
592                                         Binary.Operator.LessThanOrEqual,
593                                         new FieldExpression (pc_field),
594                                         new IntLiteral ((int) State.Running), Location),
595                                 Create_ThrowInvalidOperation (),
596                                 new Return (
597                                         new FieldExpression (current_field), Location),
598                                 Location));
599
600                         Accessor getter = new Accessor (get_block, 0, null, Location);
601
602                         Property current = new Property (
603                                 this, iterator_type_expr, 0,
604                                 false, name, null, getter, null, Location);
605                         AddProperty (current);
606                 }
607
608                 void Define_MoveNext ()
609                 {
610                         Method move_next = new Method (
611                                 this, TypeManager.system_boolean_expr,
612                                 Modifiers.PUBLIC, false, new MemberName ("MoveNext"),
613                                 Parameters.EmptyReadOnlyParameters, null,
614                                 Location.Null);
615                         AddMethod (move_next);
616
617                         ToplevelBlock block = move_next.Block = new ToplevelBlock (Location);
618
619                         MoveNextMethod inline = new MoveNextMethod (this, Location);
620                         block.AddStatement (inline);
621                 }
622
623                 void Define_GetEnumerator ()
624                 {
625                         MemberName left = new MemberName ("System.Collections.IEnumerable");
626                         MemberName name = new MemberName (left, "GetEnumerator");
627
628                         Method get_enumerator = new Method (
629                                 this,
630                                 new TypeExpression (TypeManager.ienumerator_type, Location),
631                                 0, false, name,
632                                 Parameters.EmptyReadOnlyParameters, null,
633                                 Location.Null);
634                         AddMethod (get_enumerator);
635
636                         get_enumerator.Block = new ToplevelBlock (Location);
637
638                         Expression ce = new MemberAccess (
639                                 new SimpleName ("System.Threading.Interlocked", Location),
640                                 "CompareExchange", Location);
641
642                         Expression pc = new FieldExpression (pc_field);
643                         Expression before = new IntLiteral ((int) State.Running);
644                         Expression uninitialized = new IntLiteral ((int) State.Uninitialized);
645
646                         ArrayList args = new ArrayList ();
647                         args.Add (new Argument (pc, Argument.AType.Ref));
648                         args.Add (new Argument (before, Argument.AType.Expression));
649                         args.Add (new Argument (uninitialized, Argument.AType.Expression));
650
651                         get_enumerator.Block.AddStatement (new If (
652                                 new Binary (
653                                         Binary.Operator.Equality,
654                                         new Invocation (ce, args, Location),
655                                         uninitialized, Location),
656                                 new Return (new This (block, Location), Location),
657                                 Location));
658
659                         args = new ArrayList ();
660                         if (!is_static)
661                                 args.Add (new Argument (new FieldExpression (this_field)));
662
663                         args.Add (new Argument (new BoolLiteral (true)));
664
665                         for (int i = 0; i < parameters.Count; i++)
666                                 args.Add (new Argument (
667                                                   new FieldExpression (parameter_fields [i])));
668
669                         Expression new_expr = new New (
670                                 new TypeExpression (TypeBuilder, Location), args, Location);
671                         get_enumerator.Block.AddStatement (new Return (new_expr, Location));
672                 }
673
674                 protected class SimpleParameterReference : Expression
675                 {
676                         int idx;
677
678                         public SimpleParameterReference (Type type, int idx, Location loc)
679                         {
680                                 this.idx = idx;
681                                 this.loc = loc;
682                                 this.type = type;
683                                 eclass = ExprClass.Variable;
684                         }
685
686                         public override Expression DoResolve (EmitContext ec)
687                         {
688                                 return this;
689                         }
690
691                         public override void Emit (EmitContext ec)
692                         {
693                                 DoEmit (ec);
694                         }
695
696                         protected virtual void DoEmit (EmitContext ec)
697                         {
698                                 ParameterReference.EmitLdArg (ec.ig, idx);
699                         }
700                 }
701
702                 protected class ThisParameterReference : SimpleParameterReference
703                 {
704                         public ThisParameterReference (Type type, int idx, Location loc)
705                                 : base (type, idx, loc)
706                         { }
707
708                         protected override void DoEmit (EmitContext ec)
709                         {
710                                 base.DoEmit (ec);
711                                 if (ec.TypeContainer is Struct)
712                                         ec.ig.Emit (OpCodes.Ldobj, type);
713                         }
714                 }
715
716                 protected class FieldExpression : Expression
717                 {
718                         Field field;
719
720                         public FieldExpression (Field field)
721                         {
722                                 this.field = field;
723                         }
724
725                         public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
726                         {
727                                 return DoResolve (ec);
728                         }
729
730                         public override Expression DoResolve (EmitContext ec)
731                         {
732                                 FieldExpr fexpr = new FieldExpr (field.FieldBuilder, loc);
733                                 fexpr.InstanceExpression = ec.GetThis (loc);
734                                 return fexpr.Resolve (ec);
735                         }
736
737                         public override void Emit (EmitContext ec)
738                         {
739                                 throw new InvalidOperationException ();
740                         }
741                 }
742
743                 protected class MoveNextMethod : Statement {
744                         Iterator iterator;
745
746                         public MoveNextMethod (Iterator iterator, Location loc)
747                         {
748                                 this.loc = loc;
749                                 this.iterator = iterator;
750                         }
751
752                         public override bool Resolve (EmitContext ec)
753                         {
754                                 ec.CurrentBranching.CurrentUsageVector.Return ();
755                                 return true;
756                         }
757
758                         protected override void DoEmit (EmitContext ec)
759                         {
760                                 int code_flags = Modifiers.METHOD_YIELDS;
761                                 if (iterator.is_static)
762                                         code_flags |= Modifiers.STATIC;
763
764                                 code_flags |= iterator.ModFlags & Modifiers.UNSAFE;
765
766                                 EmitContext new_ec = new EmitContext (
767                                         iterator.container, loc, ec.ig,
768                                         TypeManager.int32_type, code_flags);
769
770                                 new_ec.CurrentIterator = iterator;
771
772                                 iterator.EmitMoveNext (new_ec);
773                         }
774                 }
775
776                 protected class DisposeMethod : Statement {
777                         Iterator iterator;
778
779                         public DisposeMethod (Iterator iterator, Location loc)
780                         {
781                                 this.loc = loc;
782                                 this.iterator = iterator;
783                         }
784
785                         public override bool Resolve (EmitContext ec)
786                         {
787                                 return true;
788                         }
789
790                         protected override void DoEmit (EmitContext ec)
791                         {
792                                 iterator.EmitDispose (ec);
793                         }
794                 }
795
796                 protected class StatementList : Statement {
797                         ArrayList statements;
798
799                         public StatementList (Location loc)
800                         {
801                                 this.loc = loc;
802                                 statements = new ArrayList ();
803                         }
804
805                         public void Add (Statement statement)
806                         {
807                                 statements.Add (statement);
808                         }
809
810                         public override bool Resolve (EmitContext ec)
811                         {
812                                 foreach (Statement stmt in statements) {
813                                         if (!stmt.Resolve (ec))
814                                                 return false;
815                                 }
816
817                                 return true;
818                         }
819
820                         protected override void DoEmit (EmitContext ec)
821                         {
822                                 foreach (Statement stmt in statements)
823                                         stmt.Emit (ec);
824                         }
825                 }
826
827                 protected class SetState : Statement
828                 {
829                         Iterator iterator;
830                         State state;
831
832                         public SetState (Iterator iterator, State state, Location loc)
833                         {
834                                 this.iterator = iterator;
835                                 this.state = state;
836                                 this.loc = loc;
837                         }
838
839                         public override bool Resolve (EmitContext ec)
840                         {
841                                 return true;
842                         }
843
844                         protected override void DoEmit (EmitContext ec)
845                         {
846                                 ec.ig.Emit (OpCodes.Ldarg_0);
847                                 IntConstant.EmitInt (ec.ig, (int) state);
848                                 ec.ig.Emit (OpCodes.Stfld, iterator.pc_field.FieldBuilder);
849                         }
850                 }
851
852                 void Define_Reset ()
853                 {
854                         Method reset = new Method (
855                                 this, TypeManager.system_void_expr, Modifiers.PUBLIC,
856                                 false, new MemberName ("Reset"),
857                                 Parameters.EmptyReadOnlyParameters, null, Location);
858                         AddMethod (reset);
859
860                         reset.Block = new ToplevelBlock (Location);
861                         reset.Block.AddStatement (Create_ThrowNotSupported ());
862                 }
863
864                 void Define_Dispose ()
865                 {
866                         dispose = new Method (
867                                 this, TypeManager.system_void_expr, Modifiers.PUBLIC,
868                                 false, new MemberName ("Dispose"),
869                                 Parameters.EmptyReadOnlyParameters, null, Location);
870                         AddMethod (dispose);
871
872                         dispose.Block = new ToplevelBlock (Location);
873                         dispose.Block.AddStatement (new DisposeMethod (this, Location));
874                 }
875
876                 public ToplevelBlock Block {
877                         get { return block; }
878                 }
879
880                 public Type IteratorType {
881                         get { return iterator_type; }
882                 }
883
884                 //
885                 // This return statement tricks return into not flagging an error for being
886                 // used in a Yields method
887                 //
888                 class NoCheckReturn : Return {
889                         public NoCheckReturn (Expression expr, Location loc) : base (expr, loc)
890                         {
891                         }
892
893                         public override bool Resolve (EmitContext ec)
894                         {
895                                 ec.InIterator = false;
896                                 bool ret_val = base.Resolve (ec);
897                                 ec.InIterator = true;
898
899                                 return ret_val;
900                         }
901                 }
902
903                 bool CheckType (Type t)
904                 {
905                         if (t == TypeManager.ienumerable_type) {
906                                 iterator_type = TypeManager.object_type;
907                                 is_enumerable = true;
908                                 return true;
909                         } else if (t == TypeManager.ienumerator_type) {
910                                 iterator_type = TypeManager.object_type;
911                                 is_enumerable = false;
912                                 return true;
913                         }
914
915                         return false;
916                 }
917         }
918 }
919