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