2008-01-14 Marek Safar <marek.safar@gmail.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 TypeExpression (
337                                 TypeManager.not_supported_exception_type, Location);
338
339                         return new Throw (new New (ex_type, null, Location), Location);
340                 }
341
342                 ConstructorInfo GetInvalidOperationException ()
343                 {
344                         return TypeManager.GetConstructor (
345                                 TypeManager.invalid_operation_exception_type, Type.EmptyTypes);
346                 }
347
348                 protected override ScopeInitializer CreateScopeInitializer ()
349                 {
350                         return new IteratorHostInitializer (this);
351                 }
352
353                 protected class IteratorHostInitializer : RootScopeInitializer
354                 {
355                         new public readonly IteratorHost Host;
356                         protected Iterator.State state;
357
358                         public IteratorHostInitializer (IteratorHost host)
359                                 : base (host)
360                         {
361                                 this.Host = host;
362                         }
363
364                         protected override bool DoResolveInternal (EmitContext ec)
365                         {
366                                 if (this is EnumeratorScopeInitializer)
367                                         state = Iterator.State.Running;
368                                 else if (Host.Iterator.IsEnumerable)
369                                         state = Iterator.State.Uninitialized;
370                                 else
371                                         state = Iterator.State.Running;
372
373                                 return base.DoResolveInternal (ec);
374                         }
375
376                         protected override void EmitScopeConstructor (EmitContext ec)
377                         {
378                                 ec.ig.Emit (OpCodes.Ldc_I4, (int) state);
379                                 base.EmitScopeConstructor (ec);
380                         }
381                 }
382
383                 protected class GetEnumeratorMethod : Method
384                 {
385                         public IteratorHost Host;
386
387                         static MemberName GetMemberName (IteratorHost host, bool is_generic)
388                         {
389                                 MemberName left;
390                                 if (is_generic) {
391                                         left = new MemberName (
392                                                 "System.Collections.Generic.IEnumerable",
393                                                 host.generic_args, host.Location);
394                                 } else {
395                                         left = new MemberName (
396                                                 "System.Collections.IEnumerable", host.Location);
397                                 }
398
399                                 return new MemberName (left, "GetEnumerator", host.Location);
400                         }
401
402                         public GetEnumeratorMethod (IteratorHost host, bool is_generic)
403                                 : base (host, null, is_generic ?
404                                         host.generic_enumerator_type : host.enumerator_type,
405                                         0, false, GetMemberName (host, is_generic),
406                                         Parameters.EmptyReadOnlyParameters, null)
407                         {
408                                 this.Host = host;
409
410                                 host.AddMethod (this);
411
412                                 Block = new ToplevelBlock (host.Iterator.Container.Toplevel, null, Location);
413                                 Block.AddStatement (new GetEnumeratorStatement (host, Type));
414                         }
415
416                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
417                         {
418                                 EmitContext ec = new EmitContext (
419                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
420
421                                 ec.CurrentAnonymousMethod = Host.Iterator;
422                                 return ec;
423                         }
424
425                         protected class GetEnumeratorStatement : Statement
426                         {
427                                 IteratorHost host;
428                                 Expression type;
429
430                                 ExpressionStatement initializer;
431                                 Expression cast;
432                                 MethodInfo ce;
433
434                                 public GetEnumeratorStatement (IteratorHost host, Expression type)
435                                 {
436                                         this.host = host;
437                                         this.type = type;
438                                         loc = host.Location;
439                                 }
440
441                                 public override bool Resolve (EmitContext ec)
442                                 {
443                                         type = type.ResolveAsTypeTerminal (ec, false);
444                                         if ((type == null) || (type.Type == null))
445                                                 return false;
446
447                                         initializer = host.GetEnumeratorInitializer (ec);
448                                         if (initializer == null)
449                                                 return false;
450
451                                         cast = new ClassCast (initializer, type.Type);
452
453                                         ce = TypeManager.int_interlocked_compare_exchange;
454
455                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
456                                         return true;
457                                 }
458
459                                 protected override void DoEmit (EmitContext ec)
460                                 {
461                                         ILGenerator ig = ec.ig;
462                                         Label label_init = ig.DefineLabel ();
463
464                                         ig.Emit (OpCodes.Ldarg_0);
465                                         ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
466                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running);
467                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
468                                         ig.Emit (OpCodes.Call, ce);
469
470                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
471                                         ig.Emit (OpCodes.Bne_Un, label_init);
472
473                                         ig.Emit (OpCodes.Ldarg_0);
474                                         ig.Emit (OpCodes.Ret);
475
476                                         ig.MarkLabel (label_init);
477
478                                         initializer.EmitStatement (ec);
479                                         cast.Emit (ec);
480                                         ig.Emit (OpCodes.Ret);
481                                 }
482                         }
483                 }
484
485                 protected class DisposeMethod : Method
486                 {
487                         public IteratorHost Host;
488
489                         public DisposeMethod (IteratorHost host)
490                                 : base (host, null, TypeManager.system_void_expr, Modifiers.PUBLIC,
491                                         false, new MemberName ("Dispose", host.Location),
492                                         Parameters.EmptyReadOnlyParameters, null)
493                         {
494                                 this.Host = host;
495
496                                 host.AddMethod (this);
497
498                                 Block = new ToplevelBlock (host.Iterator.Block, null, Location);
499                                 Block.AddStatement (new DisposeMethodStatement (Host.Iterator));
500
501                                 Report.Debug (64, "DISPOSE METHOD", host, Block);
502                         }
503
504                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
505                         {
506                                 EmitContext ec = new EmitContext (
507                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
508
509                                 ec.CurrentAnonymousMethod = Host.Iterator;
510                                 return ec;
511                         }
512
513                         protected class DisposeMethodStatement : Statement
514                         {
515                                 Iterator iterator;
516
517                                 public DisposeMethodStatement (Iterator iterator)
518                                 {
519                                         this.iterator = iterator;
520                                         this.loc = iterator.Location;
521                                 }
522
523                                 public override bool Resolve (EmitContext ec)
524                                 {
525                                         return true;
526                                 }
527
528                                 protected override void DoEmit (EmitContext ec)
529                                 {
530                                         iterator.EmitDispose (ec);
531                                 }
532                         }
533                 }
534
535                 protected ScopeInitializer GetEnumeratorInitializer (EmitContext ec)
536                 {
537                         ScopeInitializer init = new EnumeratorScopeInitializer (this);
538                         if (init.Resolve (ec) == null)
539                                 throw new InternalErrorException ();
540                         return init;
541                 }
542
543                 protected class EnumeratorScopeInitializer : IteratorHostInitializer
544                 {
545                         IteratorHost host;
546
547                         public EnumeratorScopeInitializer (IteratorHost host)
548                                 : base (host)
549                         {
550                                 this.host = host;
551                         }
552
553                         protected override bool DoResolveInternal (EmitContext ec)
554                         {
555                                 type = host.IsGeneric ? host.CurrentType : host.TypeBuilder;
556                                 return base.DoResolveInternal (ec);
557                         }
558
559                         protected override void DoEmit (EmitContext ec)
560                         {
561                                 DoEmitInstance (ec);
562                         }
563
564                         protected override bool IsGetEnumerator {
565                                 get { return true; }
566                         }
567
568                         protected override void EmitParameterReference (EmitContext ec,
569                                                                         CapturedParameter cp)
570                         {
571                                 ec.ig.Emit (OpCodes.Ldarg_0);
572                                 ec.ig.Emit (OpCodes.Ldfld, cp.Field.FieldBuilder);
573                         }
574                 }
575
576                 protected class CurrentBlock : Statement {
577                         IteratorHost host;
578                         bool is_generic;
579
580                         public CurrentBlock (IteratorHost host, bool is_generic)
581                         {
582                                 this.host = host;
583                                 this.is_generic = is_generic;
584                                 loc = host.Location;
585                         }
586
587                         public override bool Resolve (EmitContext ec)
588                         {
589                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
590                                 return true;
591                         }
592
593                         protected override void DoEmit (EmitContext ec)
594                         {
595                                 ILGenerator ig = ec.ig;
596                                 Label label_ok = ig.DefineLabel ();
597
598                                 ig.Emit (OpCodes.Ldarg_0);
599                                 ig.Emit (OpCodes.Ldfld, host.PC.FieldBuilder);
600                                 ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running);
601                                 ig.Emit (OpCodes.Bgt, label_ok);
602
603                                 ig.Emit (OpCodes.Newobj, host.GetInvalidOperationException ());
604                                 ig.Emit (OpCodes.Throw);
605
606                                 ig.MarkLabel (label_ok);
607                                 ig.Emit (OpCodes.Ldarg_0);
608                                 ig.Emit (OpCodes.Ldfld, host.CurrentField.FieldBuilder);
609                                 if (!is_generic)
610                                         ig.Emit (OpCodes.Box, host.CurrentField.MemberType);
611                                 ig.Emit (OpCodes.Ret);
612                         }
613                 }
614         }
615
616         public class Iterator : AnonymousContainer {
617                 protected readonly ToplevelBlock OriginalBlock;
618                 protected readonly IMethodData OriginalMethod;
619                 protected ToplevelBlock block;
620
621                 public readonly bool IsEnumerable;
622                 public readonly bool IsStatic;
623
624                 //
625                 // The state as we generate the iterator
626                 //
627                 Label move_next_ok, move_next_error;
628                 ArrayList resume_points = new ArrayList ();
629                 int pc;
630                 
631                 public readonly Type OriginalIteratorType;
632                 public readonly IteratorHost IteratorHost;
633
634                 public enum State {
635                         Uninitialized   = -2,
636                         After,
637                         Running
638                 }
639
640                 public void EmitYieldBreak (ILGenerator ig)
641                 {
642                         ig.Emit (OpCodes.Ldarg_0);
643                         IntConstant.EmitInt (ig, (int) State.After);
644                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
645                         ig.Emit (OpCodes.Br, move_next_error);
646                 }
647
648                 internal void EmitMoveNext (EmitContext ec, Block original_block)
649                 {
650                         ILGenerator ig = ec.ig;
651
652                         move_next_ok = ig.DefineLabel ();
653                         move_next_error = ig.DefineLabel ();
654
655                         LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type);
656
657                         ig.BeginExceptionBlock ();
658
659                         Label dispatcher = ig.DefineLabel ();
660                         ig.Emit (OpCodes.Br, dispatcher);
661
662                         ResumePoint entry_point = new ResumePoint (null);
663                         resume_points.Add (entry_point);
664                         entry_point.Define (ig);
665
666                         original_block.Emit (ec);
667
668                         EmitYieldBreak (ig);
669
670                         ig.MarkLabel (dispatcher);
671
672                         Label [] labels = new Label [resume_points.Count];
673                         for (int i = 0; i < labels.Length; i++)
674                                 labels [i] = ((ResumePoint) resume_points [i]).Label;
675
676                         ig.Emit (OpCodes.Ldarg_0);
677                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
678                         ig.Emit (OpCodes.Switch, labels);
679
680                         Label end = ig.DefineLabel ();
681
682                         ig.MarkLabel (move_next_error);
683                         ig.Emit (OpCodes.Ldc_I4_0); 
684                         ig.Emit (OpCodes.Stloc, retval);
685                         ig.Emit (OpCodes.Leave, end);
686
687                         ig.MarkLabel (move_next_ok);
688                         ig.Emit (OpCodes.Ldc_I4_1);
689                         ig.Emit (OpCodes.Stloc, retval);
690                         ig.Emit (OpCodes.Leave, end);
691
692                         ig.BeginFaultBlock ();
693
694                         ig.Emit (OpCodes.Ldarg_0);
695                         ig.Emit (OpCodes.Callvirt, IteratorHost.Dispose);
696
697                         ig.EndExceptionBlock ();
698
699                         ig.MarkLabel (end);
700                         ig.Emit (OpCodes.Ldloc, retval);
701                         ig.Emit (OpCodes.Ret);
702                 }
703
704                 public void EmitDispose (EmitContext ec)
705                 {
706                         ILGenerator ig = ec.ig;
707
708                         Label end = ig.DefineLabel ();
709                         Label dispatcher = ig.DefineLabel ();
710                         ig.Emit (OpCodes.Br, dispatcher);
711
712                         Label [] labels = new Label [resume_points.Count];
713                         for (int i = 0; i < labels.Length; i++) {
714                                 ResumePoint point = (ResumePoint) resume_points [i];
715
716                                 if (point.FinallyBlocks == null) {
717                                         labels [i] = end;
718                                         continue;
719                                 }
720
721                                 labels [i] = ig.DefineLabel ();
722                                 ig.MarkLabel (labels [i]);
723
724                                 ig.BeginExceptionBlock ();
725                                 ig.BeginFinallyBlock ();
726
727                                 foreach (ExceptionStatement stmt in point.FinallyBlocks) {
728                                         if (stmt != null)
729                                                 stmt.EmitFinally (ec);
730                                 }
731
732                                 ig.EndExceptionBlock ();
733                                 ig.Emit (OpCodes.Br, end);
734                         }
735
736                         ig.MarkLabel (dispatcher);
737                         ig.Emit (OpCodes.Ldarg_0);
738                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
739                         ig.Emit (OpCodes.Switch, labels);
740
741                         ig.Emit (OpCodes.Ldarg_0);
742                         IntConstant.EmitInt (ig, (int) State.After);
743                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
744
745                         ig.MarkLabel (end);
746                 }
747
748                 protected class ResumePoint
749                 {
750                         public Label Label;
751                         public readonly ExceptionStatement[] FinallyBlocks;
752
753                         public ResumePoint (ArrayList list)
754                         {
755                                 if (list != null) {
756                                         FinallyBlocks = new ExceptionStatement [list.Count];
757                                         list.CopyTo (FinallyBlocks, 0);
758                                 }
759                         }
760
761                         public void Define (ILGenerator ig)
762                         {
763                                 Label = ig.DefineLabel ();
764                                 ig.MarkLabel (Label);
765                         }
766                 }
767
768                 //
769                 // Called back from Yield
770                 //
771                 public void MarkYield (EmitContext ec, Expression expr,
772                                        ArrayList finally_blocks)
773                 {
774                         ILGenerator ig = ec.ig;
775
776                         // Store the new current
777                         ig.Emit (OpCodes.Ldarg_0);
778                         expr.Emit (ec);
779                         ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
780
781                         // increment pc
782                         pc++;
783                         ig.Emit (OpCodes.Ldarg_0);
784                         IntConstant.EmitInt (ig, pc);
785                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
786
787                         // Return ok
788                         ig.Emit (OpCodes.Br, move_next_ok);
789
790                         ResumePoint point = new ResumePoint (finally_blocks);
791                         resume_points.Add (point);
792                         point.Define (ig);
793                 }
794
795                 public void MarkFinally (EmitContext ec, ArrayList finally_blocks)
796                 {
797                         ILGenerator ig = ec.ig;
798
799                         // increment pc
800                         pc++;
801                         ig.Emit (OpCodes.Ldarg_0);
802                         IntConstant.EmitInt (ig, pc);
803                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
804
805                         ResumePoint point = new ResumePoint (finally_blocks);
806                         resume_points.Add (point);
807                         point.Define (ig);
808                 }
809
810                 public override string ContainerType {
811                         get { return "iterator"; }
812                 }
813
814                 public override bool IsIterator {
815                         get { return true; }
816                 }
817
818                 public override RootScopeInfo RootScope {
819                         get { return IteratorHost; }
820                 }
821
822                 public override ScopeInfo Scope {
823                         get { return IteratorHost; }
824                 }
825
826                 //
827                 // Our constructor
828                 //
829                 private Iterator (IMethodData m_container, DeclSpace host, GenericMethod generic,
830                                  int modifiers, Type iterator_type, bool is_enumerable)
831                         : base (host, generic, m_container.ParameterInfo,
832                                 new ToplevelBlock (m_container.ParameterInfo, m_container.Location),
833                                 m_container.Block, TypeManager.bool_type, modifiers,
834                                 m_container.Location)
835                 {
836                         this.OriginalBlock = m_container.Block;
837                         this.OriginalMethod = m_container;
838                         this.OriginalIteratorType = iterator_type;
839                         this.IsEnumerable = is_enumerable;
840
841                         Report.Debug (64, "NEW ITERATOR", host, generic, OriginalBlock,
842                                       Container, Block);
843
844                         IteratorHost = new IteratorHost (this);
845                         Block.CreateIteratorHost (IteratorHost);
846
847                         OriginalBlock.ReParent (Container.Toplevel);
848
849                         m_container.Block = Container.Toplevel;
850
851                         OriginalBlock.MakeIterator (this);
852                 }
853
854                 protected class TestStatement : Statement
855                 {
856                         public override bool Resolve (EmitContext ec)
857                         {
858                                 return true;
859                         }
860
861                         protected override void DoEmit (EmitContext ec)
862                         {
863                                 ec.ig.Emit (OpCodes.Nop);
864                                 ec.ig.Emit (OpCodes.Neg);
865                                 ec.ig.Emit (OpCodes.Pop);
866                                 ec.ig.Emit (OpCodes.Ret);
867                         }
868                 }
869
870                 public override string GetSignatureForError ()
871                 {
872                         return OriginalMethod.GetSignatureForError ();
873                 }
874
875                 public override bool Define (EmitContext ec)
876                 {
877                         Report.Debug (64, "RESOLVE ITERATOR", this, Container, Block);
878
879                         Parameters parameters = OriginalMethod.ParameterInfo;
880                         for (int i = 0; i < parameters.Count; i++){
881                                 Parameter.Modifier mod = parameters.ParameterModifier (i);
882                                 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
883                                         Report.Error (1623, Location,
884                                                       "Iterators cannot have ref or out parameters");
885                                         return false;
886                                 }
887
888                                 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
889                                         Report.Error (1636, Location,
890                                                       "__arglist is not allowed in parameter list " +
891                                                       "of iterators");
892                                         return false;
893                                 }
894
895                                 if (parameters.ParameterType (i).IsPointer) {
896                                         Report.Error (1637, Location,
897                                                       "Iterators cannot have unsafe parameters or " +
898                                                       "yield types");
899                                         return false;
900                                 }
901                         }
902
903                         if ((ModFlags & Modifiers.UNSAFE) != 0) {
904                                 Report.Error (1629, Location, "Unsafe code may not appear in iterators");
905                                 return false;
906                         }
907
908                         if (!base.Define (ec))
909                                 return false;
910
911                         Report.Debug (64, "RESOLVE ITERATOR #1", this, method, method.Parent,
912                                       RootScope, ec);
913
914                         if (!RootScope.ResolveType ())
915                                 return false;
916                         if (!RootScope.ResolveMembers ())
917                                 return false;
918                         if (!RootScope.DefineMembers ())
919                                 return false;
920
921                         ExpressionStatement scope_init = RootScope.GetScopeInitializer (ec);
922                         Container.AddStatement (new StatementExpression (scope_init));
923                         Expression cast = new ClassCast (scope_init, OriginalMethod.ReturnType);
924                         Container.AddStatement (new NoCheckReturn (cast));
925
926                         return true;
927                 }
928
929                 protected override Method DoCreateMethodHost (EmitContext ec)
930                 {
931                         Report.Debug (128, "CREATE METHOD HOST", this, IteratorHost);
932
933                         IteratorHost.CaptureScopes ();
934
935                         return new AnonymousMethodMethod (
936                                 this, RootScope, null, TypeManager.system_boolean_expr,
937                                 Modifiers.PUBLIC, new MemberName ("MoveNext", Location),
938                                 Parameters.EmptyReadOnlyParameters);
939                 }
940
941                 public override Expression DoResolve (EmitContext ec)
942                 {
943                         throw new NotSupportedException ();
944                 }
945
946                 protected class MoveNextStatement : Statement {
947                         Iterator iterator;
948
949                         public MoveNextStatement (Iterator iterator, Location loc)
950                         {
951                                 this.loc = loc;
952                                 this.iterator = iterator;
953                         }
954
955                         public override bool Resolve (EmitContext ec)
956                         {
957                                 return iterator.OriginalBlock.Resolve (ec);
958                         }
959
960                         protected override void DoEmit (EmitContext ec)
961                         {
962                                 iterator.EmitMoveNext (ec, iterator.Block);
963                         }
964                 }
965
966                 public Type IteratorType {
967                         get { return IteratorHost.IteratorType; }
968                 }
969
970                 //
971                 // This return statement tricks return into not flagging an error for being
972                 // used in a Yields method
973                 //
974                 class NoCheckReturn : Statement {
975                         public Expression Expr;
976                 
977                         public NoCheckReturn (Expression expr)
978                         {
979                                 Expr = expr;
980                                 loc = expr.Location;
981                         }
982
983                         public override bool Resolve (EmitContext ec)
984                         {
985                                 Expr = Expr.Resolve (ec);
986                                 if (Expr == null)
987                                         return false;
988
989                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
990
991                                 return true;
992                         }
993
994                         protected override void DoEmit (EmitContext ec)
995                         {
996                                 Expr.Emit (ec);
997                                 ec.ig.Emit (OpCodes.Ret);
998                         }
999                 }
1000
1001                 public static Iterator CreateIterator (IMethodData method, DeclSpace parent,
1002                                                        GenericMethod generic, int modifiers)
1003                 {
1004                         bool is_enumerable;
1005                         Type iterator_type;
1006
1007                         Type ret = method.ReturnType;
1008                         if (ret == null)
1009                                 return null;
1010
1011                         if (!CheckType (ret, out iterator_type, out is_enumerable)) {
1012                                 Report.Error (1624, method.Location,
1013                                               "The body of `{0}' cannot be an iterator block " +
1014                                               "because `{1}' is not an iterator interface type",
1015                                               method.GetSignatureForError (),
1016                                               TypeManager.CSharpName (ret));
1017                                 return null;
1018                         }
1019
1020                         return new Iterator (method, parent, generic, modifiers,
1021                                              iterator_type, is_enumerable);
1022                 }
1023
1024                 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
1025                 {
1026                         original_iterator_type = null;
1027                         is_enumerable = false;
1028
1029                         if (ret == TypeManager.ienumerable_type) {
1030                                 original_iterator_type = TypeManager.object_type;
1031                                 is_enumerable = true;
1032                                 return true;
1033                         }
1034                         if (ret == TypeManager.ienumerator_type) {
1035                                 original_iterator_type = TypeManager.object_type;
1036                                 is_enumerable = false;
1037                                 return true;
1038                         }
1039
1040 #if GMCS_SOURCE
1041                         if (!ret.IsGenericType)
1042                                 return false;
1043
1044                         Type[] args = TypeManager.GetTypeArguments (ret);
1045                         if (args.Length != 1)
1046                                 return false;
1047
1048                         Type gt = ret.GetGenericTypeDefinition ();
1049                         if (gt == TypeManager.generic_ienumerable_type) {
1050                                 original_iterator_type = args [0];
1051                                 is_enumerable = true;
1052                                 return true;
1053                         } else if (gt == TypeManager.generic_ienumerator_type) {
1054                                 original_iterator_type = args [0];
1055                                 is_enumerable = false;
1056                                 return true;
1057                         }
1058 #endif
1059
1060                         return false;
1061                 }
1062         }
1063 }
1064