2006-10-12 Martin Baulig <martin@ximian.com>
[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 class Yield : Statement {
25                 Expression expr;
26                 ArrayList finally_blocks;
27
28                 public Yield (Expression expr, Location l)
29                 {
30                         this.expr = expr;
31                         loc = l;
32                 }
33
34                 public static bool CheckContext (EmitContext ec, Location loc, bool isYieldBreak)
35                 {
36                         if (ec.InFinally) {
37                                 Report.Error (1625, loc, "Cannot yield in the body of a " +
38                                               "finally clause");
39                                 return false;
40                         }
41
42                         for (Block block = ec.CurrentBlock; block != null; block = block.Parent) {
43                                 if (!block.Unsafe)
44                                         continue;
45
46                                 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
47                                 return false;
48                         }
49
50                         //
51                         // We can't use `ec.InUnsafe' here because it's allowed to have an iterator
52                         // inside an unsafe class.  See test-martin-29.cs for an example.
53                         //
54                         if (!ec.CurrentAnonymousMethod.IsIterator) {
55                                 Report.Error (1621, loc,
56                                               "The yield statement cannot be used inside " +
57                                               "anonymous method blocks");
58                                 return false;
59                         }
60
61                         if (ec.CurrentBranching.InTryWithCatch () && (!isYieldBreak || !ec.InCatch)) {
62                                 if (!ec.InCatch)
63                                         Report.Error (1626, loc, "Cannot yield a value in the body " +
64                                                       "of a try block with a catch clause");
65                                 else
66                                         Report.Error (1631, loc, "Cannot yield a value in the body " +
67                                                       "of a catch clause");
68                                 return false;
69                         }
70
71                         return true;
72                 }
73                 
74                 public override bool Resolve (EmitContext ec)
75                 {
76                         Report.Debug (64, "RESOLVE YIELD", this, ec, expr, expr.GetType ());
77                         expr = expr.Resolve (ec);
78                         if (expr == null)
79                                 return false;
80
81                         Report.Debug (64, "RESOLVE YIELD #1", this, ec, expr, expr.GetType (),
82                                       ec.CurrentAnonymousMethod, ec.CurrentIterator);
83
84                         if (!CheckContext (ec, loc, false))
85                                 return false;
86
87                         Iterator iterator = ec.CurrentIterator;
88                         if (expr.Type != iterator.IteratorType) {
89                                 expr = Convert.ImplicitConversionRequired (
90                                         ec, expr, iterator.IteratorType, loc);
91                                 if (expr == null)
92                                         return false;
93                         }
94
95                         ec.CurrentBranching.StealFinallyClauses (ref finally_blocks);
96                         return true;
97                 }
98
99                 protected override void DoEmit (EmitContext ec)
100                 {
101                         ec.CurrentIterator.MarkYield (ec, expr, finally_blocks);
102                 }
103         }
104
105         public class YieldBreak : Statement {
106
107                 public YieldBreak (Location l)
108                 {
109                         loc = l;
110                 }
111
112                 public override bool Resolve (EmitContext ec)
113                 {
114                         if (!Yield.CheckContext (ec, loc, true))
115                                 return false;
116
117                         ec.CurrentBranching.CurrentUsageVector.Goto ();
118                         return true;
119                 }
120
121                 protected override void DoEmit (EmitContext ec)
122                 {
123                         ec.CurrentIterator.EmitYieldBreak (ec.ig);
124                 }
125         }
126
127         public class IteratorHost : AnonymousMethodHost
128         {
129                 public readonly Iterator Iterator;
130
131                 TypeExpr iterator_type_expr;
132                 Field pc_field;
133                 Field current_field;
134                 MethodInfo dispose_method;
135
136                 TypeExpr enumerator_type;
137                 TypeExpr enumerable_type;
138                 TypeArguments generic_args;
139                 TypeExpr generic_enumerator_type;
140                 TypeExpr generic_enumerable_type;
141
142                 public IteratorHost (Iterator iterator)
143                         : base (iterator.Container.Toplevel, iterator.Host, iterator.GenericMethod,
144                                 iterator.Location)
145                 {
146                         this.Iterator = iterator;
147                 }
148
149                 public override bool IsIterator {
150                         get { return true; }
151                 }
152
153                 public MethodInfo Dispose {
154                         get { return dispose_method; }
155                 }
156
157                 public Field PC {
158                         get { return pc_field; }
159                 }
160
161                 public Field CurrentField {
162                         get { return current_field; }
163                 }
164
165                 public Type IteratorType {
166                         get { return iterator_type_expr.Type; }
167                 }
168
169                 public override TypeExpr [] GetClassBases (out TypeExpr base_class)
170                 {
171                         iterator_type_expr = InflateType (Iterator.OriginalIteratorType);
172
173 #if GMCS_SOURCE
174                         generic_args = new TypeArguments (Location);
175                         generic_args.Add (iterator_type_expr);
176 #endif
177
178                         ArrayList list = new ArrayList ();
179                         if (Iterator.IsEnumerable) {
180                                 enumerable_type = new TypeExpression (
181                                         TypeManager.ienumerable_type, Location);
182                                 list.Add (enumerable_type);
183
184 #if GMCS_SOURCE
185                                 generic_enumerable_type = new ConstructedType (
186                                         TypeManager.generic_ienumerable_type,
187                                         generic_args, Location);
188                                 list.Add (generic_enumerable_type);
189 #endif
190                         }
191
192                         enumerator_type = new TypeExpression (
193                                 TypeManager.ienumerator_type, Location);
194                         list.Add (enumerator_type);
195
196                         list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
197
198 #if GMCS_SOURCE
199                         generic_enumerator_type = new ConstructedType (
200                                 TypeManager.generic_ienumerator_type,
201                                 generic_args, Location);
202                         list.Add (generic_enumerator_type);
203 #endif
204
205                         Bases = list;
206
207                         return base.GetClassBases (out base_class);
208                 }
209
210                 protected override bool DoResolveMembers ()
211                 {
212                         pc_field = CaptureVariable ("$PC", TypeManager.system_int32_expr);
213                         current_field = CaptureVariable ("$current", iterator_type_expr);
214
215 #if GMCS_SOURCE
216                         Define_Current (true);
217 #endif
218                         Define_Current (false);
219                         new DisposeMethod (this);
220                         Define_Reset ();
221
222                         if (Iterator.IsEnumerable) {
223                                 new GetEnumeratorMethod (this, false);
224 #if GMCS_SOURCE
225                                 new GetEnumeratorMethod (this, true);
226 #endif
227                         }
228
229                         return base.DoResolveMembers ();
230                 }
231
232                 protected override bool DoDefineMembers ()
233                 {
234                         if (!base.DoDefineMembers ())
235                                 return false;
236
237                         FetchMethodDispose ();
238
239                         return true;
240                 }
241
242                 protected override void EmitScopeConstructor (EmitContext ec)
243                 {
244                         ec.ig.Emit (OpCodes.Ldarg_0);
245                         ec.ig.Emit (OpCodes.Ldarg_1);
246                         ec.ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
247                         base.EmitScopeConstructor (ec);
248                 }
249
250                 void FetchMethodDispose ()
251                 {
252                         MemberList dispose_list;
253
254                         dispose_list = FindMembers (
255                                 CurrentType != null ? CurrentType : TypeBuilder,
256                                 MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance,
257                                 Type.FilterName, "Dispose");
258
259                         if (dispose_list.Count != 1)
260                                 throw new InternalErrorException ("Cannot find Dipose() method.");
261
262                         dispose_method = (MethodInfo) dispose_list [0];
263                 }
264
265                 void Define_Current (bool is_generic)
266                 {
267                         MemberName left;
268                         Expression type;
269
270                         if (is_generic) {
271                                 left = new MemberName (
272                                         "System.Collections.Generic.IEnumerator",
273                                         generic_args, Location);
274                                 type = iterator_type_expr;
275                         } else {
276                                 left = new MemberName ("System.Collections.IEnumerator", Location);
277                                 type = TypeManager.system_object_expr;
278                         }
279
280                         MemberName name = new MemberName (left, "Current", null, Location);
281
282                         ToplevelBlock get_block = new ToplevelBlock (Location);
283                         get_block.AddStatement (new CurrentBlock (this, is_generic));
284
285                         Accessor getter = new Accessor (get_block, 0, null, Location);
286
287                         Property current = new Property (
288                                 this, type, 0, false, name, null, getter, null);
289                         AddProperty (current);
290                 }
291
292                 void Define_Reset ()
293                 {
294                         Method reset = new Method (
295                                 this, null, TypeManager.system_void_expr, Modifiers.PUBLIC,
296                                 false, new MemberName ("Reset", Location),
297                                 Parameters.EmptyReadOnlyParameters, null);
298                         AddMethod (reset);
299
300                         reset.Block = new ToplevelBlock (Location);
301                         reset.Block.AddStatement (Create_ThrowNotSupported ());
302                 }
303
304                 Statement Create_ThrowNotSupported ()
305                 {
306                         TypeExpr ex_type = new TypeExpression (
307                                 TypeManager.not_supported_exception_type, Location);
308
309                         return new Throw (new New (ex_type, null, Location), Location);
310                 }
311
312                 ConstructorInfo GetInvalidOperationException ()
313                 {
314                         return TypeManager.GetConstructor (
315                                 TypeManager.invalid_operation_exception_type, Type.EmptyTypes);
316                 }
317
318                 protected override ScopeInitializer CreateScopeInitializer ()
319                 {
320                         return new IteratorHostInitializer (this);
321                 }
322
323                 protected class IteratorHostInitializer : AnonymousMethodHostInitializer
324                 {
325                         new public readonly IteratorHost Host;
326                         protected Iterator.State state;
327
328                         public IteratorHostInitializer (IteratorHost host)
329                                 : base (host)
330                         {
331                                 this.Host = host;
332                         }
333
334                         protected override bool DoResolveInternal (EmitContext ec)
335                         {
336                                 if (this is EnumeratorScopeInitializer)
337                                         state = Iterator.State.Running;
338                                 else if (Host.Iterator.IsEnumerable)
339                                         state = Iterator.State.Uninitialized;
340                                 else
341                                         state = Iterator.State.Running;
342
343                                 return base.DoResolveInternal (ec);
344                         }
345
346                         protected override void EmitScopeConstructor (EmitContext ec)
347                         {
348                                 ec.ig.Emit (OpCodes.Ldc_I4, (int) state);
349                                 base.EmitScopeConstructor (ec);
350                         }
351                 }
352
353                 protected class GetEnumeratorMethod : Method
354                 {
355                         public IteratorHost Host;
356
357                         static MemberName GetMemberName (IteratorHost host, bool is_generic)
358                         {
359                                 MemberName left;
360                                 if (is_generic) {
361                                         left = new MemberName (
362                                                 "System.Collections.Generic.IEnumerable",
363                                                 host.generic_args, host.Location);
364                                 } else {
365                                         left = new MemberName (
366                                                 "System.Collections.IEnumerable", host.Location);
367                                 }
368
369                                 return new MemberName (left, "GetEnumerator", host.Location);
370                         }
371
372                         public GetEnumeratorMethod (IteratorHost host, bool is_generic)
373                                 : base (host, null, is_generic ?
374                                         host.generic_enumerator_type : host.enumerator_type,
375                                         0, false, GetMemberName (host, is_generic),
376                                         Parameters.EmptyReadOnlyParameters, null)
377                         {
378                                 this.Host = host;
379
380                                 host.AddMethod (this);
381
382                                 Block = new ToplevelBlock (host.Iterator.Container.Toplevel, null, Location);
383                                 Block.AddStatement (new GetEnumeratorStatement (host, Type));
384                         }
385
386                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
387                         {
388                                 EmitContext ec = new EmitContext (
389                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
390
391                                 ec.CurrentAnonymousMethod = Host.Iterator;
392                                 return ec;
393                         }
394
395                         protected class GetEnumeratorStatement : Statement
396                         {
397                                 IteratorHost host;
398                                 Expression type;
399
400                                 ExpressionStatement initializer;
401                                 Expression cast;
402                                 MethodInfo ce;
403
404                                 public GetEnumeratorStatement (IteratorHost host, Expression type)
405                                 {
406                                         this.host = host;
407                                         this.type = type;
408                                         loc = host.Location;
409                                 }
410
411                                 public override bool Resolve (EmitContext ec)
412                                 {
413                                         type = type.ResolveAsTypeTerminal (ec, false);
414                                         if ((type == null) || (type.Type == null))
415                                                 return false;
416
417                                         initializer = host.GetEnumeratorInitializer (ec);
418                                         if (initializer == null)
419                                                 return false;
420
421                                         cast = new ClassCast (initializer, type.Type);
422
423                                         ce = TypeManager.int_interlocked_compare_exchange;
424
425                                         ec.CurrentBranching.CurrentUsageVector.Return ();
426                                         return true;
427                                 }
428
429                                 protected override void DoEmit (EmitContext ec)
430                                 {
431                                         ILGenerator ig = ec.ig;
432                                         Label label_init = ig.DefineLabel ();
433
434                                         ig.Emit (OpCodes.Ldarg_0);
435                                         ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
436                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running);
437                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
438                                         ig.Emit (OpCodes.Call, ce);
439
440                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
441                                         ig.Emit (OpCodes.Bne_Un, label_init);
442
443                                         ig.Emit (OpCodes.Ldarg_0);
444                                         ig.Emit (OpCodes.Ret);
445
446                                         ig.MarkLabel (label_init);
447
448                                         initializer.EmitStatement (ec);
449                                         cast.Emit (ec);
450                                         ig.Emit (OpCodes.Ret);
451                                 }
452                         }
453                 }
454
455                 protected class DisposeMethod : Method
456                 {
457                         public IteratorHost Host;
458
459                         public DisposeMethod (IteratorHost host)
460                                 : base (host, null, TypeManager.system_void_expr, Modifiers.PUBLIC,
461                                         false, new MemberName ("Dispose", host.Location),
462                                         Parameters.EmptyReadOnlyParameters, null)
463                         {
464                                 this.Host = host;
465
466                                 host.AddMethod (this);
467
468                                 Block = new ToplevelBlock (host.Iterator.Block, null, Location);
469                                 Block.AddStatement (new DisposeMethodStatement (Host.Iterator));
470
471                                 Report.Debug (64, "DISPOSE METHOD", host, Block);
472                         }
473
474                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
475                         {
476                                 EmitContext ec = new EmitContext (
477                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
478
479                                 ec.CurrentAnonymousMethod = Host.Iterator;
480                                 return ec;
481                         }
482
483                         protected class DisposeMethodStatement : Statement
484                         {
485                                 Iterator iterator;
486
487                                 public DisposeMethodStatement (Iterator iterator)
488                                 {
489                                         this.iterator = iterator;
490                                         this.loc = iterator.Location;
491                                 }
492
493                                 public override bool Resolve (EmitContext ec)
494                                 {
495                                         return true;
496                                 }
497
498                                 protected override void DoEmit (EmitContext ec)
499                                 {
500                                         iterator.EmitDispose (ec);
501                                 }
502                         }
503                 }
504
505                 protected ScopeInitializer GetEnumeratorInitializer (EmitContext ec)
506                 {
507                         ScopeInitializer init = new EnumeratorScopeInitializer (this);
508                         if (init.Resolve (ec) == null)
509                                 throw new InternalErrorException ();
510                         return init;
511                 }
512
513                 protected class EnumeratorScopeInitializer : IteratorHostInitializer
514                 {
515                         IteratorHost host;
516
517                         public EnumeratorScopeInitializer (IteratorHost host)
518                                 : base (host)
519                         {
520                                 this.host = host;
521                         }
522
523                         protected override bool DoResolveInternal (EmitContext ec)
524                         {
525                                 type = host.IsGeneric ? host.CurrentType : host.TypeBuilder;
526                                 return base.DoResolveInternal (ec);
527                         }
528
529                         protected override void DoEmit (EmitContext ec)
530                         {
531                                 DoEmitInstance (ec);
532                         }
533
534                         protected override bool IsGetEnumerator {
535                                 get { return true; }
536                         }
537
538                         protected override void EmitParameterReference (EmitContext ec,
539                                                                         CapturedParameter cp)
540                         {
541                                 ec.ig.Emit (OpCodes.Ldarg_0);
542                                 ec.ig.Emit (OpCodes.Ldfld, cp.Field.FieldBuilder);
543                         }
544                 }
545
546                 protected class CurrentBlock : Statement {
547                         IteratorHost host;
548                         bool is_generic;
549
550                         public CurrentBlock (IteratorHost host, bool is_generic)
551                         {
552                                 this.host = host;
553                                 this.is_generic = is_generic;
554                                 loc = host.Location;
555                         }
556
557                         public override bool Resolve (EmitContext ec)
558                         {
559                                 ec.CurrentBranching.CurrentUsageVector.Return ();
560                                 return true;
561                         }
562
563                         protected override void DoEmit (EmitContext ec)
564                         {
565                                 ILGenerator ig = ec.ig;
566                                 Label label_ok = ig.DefineLabel ();
567
568                                 ig.Emit (OpCodes.Ldarg_0);
569                                 ig.Emit (OpCodes.Ldfld, host.PC.FieldBuilder);
570                                 ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running);
571                                 ig.Emit (OpCodes.Bgt, label_ok);
572
573                                 ig.Emit (OpCodes.Newobj, host.GetInvalidOperationException ());
574                                 ig.Emit (OpCodes.Throw);
575
576                                 ig.MarkLabel (label_ok);
577                                 ig.Emit (OpCodes.Ldarg_0);
578                                 ig.Emit (OpCodes.Ldfld, host.CurrentField.FieldBuilder);
579                                 if (!is_generic)
580                                         ig.Emit (OpCodes.Box, host.CurrentField.MemberType);
581                                 ig.Emit (OpCodes.Ret);
582                         }
583                 }
584         }
585
586         public class Iterator : AnonymousContainer {
587                 protected readonly ToplevelBlock OriginalBlock;
588                 protected readonly IMethodData OriginalMethod;
589                 protected ToplevelBlock block;
590
591                 public readonly bool IsEnumerable;
592                 public readonly bool IsStatic;
593
594                 //
595                 // The state as we generate the iterator
596                 //
597                 Label move_next_ok, move_next_error;
598                 ArrayList resume_points = new ArrayList ();
599                 int pc;
600                 
601                 public readonly Type OriginalIteratorType;
602                 public readonly IteratorHost IteratorHost;
603
604                 public enum State {
605                         Uninitialized   = -2,
606                         After,
607                         Running
608                 }
609
610                 public void EmitYieldBreak (ILGenerator ig)
611                 {
612                         ig.Emit (OpCodes.Ldarg_0);
613                         IntConstant.EmitInt (ig, (int) State.After);
614                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
615                         ig.Emit (OpCodes.Br, move_next_error);
616                 }
617
618                 internal void EmitMoveNext (EmitContext ec, Block original_block)
619                 {
620                         ILGenerator ig = ec.ig;
621
622                         move_next_ok = ig.DefineLabel ();
623                         move_next_error = ig.DefineLabel ();
624
625                         LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type);
626
627                         ig.BeginExceptionBlock ();
628
629                         Label dispatcher = ig.DefineLabel ();
630                         ig.Emit (OpCodes.Br, dispatcher);
631
632                         ResumePoint entry_point = new ResumePoint (null);
633                         resume_points.Add (entry_point);
634                         entry_point.Define (ig);
635
636                         original_block.Emit (ec);
637
638                         EmitYieldBreak (ig);
639
640                         ig.MarkLabel (dispatcher);
641
642                         Label [] labels = new Label [resume_points.Count];
643                         for (int i = 0; i < labels.Length; i++)
644                                 labels [i] = ((ResumePoint) resume_points [i]).Label;
645
646                         ig.Emit (OpCodes.Ldarg_0);
647                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
648                         ig.Emit (OpCodes.Switch, labels);
649
650                         Label end = ig.DefineLabel ();
651
652                         ig.MarkLabel (move_next_error);
653                         ig.Emit (OpCodes.Ldc_I4_0); 
654                         ig.Emit (OpCodes.Stloc, retval);
655                         ig.Emit (OpCodes.Leave, end);
656
657                         ig.MarkLabel (move_next_ok);
658                         ig.Emit (OpCodes.Ldc_I4_1);
659                         ig.Emit (OpCodes.Stloc, retval);
660                         ig.Emit (OpCodes.Leave, end);
661
662                         ig.BeginFaultBlock ();
663
664                         ig.Emit (OpCodes.Ldarg_0);
665                         ig.Emit (OpCodes.Callvirt, IteratorHost.Dispose);
666
667                         ig.EndExceptionBlock ();
668
669                         ig.MarkLabel (end);
670                         ig.Emit (OpCodes.Ldloc, retval);
671                         ig.Emit (OpCodes.Ret);
672                 }
673
674                 public void EmitDispose (EmitContext ec)
675                 {
676                         ILGenerator ig = ec.ig;
677
678                         Label end = ig.DefineLabel ();
679                         Label dispatcher = ig.DefineLabel ();
680                         ig.Emit (OpCodes.Br, dispatcher);
681
682                         Label [] labels = new Label [resume_points.Count];
683                         for (int i = 0; i < labels.Length; i++) {
684                                 ResumePoint point = (ResumePoint) resume_points [i];
685
686                                 if (point.FinallyBlocks == null) {
687                                         labels [i] = end;
688                                         continue;
689                                 }
690
691                                 labels [i] = ig.DefineLabel ();
692                                 ig.MarkLabel (labels [i]);
693
694                                 ig.BeginExceptionBlock ();
695                                 ig.BeginFinallyBlock ();
696
697                                 foreach (ExceptionStatement stmt in point.FinallyBlocks) {
698                                         if (stmt != null)
699                                                 stmt.EmitFinally (ec);
700                                 }
701
702                                 ig.EndExceptionBlock ();
703                                 ig.Emit (OpCodes.Br, end);
704                         }
705
706                         ig.MarkLabel (dispatcher);
707                         ig.Emit (OpCodes.Ldarg_0);
708                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
709                         ig.Emit (OpCodes.Switch, labels);
710
711                         ig.Emit (OpCodes.Ldarg_0);
712                         IntConstant.EmitInt (ig, (int) State.After);
713                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
714
715                         ig.MarkLabel (end);
716                 }
717
718                 protected class ResumePoint
719                 {
720                         public Label Label;
721                         public readonly ExceptionStatement[] FinallyBlocks;
722
723                         public ResumePoint (ArrayList list)
724                         {
725                                 if (list != null) {
726                                         FinallyBlocks = new ExceptionStatement [list.Count];
727                                         list.CopyTo (FinallyBlocks, 0);
728                                 }
729                         }
730
731                         public void Define (ILGenerator ig)
732                         {
733                                 Label = ig.DefineLabel ();
734                                 ig.MarkLabel (Label);
735                         }
736                 }
737
738                 //
739                 // Called back from Yield
740                 //
741                 public void MarkYield (EmitContext ec, Expression expr,
742                                        ArrayList finally_blocks)
743                 {
744                         ILGenerator ig = ec.ig;
745
746                         // Store the new current
747                         ig.Emit (OpCodes.Ldarg_0);
748                         expr.Emit (ec);
749                         ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
750
751                         // increment pc
752                         pc++;
753                         ig.Emit (OpCodes.Ldarg_0);
754                         IntConstant.EmitInt (ig, pc);
755                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
756
757                         // Return ok
758                         ig.Emit (OpCodes.Br, move_next_ok);
759
760                         ResumePoint point = new ResumePoint (finally_blocks);
761                         resume_points.Add (point);
762                         point.Define (ig);
763                 }
764
765                 public void MarkFinally (EmitContext ec, ArrayList finally_blocks)
766                 {
767                         ILGenerator ig = ec.ig;
768
769                         // increment pc
770                         pc++;
771                         ig.Emit (OpCodes.Ldarg_0);
772                         IntConstant.EmitInt (ig, pc);
773                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
774
775                         ResumePoint point = new ResumePoint (finally_blocks);
776                         resume_points.Add (point);
777                         point.Define (ig);
778                 }
779
780                 public override bool IsIterator {
781                         get { return true; }
782                 }
783
784                 public override AnonymousMethodHost RootScope {
785                         get { return IteratorHost; }
786                 }
787
788                 //
789                 // Our constructor
790                 //
791                 public Iterator (IMethodData m_container, TypeContainer host, GenericMethod generic,
792                                  int modifiers, Type iterator_type, bool is_enumerable)
793                         : base (null, host, generic, m_container.ParameterInfo,
794                                 new ToplevelBlock (m_container.ParameterInfo, m_container.Location),
795                                 m_container.Block, TypeManager.bool_type, modifiers,
796                                 m_container.Location)
797                 {
798                         this.OriginalBlock = m_container.Block;
799                         this.OriginalMethod = m_container;
800                         this.OriginalIteratorType = iterator_type;
801                         this.IsEnumerable = is_enumerable;
802
803                         Report.Debug (64, "NEW ITERATOR", host, generic, OriginalBlock,
804                                       Container, Block);
805
806                         IteratorHost = new IteratorHost (this);
807                         Block.CreateIteratorHost (IteratorHost);
808
809                         OriginalBlock.ReParent (Container.Toplevel);
810
811                         m_container.Block = Container.Toplevel;
812
813                         OriginalBlock.MakeIterator (this);
814                 }
815
816                 protected class TestStatement : Statement
817                 {
818                         public override bool Resolve (EmitContext ec)
819                         {
820                                 return true;
821                         }
822
823                         protected override void DoEmit (EmitContext ec)
824                         {
825                                 ec.ig.Emit (OpCodes.Nop);
826                                 ec.ig.Emit (OpCodes.Neg);
827                                 ec.ig.Emit (OpCodes.Pop);
828                                 ec.ig.Emit (OpCodes.Ret);
829                         }
830                 }
831
832                 public override string GetSignatureForError ()
833                 {
834                         return OriginalMethod.GetSignatureForError ();
835                 }
836
837                 public override bool Resolve (EmitContext ec)
838                 {
839                         Report.Debug (64, "RESOLVE ITERATOR", this, Container, Block);
840
841                         Parameters parameters = OriginalMethod.ParameterInfo;
842                         for (int i = 0; i < parameters.Count; i++){
843                                 Parameter.Modifier mod = parameters.ParameterModifier (i);
844                                 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
845                                         Report.Error (1623, Location,
846                                                       "Iterators cannot have ref or out parameters");
847                                         return false;
848                                 }
849
850                                 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
851                                         Report.Error (1636, Location,
852                                                       "__arglist is not allowed in parameter list " +
853                                                       "of iterators");
854                                         return false;
855                                 }
856
857                                 if (parameters.ParameterType (i).IsPointer) {
858                                         Report.Error (1637, Location,
859                                                       "Iterators cannot have unsafe parameters or " +
860                                                       "yield types");
861                                         return false;
862                                 }
863                         }
864
865                         if ((ModFlags & Modifiers.UNSAFE) != 0) {
866                                 Report.Error (1629, Location, "Unsafe code may not appear in iterators");
867                                 return false;
868                         }
869
870                         if (!base.Resolve (ec))
871                                 return false;
872
873                         Report.Debug (64, "RESOLVE ITERATOR #1", this, method, method.Parent,
874                                       RootScope, ec);
875
876                         if (!RootScope.ResolveType ())
877                                 return false;
878                         if (!RootScope.ResolveMembers ())
879                                 return false;
880                         if (!RootScope.DefineMembers ())
881                                 return false;
882
883                         ExpressionStatement scope_init = RootScope.GetScopeInitializer (ec);
884                         Container.AddStatement (new StatementExpression (scope_init));
885                         Expression cast = new ClassCast (scope_init, OriginalMethod.ReturnType);
886                         Container.AddStatement (new NoCheckReturn (cast));
887
888                         return true;
889                 }
890
891                 protected override Method DoCreateMethodHost (EmitContext ec)
892                 {
893                         return new AnonymousMethodMethod (
894                                 this, null, TypeManager.system_boolean_expr,
895                                 Modifiers.PUBLIC, new MemberName ("MoveNext", Location),
896                                 Parameters.EmptyReadOnlyParameters);
897                 }
898
899                 protected class MoveNextStatement : Statement {
900                         Iterator iterator;
901
902                         public MoveNextStatement (Iterator iterator, Location loc)
903                         {
904                                 this.loc = loc;
905                                 this.iterator = iterator;
906                         }
907
908                         public override bool Resolve (EmitContext ec)
909                         {
910                                 return iterator.OriginalBlock.Resolve (ec);
911                         }
912
913                         protected override void DoEmit (EmitContext ec)
914                         {
915                                 iterator.EmitMoveNext (ec, iterator.Block);
916                         }
917                 }
918
919                 public Type IteratorType {
920                         get { return IteratorHost.IteratorType; }
921                 }
922
923                 //
924                 // This return statement tricks return into not flagging an error for being
925                 // used in a Yields method
926                 //
927                 class NoCheckReturn : Statement {
928                         public Expression Expr;
929                 
930                         public NoCheckReturn (Expression expr)
931                         {
932                                 Expr = expr;
933                                 loc = expr.Location;
934                         }
935
936                         public override bool Resolve (EmitContext ec)
937                         {
938                                 Expr = Expr.Resolve (ec);
939                                 if (Expr == null)
940                                         return false;
941
942                                 ec.CurrentBranching.CurrentUsageVector.Return ();
943
944                                 return true;
945                         }
946
947                         protected override void DoEmit (EmitContext ec)
948                         {
949                                 Expr.Emit (ec);
950                                 ec.ig.Emit (OpCodes.Ret);
951                         }
952                 }
953
954                 public static Iterator CreateIterator (IMethodData method, TypeContainer parent,
955                                                        GenericMethod generic, int modifiers)
956                 {
957                         bool is_enumerable;
958                         Type iterator_type;
959
960                         if (!CheckType (method.ReturnType, out iterator_type, out is_enumerable)) {
961                                 Report.Error (1624, method.Location,
962                                               "The body of `{0}' cannot be an iterator block " +
963                                               "because `{1}' is not an iterator interface type",
964                                               method.GetSignatureForError (),
965                                               TypeManager.CSharpName (method.ReturnType));
966                                 return null;
967                         }
968
969                         return new Iterator (method, parent, generic, modifiers,
970                                              iterator_type, is_enumerable);
971                 }
972
973                 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
974                 {
975                         original_iterator_type = null;
976                         is_enumerable = false;
977
978                         if (ret == TypeManager.ienumerable_type) {
979                                 original_iterator_type = TypeManager.object_type;
980                                 is_enumerable = true;
981                                 return true;
982                         }
983                         if (ret == TypeManager.ienumerator_type) {
984                                 original_iterator_type = TypeManager.object_type;
985                                 is_enumerable = false;
986                                 return true;
987                         }
988
989 #if GMCS_SOURCE
990                         if (!ret.IsGenericType)
991                                 return false;
992
993                         Type[] args = TypeManager.GetTypeArguments (ret);
994                         if (args.Length != 1)
995                                 return false;
996
997                         Type gt = ret.GetGenericTypeDefinition ();
998                         if (gt == TypeManager.generic_ienumerable_type) {
999                                 original_iterator_type = args [0];
1000                                 is_enumerable = true;
1001                                 return true;
1002                         } else if (gt == TypeManager.generic_ienumerator_type) {
1003                                 original_iterator_type = args [0];
1004                                 is_enumerable = false;
1005                                 return true;
1006                         }
1007 #endif
1008
1009                         return false;
1010                 }
1011         }
1012 }
1013