ce2db5b078e222e9222f32262569ea11d5fe9bae
[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 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003 Ximian, Inc.
10 // Copyright 2003-2008 Novell, Inc.
11 //
12
13 // TODO:
14 //    Flow analysis for Yield.
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)
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 void MutateHoistedGenericType (AnonymousMethodStorey storey)
61                 {
62                         expr.MutateHoistedGenericType (storey);
63                 }
64                 
65                 public override bool Resolve (EmitContext ec)
66                 {
67                         expr = expr.Resolve (ec);
68                         if (expr == null)
69                                 return false;
70
71                         Report.Debug (64, "RESOLVE YIELD #1", this, ec, expr, expr.GetType (),
72                                       ec.CurrentAnonymousMethod, ec.CurrentIterator);
73
74                         if (!CheckContext (ec, loc))
75                                 return false;
76
77                         Iterator iterator = ec.CurrentIterator;
78                         if (expr.Type != iterator.OriginalIteratorType) {
79                                 expr = Convert.ImplicitConversionRequired (
80                                         ec, expr, iterator.OriginalIteratorType, loc);
81                                 if (expr == null)
82                                         return false;
83                         }
84
85                         if (!ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
86                                 unwind_protect = ec.CurrentBranching.AddResumePoint (this, loc, out resume_pc);
87
88                         return true;
89                 }
90
91                 protected override void DoEmit (EmitContext ec)
92                 {
93                         ec.CurrentIterator.MarkYield (ec, expr, resume_pc, unwind_protect, resume_point);
94                 }
95
96                 protected override void CloneTo (CloneContext clonectx, Statement t)
97                 {
98                         Yield target = (Yield) t;
99
100                         target.expr = expr.Clone (clonectx);
101                 }
102         }
103
104         public class YieldBreak : ExitStatement {
105                 public YieldBreak (Location l)
106                 {
107                         loc = l;
108                 }
109
110                 public override void Error_FinallyClause ()
111                 {
112                         Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
113                 }
114
115                 protected override void CloneTo (CloneContext clonectx, Statement target)
116                 {
117                         throw new NotSupportedException ();
118                 }
119
120                 protected override bool DoResolve (EmitContext ec)
121                 {
122                         return Yield.CheckContext (ec, loc);
123                 }
124
125                 protected override void DoEmit (EmitContext ec)
126                 {
127                         ec.CurrentIterator.EmitYieldBreak (ec.ig, unwind_protect);
128                 }
129
130                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
131                 {
132                         // nothing to do
133                 }
134         }
135
136         //
137         // Wraps method block into iterator wrapper block
138         //
139         class IteratorStatement : Statement
140         {
141                 Iterator iterator;
142                 ExplicitBlock original_block;
143
144                 public IteratorStatement (Iterator iterator, ExplicitBlock original_block)
145                 {
146                         this.iterator = iterator;
147                         this.original_block = original_block;
148                         this.loc = iterator.Location;
149                 }
150
151                 protected override void CloneTo (CloneContext clonectx, Statement target)
152                 {
153                         IteratorStatement t = (IteratorStatement) target;
154                         t.original_block = (ExplicitBlock) original_block.Clone (clonectx);
155                         t.iterator = (Iterator) iterator.Clone (clonectx);
156                 }
157
158                 public override bool Resolve (EmitContext ec)
159                 {
160                         ec.StartFlowBranching (iterator);
161                         bool ok = original_block.Resolve (ec);
162                         ec.EndFlowBranching ();
163                         return ok;
164                 }
165
166                 protected override void DoEmit (EmitContext ec)
167                 {
168                         iterator.EmitMoveNext (ec, original_block);
169                 }
170
171                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
172                 {
173                         original_block.MutateHoistedGenericType (storey);
174                         iterator.MutateHoistedGenericType (storey);
175                 }
176         }
177
178         public class IteratorStorey : AnonymousMethodStorey
179         {
180                 class IteratorMethod : Method
181                 {
182                         readonly IteratorStorey host;
183
184                         public IteratorMethod (IteratorStorey host, FullNamedExpression returnType, int mod, MemberName name)
185                                 : base (host, null, returnType, mod | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
186                                   name, Parameters.EmptyReadOnlyParameters, null)
187                         {
188                                 this.host = host;
189
190                                 Block = new ToplevelBlock (host.Iterator.Container.Toplevel, null, Location);
191                         }
192
193                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
194                         {
195                                 EmitContext ec = new EmitContext (
196                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
197
198                                 ec.CurrentAnonymousMethod = host.Iterator;
199                                 return ec;
200                         }
201                 }
202
203                 class GetEnumeratorMethod : IteratorMethod
204                 {
205                         sealed class GetEnumeratorStatement : Statement
206                         {
207                                 IteratorStorey host;
208                                 Expression type;
209
210                                 Expression cast;
211
212                                 public GetEnumeratorStatement (IteratorStorey host, Expression type)
213                                 {
214                                         this.host = host;
215                                         this.type = type;
216                                         loc = host.Location;
217                                 }
218
219                                 protected override void CloneTo (CloneContext clonectx, Statement target)
220                                 {
221                                         throw new NotSupportedException ();
222                                 }
223
224                                 public override bool Resolve (EmitContext ec)
225                                 {
226                                         type = type.ResolveAsTypeTerminal (ec, false);
227                                         if ((type == null) || (type.Type == null))
228                                                 return false;
229
230                                         TypeExpression storey_type_expr = new TypeExpression (host.TypeBuilder, loc);
231                                         Expression new_storey;
232                                         ArrayList init = null;
233                                         if (host.hoisted_this != null) {
234                                                 init = new ArrayList (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
235                                                 HoistedThis ht = host.hoisted_this;
236                                                 FieldExpr from = new FieldExpr (ht.Field.FieldBuilder, loc);
237                                                 from.InstanceExpression = CompilerGeneratedThis.Instance;
238                                                 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
239                                         }
240
241                                         if (host.hoisted_params != null) {
242                                                 if (init == null)
243                                                         init = new ArrayList (host.HoistedParameters.Count);
244
245                                                 foreach (HoistedParameter hp in host.HoistedParameters) {
246                                                         FieldExpr from = new FieldExpr (hp.Field.FieldBuilder, loc);
247                                                         from.InstanceExpression = CompilerGeneratedThis.Instance;
248                                                         init.Add (new ElementInitializer (hp.Field.Name, from, loc));
249                                                 }
250                                         }
251
252                                         if (init != null) {
253                                                 new_storey = new NewInitialize (storey_type_expr, new ArrayList (0),
254                                                         new CollectionOrObjectInitializers (init, loc), loc);
255                                         } else {
256                                                 new_storey = new New (storey_type_expr, new ArrayList (0), loc);
257                                         }
258
259                                         new_storey = new_storey.Resolve (ec);
260                                         if (new_storey != null)
261                                                 cast = Convert.ImplicitConversionRequired (ec, new_storey, type.Type, loc);
262
263                                         if (TypeManager.int_interlocked_compare_exchange == null) {
264                                                 Type t = TypeManager.CoreLookupType ("System.Threading", "Interlocked", Kind.Class, true);
265                                                 if (t != null) {
266                                                         TypeManager.int_interlocked_compare_exchange = TypeManager.GetPredefinedMethod (
267                                                                 t, "CompareExchange", loc, TypeManager.int32_type,
268                                                                 TypeManager.int32_type, TypeManager.int32_type);
269                                                 }
270                                         }
271
272                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
273                                         return true;
274                                 }
275
276                                 protected override void DoEmit (EmitContext ec)
277                                 {
278                                         ILGenerator ig = ec.ig;
279                                         Label label_init = ig.DefineLabel ();
280
281                                         ig.Emit (OpCodes.Ldarg_0);
282                                         ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
283                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Start);
284                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
285                                         ig.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange);
286
287                                         ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
288                                         ig.Emit (OpCodes.Bne_Un, label_init);
289
290                                         ig.Emit (OpCodes.Ldarg_0);
291                                         ig.Emit (OpCodes.Ret);
292
293                                         ig.MarkLabel (label_init);
294
295                                         cast.Emit (ec);
296                                         ig.Emit (OpCodes.Ret);
297                                 }
298
299                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
300                                 {
301                                         throw new NotSupportedException ();
302                                 }
303                         }
304
305                         public GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
306                                 : base (host, returnType, 0, name)
307                         {
308                                 Block.AddStatement (new GetEnumeratorStatement (host, type_name));
309                         }
310                 }
311
312                 class DisposeMethod : IteratorMethod
313                 {
314                         sealed class DisposeMethodStatement : Statement
315                         {
316                                 Iterator iterator;
317
318                                 public DisposeMethodStatement (Iterator iterator)
319                                 {
320                                         this.iterator = iterator;
321                                         this.loc = iterator.Location;
322                                 }
323
324                                 protected override void CloneTo (CloneContext clonectx, Statement target)
325                                 {
326                                         throw new NotSupportedException ();
327                                 }
328
329                                 public override bool Resolve (EmitContext ec)
330                                 {
331                                         return true;
332                                 }
333
334                                 protected override void DoEmit (EmitContext ec)
335                                 {
336                                         iterator.EmitDispose (ec);
337                                 }
338
339                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
340                                 {
341                                         throw new NotSupportedException ();
342                                 }
343                         }
344
345                         public DisposeMethod (IteratorStorey host)
346                                 : base (host, TypeManager.system_void_expr, Modifiers.PUBLIC, new MemberName ("Dispose", host.Location))
347                         {
348                                 host.AddMethod (this);
349
350                                 Block = new ToplevelBlock (host.Iterator.Container, null, Location);
351                                 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
352                         }
353                 }
354
355                 //
356                 // Uses Method as method info
357                 //
358                 class DynamicMethodGroupExpr : MethodGroupExpr
359                 {
360                         readonly Method method;
361
362                         public DynamicMethodGroupExpr (Method method, Location loc)
363                                 : base (null, loc)
364                         {
365                                 this.method = method;
366                         }
367
368                         public override Expression DoResolve (EmitContext ec)
369                         {
370                                 Methods = new MethodBase [] { method.MethodBuilder };
371                                 type = method.Parent.TypeBuilder;
372                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
373                                 return base.DoResolve (ec);
374                         }
375                 }
376
377                 public readonly Iterator Iterator;
378
379                 TypeExpr iterator_type_expr;
380                 Field pc_field;
381                 Field current_field;
382
383                 TypeExpr enumerator_type;
384                 TypeExpr enumerable_type;
385 #if GMCS_SOURCE
386                 TypeArguments generic_args;
387                 TypeExpr generic_enumerator_type;
388                 TypeExpr generic_enumerable_type;
389 #else
390                 const TypeArguments generic_args = null;
391 #endif
392
393                 int local_name_idx;
394
395                 public IteratorStorey (Iterator iterator)
396                         : base (iterator.Container.Toplevel, iterator.Host,
397                           iterator.OriginalMethod as MemberBase, iterator.GenericMethod, "Iterator")
398                 {
399                         this.Iterator = iterator;
400                         HasHoistedVariables = true;
401                 }
402
403                 public Field PC {
404                         get { return pc_field; }
405                 }
406
407                 public Field CurrentField {
408                         get { return current_field; }
409                 }
410
411                 public ArrayList HoistedParameters {
412                         get { return hoisted_params; }
413                 }
414
415                 protected override TypeExpr [] ResolveBaseTypes (out TypeExpr base_class)
416                 {
417                         iterator_type_expr = new TypeExpression (MutateType (Iterator.OriginalIteratorType), Location);
418
419 #if GMCS_SOURCE
420                         generic_args = new TypeArguments (Location);
421                         generic_args.Add (iterator_type_expr);
422 #endif
423
424                         ArrayList list = new ArrayList ();
425                         if (Iterator.IsEnumerable) {
426                                 enumerable_type = new TypeExpression (
427                                         TypeManager.ienumerable_type, Location);
428                                 list.Add (enumerable_type);
429
430 #if GMCS_SOURCE
431                                 generic_enumerable_type = new ConstructedType (
432                                         TypeManager.generic_ienumerable_type,
433                                         generic_args, Location);
434                                 list.Add (generic_enumerable_type);
435 #endif
436                         }
437
438                         enumerator_type = new TypeExpression (
439                                 TypeManager.ienumerator_type, Location);
440                         list.Add (enumerator_type);
441
442                         list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
443
444 #if GMCS_SOURCE
445                         generic_enumerator_type = new ConstructedType (
446                                 TypeManager.generic_ienumerator_type,
447                                 generic_args, Location);
448                         list.Add (generic_enumerator_type);
449 #endif
450
451                         type_bases = list;
452
453                         return base.ResolveBaseTypes (out base_class);
454                 }
455
456                 protected override string GetVariableMangledName (LocalInfo local_info)
457                 {
458                         return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
459                 }
460
461                 public void DefineIteratorMembers ()
462                 {
463                         pc_field = AddCompilerGeneratedField ("$PC", TypeManager.system_int32_expr);
464                         current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
465
466 #if GMCS_SOURCE
467                         Define_Current (true);
468 #endif
469                         Define_Current (false);
470                         new DisposeMethod (this);
471                         Define_Reset ();
472
473                         if (Iterator.IsEnumerable) {
474                                 MemberName name = new MemberName (
475                                         new MemberName ("System.Collections.IEnumerable", Location), "GetEnumerator", Location);
476
477 #if GMCS_SOURCE
478                                 Method get_enumerator = new IteratorMethod (this, enumerator_type, 0, name);
479
480                                 name = new MemberName (
481                                         new MemberName ("System.Collections.Generic.IEnumerable", generic_args, Location), "GetEnumerator", Location);
482                                 Method gget_enumerator = new GetEnumeratorMethod (this, generic_enumerator_type, name);
483
484                                 //
485                                 // Just call generic GetEnumerator implementation
486                                 //
487                                 get_enumerator.Block.AddStatement (
488                                         new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), new ArrayList (0)), Location));
489
490                                 AddMethod (get_enumerator);
491                                 AddMethod (gget_enumerator);
492 #else
493                                 AddMethod (new GetEnumeratorMethod (this, enumerator_type, name));
494 #endif
495                         }
496                 }
497
498                 void Define_Current (bool is_generic)
499                 {
500                         MemberName left;
501                         TypeExpr type;
502
503                         if (is_generic) {
504                                 left = new MemberName (
505                                         "System.Collections.Generic.IEnumerator",
506                                         generic_args, Location);
507                                 type = iterator_type_expr;
508                         } else {
509                                 left = new MemberName ("System.Collections.IEnumerator", Location);
510                                 type = TypeManager.system_object_expr;
511                         }
512
513                         MemberName name = new MemberName (left, "Current", null, Location);
514
515                         ToplevelBlock get_block = new ToplevelBlock (Location);
516                         get_block.AddStatement (new CurrentBlock (this, is_generic));
517
518                         Accessor getter = new Accessor (get_block, 0, null, null, Location);
519
520                         Property current = new Property (
521                                 this, type, Modifiers.DEBUGGER_HIDDEN, name, null, getter, null, false);
522                         AddProperty (current);
523                 }
524
525                 void Define_Reset ()
526                 {
527                         Method reset = new Method (
528                                 this, null, TypeManager.system_void_expr,
529                                 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
530                                 new MemberName ("Reset", Location),
531                                 Parameters.EmptyReadOnlyParameters, null);
532                         AddMethod (reset);
533
534                         reset.Block = new ToplevelBlock (Location);
535                         reset.Block.AddStatement (Create_ThrowNotSupported ());
536                 }
537
538                 Statement Create_ThrowNotSupported ()
539                 {
540                         TypeExpr ex_type = new TypeLookupExpression ("System.NotSupportedException");
541
542                         return new Throw (new New (ex_type, null, Location), Location);
543                 }
544
545                 protected class CurrentBlock : Statement {
546                         IteratorStorey host;
547                         bool is_generic;
548
549                         public CurrentBlock (IteratorStorey host, bool is_generic)
550                         {
551                                 this.host = host;
552                                 this.is_generic = is_generic;
553                                 loc = host.Location;
554                         }
555
556                         protected override void CloneTo (CloneContext clonectx, Statement target)
557                         {
558                                 throw new NotSupportedException ();
559                         }
560
561                         public override bool Resolve (EmitContext ec)
562                         {
563                                 // We emit a 'ret', so prevent the enclosing TopLevelBlock from emitting one too
564                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
565                                 return true;
566                         }
567
568                         protected override void DoEmit (EmitContext ec)
569                         {
570                                 ILGenerator ig = ec.ig;
571
572                                 ig.Emit (OpCodes.Ldarg_0);
573                                 ig.Emit (OpCodes.Ldfld, host.CurrentField.FieldBuilder);
574                                 if (!is_generic)
575                                         ig.Emit (OpCodes.Box, host.CurrentField.MemberType);
576                                 ig.Emit (OpCodes.Ret);
577                         }
578
579                         public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
580                         {
581                                 throw new NotSupportedException ();
582                         }
583                 }
584         }
585
586         //
587         // Iterators are implemented as hidden anonymous block
588         //
589         public class Iterator : AnonymousExpression {
590                 public readonly IMethodData OriginalMethod;
591                 AnonymousMethodMethod method;
592                 public readonly TypeContainer Host;
593                 public readonly bool IsEnumerable;
594
595                 //
596                 // The state as we generate the iterator
597                 //
598                 Label move_next_ok, move_next_error;
599                 LocalBuilder skip_finally, current_pc;
600
601                 public LocalBuilder SkipFinally {
602                         get { return skip_finally; }
603                 }
604
605                 public LocalBuilder CurrentPC {
606                         get { return current_pc; }
607                 }
608
609                 public Block Container {
610                         get { return OriginalMethod.Block; }
611                 }
612
613                 public GenericMethod GenericMethod {
614                         get { return OriginalMethod.GenericMethod; }
615                 }
616
617                 public readonly Type OriginalIteratorType;
618
619                 readonly IteratorStorey IteratorHost;
620
621                 public enum State {
622                         Running = -3, // Used only in CurrentPC, never stored into $PC
623                         Uninitialized = -2,
624                         After = -1,
625                         Start = 0
626                 }
627
628                 public override void AddStoreyReference (AnonymousMethodStorey storey)
629                 {
630                         // do nothing
631                 }
632
633                 public void EmitYieldBreak (ILGenerator ig, bool unwind_protect)
634                 {
635                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
636                 }
637
638                 void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block)
639                 {
640                         ILGenerator ig = ec.ig;
641
642                         ig.Emit (OpCodes.Ldarg_0);
643                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
644
645                         ig.Emit (OpCodes.Ldarg_0);
646                         IntConstant.EmitInt (ig, (int) State.After);
647                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
648
649                         // We only care if the PC is zero (start executing) or non-zero (don't do anything)
650                         ig.Emit (OpCodes.Brtrue, move_next_error);
651
652                         SymbolWriter.StartIteratorBody (ec.ig);
653                         original_block.Emit (ec);
654                         SymbolWriter.EndIteratorBody (ec.ig);
655
656                         ig.MarkLabel (move_next_error);
657                         ig.Emit (OpCodes.Ldc_I4_0);
658                         ig.Emit (OpCodes.Ret);
659                 }
660
661                 internal void EmitMoveNext (EmitContext ec, Block original_block)
662                 {
663                         ILGenerator ig = ec.ig;
664
665                         move_next_ok = ig.DefineLabel ();
666                         move_next_error = ig.DefineLabel ();
667
668                         if (resume_points == null) {
669                                 EmitMoveNext_NoResumePoints (ec, original_block);
670                                 return;
671                         }
672
673                         current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
674                         ig.Emit (OpCodes.Ldarg_0);
675                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
676                         ig.Emit (OpCodes.Stloc, current_pc);
677
678                         // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
679                         ig.Emit (OpCodes.Ldarg_0);
680                         IntConstant.EmitInt (ig, (int) State.After);
681                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
682
683                         Label [] labels = new Label [1 + resume_points.Count];
684                         labels [0] = ig.DefineLabel ();
685
686                         bool need_skip_finally = false;
687                         for (int i = 0; i < resume_points.Count; ++i) {
688                                 ResumableStatement s = (ResumableStatement) resume_points [i];
689                                 need_skip_finally |= s is ExceptionStatement;
690                                 labels [i+1] = s.PrepareForEmit (ec);
691                         }
692
693                         if (need_skip_finally) {
694                                 skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type);
695                                 ig.Emit (OpCodes.Ldc_I4_0);
696                                 ig.Emit (OpCodes.Stloc, skip_finally);
697                         }
698
699                         SymbolWriter.StartIteratorDispatcher (ec.ig);
700                         ig.Emit (OpCodes.Ldloc, current_pc);
701                         ig.Emit (OpCodes.Switch, labels);
702
703                         ig.Emit (OpCodes.Br, move_next_error);
704                         SymbolWriter.EndIteratorDispatcher (ec.ig);
705
706                         ig.MarkLabel (labels [0]);
707
708                         SymbolWriter.StartIteratorBody (ec.ig);
709                         original_block.Emit (ec);
710                         SymbolWriter.EndIteratorBody (ec.ig);
711
712                         SymbolWriter.StartIteratorDispatcher (ec.ig);
713
714                         ig.Emit (OpCodes.Ldarg_0);
715                         IntConstant.EmitInt (ig, (int) State.After);
716                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
717
718                         ig.MarkLabel (move_next_error);
719                         ig.Emit (OpCodes.Ldc_I4_0);
720                         ig.Emit (OpCodes.Ret);
721
722                         ig.MarkLabel (move_next_ok);
723                         ig.Emit (OpCodes.Ldc_I4_1);
724                         ig.Emit (OpCodes.Ret);
725
726                         SymbolWriter.EndIteratorDispatcher (ec.ig);
727                 }
728
729                 public void EmitDispose (EmitContext ec)
730                 {
731                         ILGenerator ig = ec.ig;
732
733                         Label end = ig.DefineLabel ();
734
735                         Label [] labels = null;
736                         int n_resume_points = resume_points == null ? 0 : resume_points.Count;
737                         for (int i = 0; i < n_resume_points; ++i) {
738                                 ResumableStatement s = (ResumableStatement) resume_points [i];
739                                 Label ret = s.PrepareForDispose (ec, end);
740                                 if (ret.Equals (end) && labels == null)
741                                         continue;
742                                 if (labels == null) {
743                                         labels = new Label [resume_points.Count + 1];
744                                         for (int j = 0; j <= i; ++j)
745                                                 labels [j] = end;
746                                 }
747                                 labels [i+1] = ret;
748                         }
749
750                         if (labels != null) {
751                                 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
752                                 ig.Emit (OpCodes.Ldarg_0);
753                                 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
754                                 ig.Emit (OpCodes.Stloc, current_pc);
755                         }
756
757                         ig.Emit (OpCodes.Ldarg_0);
758                         IntConstant.EmitInt (ig, (int) State.After);
759                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
760
761                         if (labels != null) {
762                                 //SymbolWriter.StartIteratorDispatcher (ec.ig);
763                                 ig.Emit (OpCodes.Ldloc, current_pc);
764                                 ig.Emit (OpCodes.Switch, labels);
765                                 //SymbolWriter.EndIteratorDispatcher (ec.ig);
766
767                                 foreach (ResumableStatement s in resume_points)
768                                         s.EmitForDispose (ec, this, end, true);
769                         }
770
771                         ig.MarkLabel (end);
772                 }
773
774
775                 ArrayList resume_points;
776                 public int AddResumePoint (ResumableStatement stmt)
777                 {
778                         if (resume_points == null)
779                                 resume_points = new ArrayList ();
780                         resume_points.Add (stmt);
781                         return resume_points.Count;
782                 }
783
784                 //
785                 // Called back from Yield
786                 //
787                 public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
788                 {
789                         ILGenerator ig = ec.ig;
790
791                         // Store the new current
792                         ig.Emit (OpCodes.Ldarg_0);
793                         expr.Emit (ec);
794                         ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
795
796                         // store resume program-counter
797                         ig.Emit (OpCodes.Ldarg_0);
798                         IntConstant.EmitInt (ig, resume_pc);
799                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
800
801                         // mark finally blocks as disabled
802                         if (unwind_protect && skip_finally != null) {
803                                 ig.Emit (OpCodes.Ldc_I4_1);
804                                 ig.Emit (OpCodes.Stloc, skip_finally);
805                         }
806
807                         // Return ok
808                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
809
810                         ig.MarkLabel (resume_point);
811                 }
812
813                 public override string ContainerType {
814                         get { return "iterator"; }
815                 }
816
817                 public override bool IsIterator {
818                         get { return true; }
819                 }
820
821                 public override AnonymousMethodStorey Storey {
822                         get { return IteratorHost; }
823                 }
824
825                 //
826                 // Our constructor
827                 //
828                 private Iterator (IMethodData method, TypeContainer host, Type iterator_type, bool is_enumerable)
829                         : base (
830                                 new ToplevelBlock (method.Block, Parameters.EmptyReadOnlyParameters, method.Block.StartLocation),
831                                 TypeManager.bool_type,
832                                 method.Location)
833                 {
834                         this.OriginalMethod = method;
835                         this.OriginalIteratorType = iterator_type;
836                         this.IsEnumerable = is_enumerable;
837                         this.Host = host;
838
839                         IteratorHost = Block.ChangeToIterator (this, method.Block);
840                 }
841
842                 public override string GetSignatureForError ()
843                 {
844                         return OriginalMethod.GetSignatureForError ();
845                 }
846
847                 public override Expression DoResolve (EmitContext ec)
848                 {
849                         method = new AnonymousMethodMethod (Storey,
850                                 this, Storey, null, TypeManager.system_boolean_expr,
851                                 Modifiers.PUBLIC, OriginalMethod.GetSignatureForError (),
852                                 new MemberName ("MoveNext", Location),
853                                 Parameters.EmptyReadOnlyParameters);
854
855                         if (!Compatible (ec))
856                                 return null;
857
858                         IteratorHost.DefineIteratorMembers ();
859
860                         eclass = ExprClass.Value;
861                         type = ec.ReturnType;
862                         return this;
863                 }
864
865                 public override void Emit (EmitContext ec)
866                 {
867                         //
868                         // Load Iterator storey instance
869                         //
870                         method.Storey.Instance.Emit (ec);
871
872                         //
873                         // Initialize iterator PC when it's unitialized
874                         //
875                         if (IsEnumerable) {
876                                 ILGenerator ig = ec.ig;
877                                 ig.Emit (OpCodes.Dup);
878                                 IntConstant.EmitInt (ig, (int)State.Uninitialized);
879
880                                 FieldInfo field = IteratorHost.PC.FieldBuilder;
881 #if GMCS_SOURCE
882                                 if (Storey.MemberName.IsGeneric)
883                                         field = TypeBuilder.GetField (Storey.Instance.Type, field);
884 #endif
885                                 ig.Emit (OpCodes.Stfld, field);
886                         }
887                 }
888
889                 public override Expression CreateExpressionTree (EmitContext ec)
890                 {
891                         throw new NotSupportedException ("ET");
892                 }
893
894                 public static void CreateIterator (IMethodData method, TypeContainer parent, int modifiers)
895                 {
896                         bool is_enumerable;
897                         Type iterator_type;
898
899                         Type ret = method.ReturnType;
900                         if (ret == null)
901                                 return;
902
903                         if (!CheckType (ret, out iterator_type, out is_enumerable)) {
904                                 Report.Error (1624, method.Location,
905                                               "The body of `{0}' cannot be an iterator block " +
906                                               "because `{1}' is not an iterator interface type",
907                                               method.GetSignatureForError (),
908                                               TypeManager.CSharpName (ret));
909                                 return;
910                         }
911
912                         Parameters parameters = method.ParameterInfo;
913                         for (int i = 0; i < parameters.Count; i++) {
914                                 Parameter p = parameters [i];
915                                 Parameter.Modifier mod = p.ModFlags;
916                                 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
917                                         Report.Error (1623, p.Location,
918                                                 "Iterators cannot have ref or out parameters");
919                                         return;
920                                 }
921
922                                 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
923                                         Report.Error (1636, method.Location,
924                                                 "__arglist is not allowed in parameter list of iterators");
925                                         return;
926                                 }
927
928                                 if (parameters.Types [i].IsPointer) {
929                                         Report.Error (1637, p.Location,
930                                                           "Iterators cannot have unsafe parameters or " +
931                                                           "yield types");
932                                         return;
933                                 }
934                         }
935
936                         if ((modifiers & Modifiers.UNSAFE) != 0) {
937                                 Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
938                                 return;
939                         }
940
941                         Iterator iter = new Iterator (method, parent, iterator_type, is_enumerable);
942                         iter.Storey.DefineType ();
943                 }
944
945                 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
946                 {
947                         original_iterator_type = null;
948                         is_enumerable = false;
949
950                         if (ret == TypeManager.ienumerable_type) {
951                                 original_iterator_type = TypeManager.object_type;
952                                 is_enumerable = true;
953                                 return true;
954                         }
955                         if (ret == TypeManager.ienumerator_type) {
956                                 original_iterator_type = TypeManager.object_type;
957                                 is_enumerable = false;
958                                 return true;
959                         }
960
961                         if (!TypeManager.IsGenericType (ret))
962                                 return false;
963
964                         Type[] args = TypeManager.GetTypeArguments (ret);
965                         if (args.Length != 1)
966                                 return false;
967
968                         Type gt = TypeManager.DropGenericTypeArguments (ret);
969                         if (gt == TypeManager.generic_ienumerable_type) {
970                                 original_iterator_type = args [0];
971                                 is_enumerable = true;
972                                 return true;
973                         }
974                         
975                         if (gt == TypeManager.generic_ienumerator_type) {
976                                 original_iterator_type = args [0];
977                                 is_enumerable = false;
978                                 return true;
979                         }
980
981                         return false;
982                 }
983         }
984 }
985