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