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