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