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