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