Merge pull request #944 from ermshiperete/bug-novell-496138
[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 // Copyright 2011 Xamarin Inc.
12 //
13
14 using System;
15 using System.Collections.Generic;
16 using Mono.CompilerServices.SymbolWriter;
17
18 #if STATIC
19 using IKVM.Reflection.Emit;
20 #else
21 using System.Reflection.Emit;
22 #endif
23
24 namespace Mono.CSharp
25 {
26         public abstract class YieldStatement<T> : ResumableStatement where T : StateMachineInitializer
27         {
28                 protected Expression expr;
29                 protected bool unwind_protect;
30                 protected T machine_initializer;
31                 int resume_pc;
32                 ExceptionStatement inside_try_block;
33
34                 protected YieldStatement (Expression expr, Location l)
35                 {
36                         this.expr = expr;
37                         loc = l;
38                 }
39
40                 public Expression Expr {
41                         get { return this.expr; }
42                 }
43                 
44                 protected override void CloneTo (CloneContext clonectx, Statement t)
45                 {
46                         var target = (YieldStatement<T>) t;
47                         target.expr = expr.Clone (clonectx);
48                 }
49
50                 protected override void DoEmit (EmitContext ec)
51                 {
52                         machine_initializer.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point);
53                 }
54
55                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
56                 {
57                         expr.FlowAnalysis (fc);
58
59                         RegisterResumePoint ();
60
61                         return false;
62                 }
63
64                 public override bool Resolve (BlockContext bc)
65                 {
66                         expr = expr.Resolve (bc);
67                         if (expr == null)
68                                 return false;
69
70                         machine_initializer = bc.CurrentAnonymousMethod as T;
71                         inside_try_block = bc.CurrentTryBlock;
72                         return true;
73                 }
74
75                 public void RegisterResumePoint ()
76                 {
77                         if (resume_pc != 0)
78                                 return;
79
80                         if (inside_try_block == null) {
81                                 resume_pc = machine_initializer.AddResumePoint (this);
82                         } else {
83                                 resume_pc = inside_try_block.AddResumePoint (this, resume_pc, machine_initializer);
84                                 unwind_protect = true;
85                                 inside_try_block = null;
86                         }
87                 }
88         }
89
90         public class Yield : YieldStatement<Iterator>
91         {
92                 public Yield (Expression expr, Location loc)
93                         : base (expr, loc)
94                 {
95                 }
96
97                 public static bool CheckContext (BlockContext bc, Location loc)
98                 {
99                         if (!bc.CurrentAnonymousMethod.IsIterator) {
100                                 bc.Report.Error (1621, loc,
101                                         "The yield statement cannot be used inside anonymous method blocks");
102                                 return false;
103                         }
104
105                         if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
106                                 bc.Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
107                                 return false;
108                         }
109
110                         return true;
111                 }
112
113                 public override bool Resolve (BlockContext bc)
114                 {
115                         if (!CheckContext (bc, loc))
116                                 return false;
117
118                         if (bc.HasAny (ResolveContext.Options.TryWithCatchScope)) {
119                                 bc.Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause");
120                         }
121
122                         if (bc.HasSet (ResolveContext.Options.CatchScope)) {
123                                 bc.Report.Error (1631, loc, "Cannot yield a value in the body of a catch clause");
124                         }
125
126                         if (!base.Resolve (bc))
127                                 return false;
128
129                         var otype = bc.CurrentIterator.OriginalIteratorType;
130                         if (expr.Type != otype) {
131                                 expr = Convert.ImplicitConversionRequired (bc, expr, otype, loc);
132                                 if (expr == null)
133                                         return false;
134                         }
135
136                         return true;
137                 }
138                 
139                 public override object Accept (StructuralVisitor visitor)
140                 {
141                         return visitor.Visit (this);
142                 }
143         }
144
145         public class YieldBreak : ExitStatement
146         {
147                 Iterator iterator;
148
149                 public YieldBreak (Location l)
150                 {
151                         loc = l;
152                 }
153
154                 protected override bool IsLocalExit {
155                         get {
156                                 return false;
157                         }
158                 }
159
160                 protected override void CloneTo (CloneContext clonectx, Statement target)
161                 {
162                         throw new NotSupportedException ();
163                 }
164
165                 protected override bool DoResolve (BlockContext bc)
166                 {
167                         iterator = bc.CurrentIterator;
168                         return Yield.CheckContext (bc, loc);
169                 }
170
171                 protected override void DoEmit (EmitContext ec)
172                 {
173                         iterator.EmitYieldBreak (ec, unwind_protect);
174                 }
175
176                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
177                 {
178                         return true;
179                 }
180
181                 public override Reachability MarkReachable (Reachability rc)
182                 {
183                         base.MarkReachable (rc);
184                         return Reachability.CreateUnreachable ();
185                 }
186                 
187                 public override object Accept (StructuralVisitor visitor)
188                 {
189                         return visitor.Visit (this);
190                 }
191         }
192
193         public abstract class StateMachine : AnonymousMethodStorey
194         {
195                 public enum State
196                 {
197                         Running = -3, // Used only in CurrentPC, never stored into $PC
198                         Uninitialized = -2,
199                         After = -1,
200                         Start = 0
201                 }
202
203                 Field pc_field;
204                 StateMachineMethod method;
205                 int local_name_idx;
206
207                 protected StateMachine (ParametersBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind)
208                         : base (block, parent, host, tparams, name, kind)
209                 {
210                         OriginalTypeParameters = tparams;
211                 }
212
213                 #region Properties
214
215                 public TypeParameters OriginalTypeParameters { get; private set; }
216
217                 public StateMachineMethod StateMachineMethod {
218                         get {
219                                 return method;
220                         }
221                 }
222
223                 public Field PC {
224                         get {
225                                 return pc_field;
226                         }
227                 }
228
229                 #endregion
230
231                 public void AddEntryMethod (StateMachineMethod method)
232                 {
233                         if (this.method != null)
234                                 throw new InternalErrorException ();
235
236                         this.method = method;
237                         Members.Add (method);
238                 }
239
240                 protected override bool DoDefineMembers ()
241                 {
242                         pc_field = AddCompilerGeneratedField ("$PC", new TypeExpression (Compiler.BuiltinTypes.Int, Location));
243
244                         return base.DoDefineMembers ();
245                 }
246
247                 protected override string GetVariableMangledName (LocalVariable local_info)
248                 {
249                         if (local_info.IsCompilerGenerated)
250                                 return base.GetVariableMangledName (local_info);
251
252                         return "<" + local_info.Name + ">__" + local_name_idx++.ToString ("X");
253                 }
254         }
255
256         class IteratorStorey : StateMachine
257         {
258                 class GetEnumeratorMethod : StateMachineMethod
259                 {
260                         sealed class GetEnumeratorStatement : Statement
261                         {
262                                 readonly IteratorStorey host;
263                                 readonly StateMachineMethod host_method;
264
265                                 Expression new_storey;
266
267                                 public GetEnumeratorStatement (IteratorStorey host, StateMachineMethod host_method)
268                                 {
269                                         this.host = host;
270                                         this.host_method = host_method;
271                                         loc = host_method.Location;
272                                 }
273
274                                 protected override void CloneTo (CloneContext clonectx, Statement target)
275                                 {
276                                         throw new NotSupportedException ();
277                                 }
278
279                                 public override bool Resolve (BlockContext ec)
280                                 {
281                                         TypeExpression storey_type_expr = new TypeExpression (host.Definition, loc);
282                                         List<Expression> init = null;
283                                         if (host.hoisted_this != null) {
284                                                 init = new List<Expression> (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
285                                                 HoistedThis ht = host.hoisted_this;
286                                                 FieldExpr from = new FieldExpr (ht.Field, loc);
287                                                 from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
288                                                 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
289                                         }
290
291                                         if (host.hoisted_params != null) {
292                                                 if (init == null)
293                                                         init = new List<Expression> (host.HoistedParameters.Count);
294
295                                                 for (int i = 0; i < host.hoisted_params.Count; ++i) {
296                                                         HoistedParameter hp = host.hoisted_params [i];
297                                                         HoistedParameter hp_cp = host.hoisted_params_copy [i] ?? hp;
298
299                                                         FieldExpr from = new FieldExpr (hp_cp.Field, loc);
300                                                         from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
301
302                                                         init.Add (new ElementInitializer (hp.Field.Name, from, loc));
303                                                 }
304                                         }
305
306                                         if (init != null) {
307                                                 new_storey = new NewInitialize (storey_type_expr, null,
308                                                         new CollectionOrObjectInitializers (init, loc), loc);
309                                         } else {
310                                                 new_storey = new New (storey_type_expr, null, loc);
311                                         }
312
313                                         new_storey = new_storey.Resolve (ec);
314                                         if (new_storey != null)
315                                                 new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
316
317                                         return true;
318                                 }
319
320                                 protected override void DoEmit (EmitContext ec)
321                                 {
322                                         Label label_init = ec.DefineLabel ();
323
324                                         ec.EmitThis ();
325                                         ec.Emit (OpCodes.Ldflda, host.PC.Spec);
326                                         ec.EmitInt ((int) State.Start);
327                                         ec.EmitInt ((int) State.Uninitialized);
328
329                                         var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve (loc);
330                                         if (m != null)
331                                                 ec.Emit (OpCodes.Call, m);
332
333                                         ec.EmitInt ((int) State.Uninitialized);
334                                         ec.Emit (OpCodes.Bne_Un_S, label_init);
335
336                                         ec.EmitThis ();
337                                         ec.Emit (OpCodes.Ret);
338
339                                         ec.MarkLabel (label_init);
340
341                                         new_storey.Emit (ec);
342                                         ec.Emit (OpCodes.Ret);
343                                 }
344
345                                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
346                                 {
347                                         throw new NotImplementedException ();
348                                 }
349
350                                 public override Reachability MarkReachable (Reachability rc)
351                                 {
352                                         base.MarkReachable (rc);
353                                         return Reachability.CreateUnreachable ();
354                                 }
355                         }
356
357                         GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
358                                 : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name, ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
359                         {
360                         }
361
362                         public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name)
363                         {
364                                 return Create (host, returnType, name, null);
365                         }
366
367                         public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name, Statement statement)
368                         {
369                                 var m = new GetEnumeratorMethod (host, returnType, name);
370                                 var stmt = statement ?? new GetEnumeratorStatement (host, m);
371                                 m.block.AddStatement (stmt);
372                                 return m;
373                         }
374                 }
375
376                 class DisposeMethod : StateMachineMethod
377                 {
378                         sealed class DisposeMethodStatement : Statement
379                         {
380                                 Iterator iterator;
381
382                                 public DisposeMethodStatement (Iterator iterator)
383                                 {
384                                         this.iterator = iterator;
385                                         this.loc = iterator.Location;
386                                 }
387
388                                 protected override void CloneTo (CloneContext clonectx, Statement target)
389                                 {
390                                         throw new NotSupportedException ();
391                                 }
392
393                                 public override bool Resolve (BlockContext ec)
394                                 {
395                                         return true;
396                                 }
397
398                                 protected override void DoEmit (EmitContext ec)
399                                 {
400                                         ec.CurrentAnonymousMethod = iterator;
401                                         iterator.EmitDispose (ec);
402                                 }
403
404                                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
405                                 {
406                                         throw new NotImplementedException ();
407                                 }
408                         }
409
410                         public DisposeMethod (IteratorStorey host)
411                                 : base (host, null, new TypeExpression (host.Compiler.BuiltinTypes.Void, host.Location), Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
412                                         new MemberName ("Dispose", host.Location), ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
413                         {
414                                 host.Members.Add (this);
415
416                                 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
417                         }
418                 }
419
420                 //
421                 // Uses Method as method info
422                 //
423                 class DynamicMethodGroupExpr : MethodGroupExpr
424                 {
425                         readonly Method method;
426
427                         public DynamicMethodGroupExpr (Method method, Location loc)
428                                 : base ((IList<MemberSpec>) null, null, loc)
429                         {
430                                 this.method = method;
431                                 eclass = ExprClass.Unresolved;
432                         }
433
434                         protected override Expression DoResolve (ResolveContext ec)
435                         {
436                                 Methods = new List<MemberSpec> (1) { method.Spec };
437                                 type = method.Parent.Definition;
438                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
439                                 return base.DoResolve (ec);
440                         }
441                 }
442
443                 class DynamicFieldExpr : FieldExpr
444                 {
445                         readonly Field field;
446
447                         public DynamicFieldExpr (Field field, Location loc)
448                                 : base (loc)
449                         {
450                                 this.field = field;
451                         }
452
453                         protected override Expression DoResolve (ResolveContext ec)
454                         {
455                                 spec = field.Spec;
456                                 type = spec.MemberType;
457                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
458                                 return base.DoResolve (ec);
459                         }
460                 }
461
462                 public readonly Iterator Iterator;
463
464                 List<HoistedParameter> hoisted_params_copy;
465
466                 TypeExpr iterator_type_expr;
467                 Field current_field;
468                 Field disposing_field;
469
470                 TypeSpec generic_enumerator_type;
471                 TypeSpec generic_enumerable_type;
472
473                 public IteratorStorey (Iterator iterator)
474                         : base (iterator.Container.ParametersBlock, iterator.Host,
475                           iterator.OriginalMethod as MemberBase, iterator.OriginalMethod.CurrentTypeParameters, "Iterator", MemberKind.Class)
476                 {
477                         this.Iterator = iterator;
478                 }
479
480                 public Field CurrentField {
481                         get {
482                                 return current_field;
483                         }
484                 }
485
486                 public Field DisposingField {
487                         get {
488                                 return disposing_field;
489                         }
490                 }
491
492                 public IList<HoistedParameter> HoistedParameters {
493                         get { return hoisted_params; }
494                 }
495
496                 protected override Constructor DefineDefaultConstructor (bool is_static)
497                 {
498                         var ctor = base.DefineDefaultConstructor (is_static);
499                         ctor.ModFlags |= Modifiers.DEBUGGER_HIDDEN;
500                         return ctor;
501                 }
502
503                 protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
504                 {
505                         var mtype = Iterator.OriginalIteratorType;
506                         if (Mutator != null)
507                                 mtype = Mutator.Mutate (mtype);
508
509                         iterator_type_expr = new TypeExpression (mtype, Location);
510
511                         var ifaces = new List<TypeSpec> (5);
512                         if (Iterator.IsEnumerable) {
513                                 ifaces.Add (Compiler.BuiltinTypes.IEnumerable);
514
515                                 if (Module.PredefinedTypes.IEnumerableGeneric.Define ()) {
516                                         generic_enumerable_type = Module.PredefinedTypes.IEnumerableGeneric.TypeSpec.MakeGenericType (Module, new[] { mtype });
517                                         ifaces.Add (generic_enumerable_type);
518                                 }
519                         }
520
521                         ifaces.Add (Compiler.BuiltinTypes.IEnumerator);
522                         ifaces.Add (Compiler.BuiltinTypes.IDisposable);
523
524                         var ienumerator_generic = Module.PredefinedTypes.IEnumeratorGeneric;
525                         if (ienumerator_generic.Define ()) {
526                                 generic_enumerator_type = ienumerator_generic.TypeSpec.MakeGenericType (Module, new [] { mtype });
527                                 ifaces.Add (generic_enumerator_type);
528                         }
529
530                         base_class = null;
531
532                         base_type = Compiler.BuiltinTypes.Object;
533                         return ifaces.ToArray ();
534                 }
535
536                 protected override bool DoDefineMembers ()
537                 {
538                         current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
539                         disposing_field = AddCompilerGeneratedField ("$disposing", new TypeExpression (Compiler.BuiltinTypes.Bool, Location));
540
541                         if (Iterator.IsEnumerable && hoisted_params != null) {
542                                 //
543                                 // Iterators are independent, each GetEnumerator call has to
544                                 // create same enumerator therefore we have to keep original values
545                                 // around for re-initialization
546                                 //
547                                 hoisted_params_copy = new List<HoistedParameter> (hoisted_params.Count);
548                                 foreach (HoistedParameter hp in hoisted_params) {
549
550                                         //
551                                         // Don't create field copy for unmodified captured parameters
552                                         //
553                                         HoistedParameter hp_copy;
554                                         if (hp.IsAssigned) {
555                                                 hp_copy = new HoistedParameter (hp, "<$>" + hp.Field.Name);
556                                         } else {
557                                                 hp_copy = null;
558                                         }
559
560                                         hoisted_params_copy.Add (hp_copy);
561                                 }
562                         }
563
564                         if (generic_enumerator_type != null)
565                                 Define_Current (true);
566
567                         Define_Current (false);
568                         new DisposeMethod (this);
569                         Define_Reset ();
570
571                         if (Iterator.IsEnumerable) {
572                                 FullNamedExpression explicit_iface = new TypeExpression (Compiler.BuiltinTypes.IEnumerable, Location);
573                                 var name = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null);
574
575                                 if (generic_enumerator_type != null) {
576                                         explicit_iface = new TypeExpression (generic_enumerable_type, Location);
577                                         var gname = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null);
578                                         Method gget_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (generic_enumerator_type, Location), gname);
579
580                                         //
581                                         // Just call generic GetEnumerator implementation
582                                         //
583                                         var stmt = new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), null), Location);
584                                         Method get_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name, stmt);
585
586                                         Members.Add (get_enumerator);
587                                         Members.Add (gget_enumerator);
588                                 } else {
589                                         Members.Add (GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name));
590                                 }
591                         }
592
593                         return base.DoDefineMembers ();
594                 }
595
596                 void Define_Current (bool is_generic)
597                 {
598                         TypeExpr type;
599                         FullNamedExpression explicit_iface;
600
601                         if (is_generic) {
602                                 explicit_iface = new TypeExpression (generic_enumerator_type, Location);
603                                 type = iterator_type_expr;
604                         } else {
605                                 explicit_iface = new TypeExpression (Module.Compiler.BuiltinTypes.IEnumerator, Location);
606                                 type = new TypeExpression (Compiler.BuiltinTypes.Object, Location);
607                         }
608
609                         var name = new MemberName ("Current", null, explicit_iface, Location);
610
611                         ToplevelBlock get_block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location,
612                                 Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
613                         get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location));
614                                 
615                         Property current = new Property (this, type, Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, name, null);
616                         current.Get = new Property.GetMethod (current, Modifiers.COMPILER_GENERATED, null, Location);
617                         current.Get.Block = get_block;
618
619                         Members.Add (current);
620                 }
621
622                 void Define_Reset ()
623                 {
624                         Method reset = new Method (
625                                 this, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
626                                 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
627                                 new MemberName ("Reset", Location),
628                                 ParametersCompiled.EmptyReadOnlyParameters, null);
629                         Members.Add (reset);
630
631                         reset.Block = new ToplevelBlock (Compiler, reset.ParameterInfo, Location,
632                                 Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
633
634                         TypeSpec ex_type = Module.PredefinedTypes.NotSupportedException.Resolve ();
635                         if (ex_type == null)
636                                 return;
637
638                         reset.Block.AddStatement (new Throw (new New (new TypeExpression (ex_type, Location), null, Location), Location));
639                 }
640
641                 protected override void EmitHoistedParameters (EmitContext ec, List<HoistedParameter> hoisted)
642                 {
643                         base.EmitHoistedParameters (ec, hoisted);
644                         if (hoisted_params_copy != null)
645                                 base.EmitHoistedParameters (ec, hoisted_params_copy);
646                 }
647         }
648
649         public class StateMachineMethod : Method
650         {
651                 readonly StateMachineInitializer expr;
652
653                 public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType,
654                         Modifiers mod, MemberName name, ToplevelBlock.Flags blockFlags)
655                         : base (host, returnType, mod | Modifiers.COMPILER_GENERATED,
656                           name, ParametersCompiled.EmptyReadOnlyParameters, null)
657                 {
658                         this.expr = expr;
659                         Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null, blockFlags);
660                 }
661
662                 public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod)
663                 {
664                         EmitContext ec = new EmitContext (this, ig, MemberType, sourceMethod);
665                         ec.CurrentAnonymousMethod = expr;
666
667                         if (expr is AsyncInitializer)
668                                 ec.With (BuilderContext.Options.AsyncBody, true);
669
670                         return ec;
671                 }
672         }
673
674         public abstract class StateMachineInitializer : AnonymousExpression
675         {
676                 sealed class MoveNextBodyStatement : Statement
677                 {
678                         readonly StateMachineInitializer state_machine;
679
680                         public MoveNextBodyStatement (StateMachineInitializer stateMachine)
681                         {
682                                 this.state_machine = stateMachine;
683                                 this.loc = stateMachine.Location;
684                         }
685
686                         protected override void CloneTo (CloneContext clonectx, Statement target)
687                         {
688                                 throw new NotSupportedException ();
689                         }
690
691                         public override bool Resolve (BlockContext ec)
692                         {
693                                 return true;
694                         }
695
696                         protected override void DoEmit (EmitContext ec)
697                         {
698                                 state_machine.EmitMoveNext (ec);
699                         }
700
701                         public override void Emit (EmitContext ec)
702                         {
703                                 // Don't create sequence point
704                                 DoEmit (ec);
705                         }
706
707                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
708                         {
709                                 return state_machine.ReturnType.Kind != MemberKind.Void;
710                         }
711
712                         public override Reachability MarkReachable (Reachability rc)
713                         {
714                                 base.MarkReachable (rc);
715
716                                 if (state_machine.ReturnType.Kind != MemberKind.Void)
717                                         rc = Reachability.CreateUnreachable ();
718
719                                 return rc;
720                         }
721                 }
722
723                 public readonly TypeDefinition Host;
724                 protected StateMachine storey;
725
726                 //
727                 // The state as we generate the machine
728                 //
729                 protected Label move_next_ok;
730                 protected Label move_next_error;
731                 LocalBuilder skip_finally;
732                 protected LocalBuilder current_pc;
733                 protected List<ResumableStatement> resume_points;
734
735                 protected StateMachineInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
736                         : base (block, returnType, block.StartLocation)
737                 {
738                         this.Host = host;
739                 }
740
741                 #region Properties
742
743                 public Label BodyEnd { get; set; }
744
745                 public LocalBuilder CurrentPC
746                 {
747                         get {
748                                 return current_pc;
749                         }
750                 }
751
752                 public LocalBuilder SkipFinally {
753                         get {
754                                 return skip_finally;
755                         }
756                 }
757
758                 public override AnonymousMethodStorey Storey {
759                         get {
760                                 return storey;
761                         }
762                 }
763
764                 #endregion
765
766                 public int AddResumePoint (ResumableStatement stmt)
767                 {
768                         if (resume_points == null)
769                                 resume_points = new List<ResumableStatement> ();
770
771                         resume_points.Add (stmt);
772                         return resume_points.Count;
773                 }
774
775                 public override Expression CreateExpressionTree (ResolveContext ec)
776                 {
777                         throw new NotSupportedException ("ET");
778                 }
779
780                 protected virtual BlockContext CreateBlockContext (BlockContext bc)
781                 {
782                         var ctx = new BlockContext (bc, block, bc.ReturnType);
783                         ctx.CurrentAnonymousMethod = this;
784
785                         ctx.AssignmentInfoOffset = bc.AssignmentInfoOffset;
786                         ctx.EnclosingLoop = bc.EnclosingLoop;
787                         ctx.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
788                         ctx.Switch = bc.Switch;
789
790                         return ctx;
791                 }
792
793                 protected override Expression DoResolve (ResolveContext rc)
794                 {
795                         var bc = (BlockContext) rc;
796                         var ctx = CreateBlockContext (bc);
797
798                         Block.Resolve (ctx);
799
800                         if (!rc.IsInProbingMode) {
801                                 var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc), 0);
802                                 move_next.Block.AddStatement (new MoveNextBodyStatement (this));
803                                 storey.AddEntryMethod (move_next);
804                         }
805
806                         bc.AssignmentInfoOffset = ctx.AssignmentInfoOffset;
807                         eclass = ExprClass.Value;
808                         return this;
809                 }
810
811                 public override void Emit (EmitContext ec)
812                 {
813                         //
814                         // Load state machine instance
815                         //
816                         storey.Instance.Emit (ec);
817                 }
818
819                 void EmitMoveNext_NoResumePoints (EmitContext ec)
820                 {
821                         ec.EmitThis ();
822                         ec.Emit (OpCodes.Ldfld, storey.PC.Spec);
823
824                         ec.EmitThis ();
825                         ec.EmitInt ((int) IteratorStorey.State.After);
826                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
827
828                         // We only care if the PC is zero (start executing) or non-zero (don't do anything)
829                         ec.Emit (OpCodes.Brtrue, move_next_error);
830
831                         BodyEnd = ec.DefineLabel ();
832
833                         var async_init = this as AsyncInitializer;
834                         if (async_init != null)
835                                 ec.BeginExceptionBlock ();
836
837                         block.EmitEmbedded (ec);
838
839                         if (async_init != null)
840                                 async_init.EmitCatchBlock (ec);
841
842                         ec.MarkLabel (BodyEnd);
843
844                         EmitMoveNextEpilogue (ec);
845
846                         ec.MarkLabel (move_next_error);
847
848                         if (ReturnType.Kind != MemberKind.Void) {
849                                 ec.EmitInt (0);
850                                 ec.Emit (OpCodes.Ret);
851                         }
852
853                         ec.MarkLabel (move_next_ok);
854                 }
855
856                 void EmitMoveNext (EmitContext ec)
857                 {
858                         move_next_ok = ec.DefineLabel ();
859                         move_next_error = ec.DefineLabel ();
860
861                         if (resume_points == null) {
862                                 EmitMoveNext_NoResumePoints (ec);
863                                 return;
864                         }
865                         
866                         current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt);
867                         ec.EmitThis ();
868                         ec.Emit (OpCodes.Ldfld, storey.PC.Spec);
869                         ec.Emit (OpCodes.Stloc, current_pc);
870
871                         // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
872                         ec.EmitThis ();
873                         ec.EmitInt ((int) IteratorStorey.State.After);
874                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
875
876                         Label[] labels = new Label[1 + resume_points.Count];
877                         labels[0] = ec.DefineLabel ();
878
879                         bool need_skip_finally = false;
880                         for (int i = 0; i < resume_points.Count; ++i) {
881                                 ResumableStatement s = resume_points[i];
882                                 need_skip_finally |= s is ExceptionStatement;
883                                 labels[i + 1] = s.PrepareForEmit (ec);
884                         }
885
886                         if (need_skip_finally) {
887                                 skip_finally = ec.GetTemporaryLocal (ec.BuiltinTypes.Bool);
888                                 ec.EmitInt (0);
889                                 ec.Emit (OpCodes.Stloc, skip_finally);
890                         }
891
892                         var async_init = this as AsyncInitializer;
893                         if (async_init != null)
894                                 ec.BeginExceptionBlock ();
895
896                         ec.Emit (OpCodes.Ldloc, current_pc);
897                         ec.Emit (OpCodes.Switch, labels);
898
899                         ec.Emit (async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error);
900
901                         ec.MarkLabel (labels[0]);
902
903                         BodyEnd = ec.DefineLabel ();
904
905                         block.EmitEmbedded (ec);
906
907                         ec.MarkLabel (BodyEnd);
908
909                         if (async_init != null) {
910                                 async_init.EmitCatchBlock (ec);
911                         }
912
913                         ec.Mark (Block.Original.EndLocation);
914                         ec.EmitThis ();
915                         ec.EmitInt ((int) IteratorStorey.State.After);
916                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
917
918                         EmitMoveNextEpilogue (ec);
919
920                         ec.MarkLabel (move_next_error);
921                         
922                         if (ReturnType.Kind != MemberKind.Void) {
923                                 ec.EmitInt (0);
924                                 ec.Emit (OpCodes.Ret);
925                         }
926
927                         ec.MarkLabel (move_next_ok);
928
929                         if (ReturnType.Kind != MemberKind.Void) {
930                                 ec.EmitInt (1);
931                                 ec.Emit (OpCodes.Ret);
932                         }
933                 }
934
935                 protected virtual void EmitMoveNextEpilogue (EmitContext ec)
936                 {
937                 }
938
939                 public void EmitLeave (EmitContext ec, bool unwind_protect)
940                 {
941                         // Return ok
942                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
943                 }
944
945                 //
946                 // Called back from YieldStatement
947                 //
948                 public virtual void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
949                 {
950                         //
951                         // Guard against being disposed meantime
952                         //
953                         Label disposed = ec.DefineLabel ();
954                         var iterator = storey as IteratorStorey;
955                         if (iterator != null) {
956                                 ec.EmitThis ();
957                                 ec.Emit (OpCodes.Ldfld, iterator.DisposingField.Spec);
958                                 ec.Emit (OpCodes.Brtrue_S, disposed);
959                         }
960
961                         //
962                         // store resume program-counter
963                         //
964                         ec.EmitThis ();
965                         ec.EmitInt (resume_pc);
966                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
967
968                         if (iterator != null) {
969                                 ec.MarkLabel (disposed);
970                         }
971
972                         // mark finally blocks as disabled
973                         if (unwind_protect && skip_finally != null) {
974                                 ec.EmitInt (1);
975                                 ec.Emit (OpCodes.Stloc, skip_finally);
976                         }
977                 }
978
979                 public void SetStateMachine (StateMachine stateMachine)
980                 {
981                         this.storey = stateMachine;
982                 }
983         }
984
985         //
986         // Iterators are implemented as state machine blocks
987         //
988         public class Iterator : StateMachineInitializer
989         {
990                 sealed class TryFinallyBlockProxyStatement : Statement
991                 {
992                         TryFinallyBlock block;
993                         Iterator iterator;
994
995                         public TryFinallyBlockProxyStatement (Iterator iterator, TryFinallyBlock block)
996                         {
997                                 this.iterator = iterator;
998                                 this.block = block;
999                         }
1000
1001                         protected override void CloneTo (CloneContext clonectx, Statement target)
1002                         {
1003                                 throw new NotSupportedException ();
1004                         }
1005
1006                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1007                         {
1008                                 throw new NotSupportedException ();
1009                         }
1010
1011                         protected override void DoEmit (EmitContext ec)
1012                         {
1013                                 //
1014                                 // Restore redirection for any captured variables
1015                                 //
1016                                 ec.CurrentAnonymousMethod = iterator;
1017
1018                                 using (ec.With (BuilderContext.Options.OmitDebugInfo, !ec.HasMethodSymbolBuilder)) {
1019                                         block.EmitFinallyBody (ec);
1020                                 }
1021                         }
1022                 }
1023
1024                 public readonly IMethodData OriginalMethod;
1025                 public readonly bool IsEnumerable;
1026                 public readonly TypeSpec OriginalIteratorType;
1027                 int finally_hosts_counter;
1028
1029                 public Iterator (ParametersBlock block, IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
1030                         : base (block, host, host.Compiler.BuiltinTypes.Bool)
1031                 {
1032                         this.OriginalMethod = method;
1033                         this.OriginalIteratorType = iterator_type;
1034                         this.IsEnumerable = is_enumerable;
1035                         this.type = method.ReturnType;
1036                 }
1037
1038                 #region Properties
1039
1040                 public ToplevelBlock Container {
1041                         get { return OriginalMethod.Block; }
1042                 }
1043
1044                 public override string ContainerType {
1045                         get { return "iterator"; }
1046                 }
1047
1048                 public override bool IsIterator {
1049                         get { return true; }
1050                 }
1051
1052                 #endregion
1053
1054                 public Method CreateFinallyHost (TryFinallyBlock block)
1055                 {
1056                         var method = new Method (storey, new TypeExpression (storey.Compiler.BuiltinTypes.Void, loc),
1057                                 Modifiers.COMPILER_GENERATED, new MemberName (CompilerGeneratedContainer.MakeName (null, null, "Finally", finally_hosts_counter++), loc),
1058                                 ParametersCompiled.EmptyReadOnlyParameters, null);
1059
1060                         method.Block = new ToplevelBlock (method.Compiler, method.ParameterInfo, loc,
1061                                 ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis);
1062                         method.Block.AddStatement (new TryFinallyBlockProxyStatement (this, block));
1063
1064                         // Cannot it add to storey because it'd be emitted before nested
1065                         // anonoymous methods which could capture shared variable
1066
1067                         return method;
1068                 }
1069
1070                 public void EmitYieldBreak (EmitContext ec, bool unwind_protect)
1071                 {
1072                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
1073                 }
1074
1075                 public override string GetSignatureForError ()
1076                 {
1077                         return OriginalMethod.GetSignatureForError ();
1078                 }
1079
1080                 public override void Emit (EmitContext ec)
1081                 {
1082                         //
1083                         // Load Iterator storey instance
1084                         //
1085                         storey.Instance.Emit (ec);
1086
1087                         //
1088                         // Initialize iterator PC when it's unitialized
1089                         //
1090                         if (IsEnumerable) {
1091                                 ec.Emit (OpCodes.Dup);
1092                                 ec.EmitInt ((int)IteratorStorey.State.Uninitialized);
1093
1094                                 var field = storey.PC.Spec;
1095                                 if (storey.MemberName.IsGeneric) {
1096                                         field = MemberCache.GetMember (Storey.Instance.Type, field);
1097                                 }
1098
1099                                 ec.Emit (OpCodes.Stfld, field);
1100                         }
1101                 }
1102
1103                 public void EmitDispose (EmitContext ec)
1104                 {
1105                         if (resume_points == null)
1106                                 return;
1107
1108                         Label end = ec.DefineLabel ();
1109
1110                         Label[] labels = null;
1111                         for (int i = 0; i < resume_points.Count; ++i) {
1112                                 ResumableStatement s = resume_points[i];
1113                                 Label ret = s.PrepareForDispose (ec, end);
1114                                 if (ret.Equals (end) && labels == null)
1115                                         continue;
1116                                 if (labels == null) {
1117                                         labels = new Label[resume_points.Count + 1];
1118                                         for (int j = 0; j <= i; ++j)
1119                                                 labels[j] = end;
1120                                 }
1121
1122                                 labels[i + 1] = ret;
1123                         }
1124
1125                         if (labels != null) {
1126                                 current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt);
1127                                 ec.EmitThis ();
1128                                 ec.Emit (OpCodes.Ldfld, storey.PC.Spec);
1129                                 ec.Emit (OpCodes.Stloc, current_pc);
1130                         }
1131
1132                         ec.EmitThis ();
1133                         ec.EmitInt (1);
1134                         ec.Emit (OpCodes.Stfld, ((IteratorStorey) storey).DisposingField.Spec);
1135
1136                         ec.EmitThis ();
1137                         ec.EmitInt ((int) IteratorStorey.State.After);
1138                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
1139
1140                         if (labels != null) {
1141                                 //SymbolWriter.StartIteratorDispatcher (ec.ig);
1142                                 ec.Emit (OpCodes.Ldloc, current_pc);
1143                                 ec.Emit (OpCodes.Switch, labels);
1144                                 //SymbolWriter.EndIteratorDispatcher (ec.ig);
1145
1146                                 foreach (ResumableStatement s in resume_points)
1147                                         s.EmitForDispose (ec, current_pc, end, true);
1148                         }
1149
1150                         ec.MarkLabel (end);
1151                 }
1152
1153                 public override void EmitStatement (EmitContext ec)
1154                 {
1155                         throw new NotImplementedException ();
1156                 }
1157
1158                 public override void InjectYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
1159                 {
1160                         // Store the new value into current
1161                         var fe = new FieldExpr (((IteratorStorey) storey).CurrentField, loc);
1162                         fe.InstanceExpression = new CompilerGeneratedThis (storey.CurrentType, loc);
1163                         fe.EmitAssign (ec, expr, false, false);
1164
1165                         base.InjectYield (ec, expr, resume_pc, unwind_protect, resume_point);
1166
1167                         EmitLeave (ec, unwind_protect);
1168
1169                         ec.MarkLabel (resume_point);
1170                 }
1171
1172                 public static void CreateIterator (IMethodData method, TypeDefinition parent, Modifiers modifiers)
1173                 {
1174                         bool is_enumerable;
1175                         TypeSpec iterator_type;
1176
1177                         TypeSpec ret = method.ReturnType;
1178                         if (ret == null)
1179                                 return;
1180
1181                         if (!CheckType (ret, parent, out iterator_type, out is_enumerable)) {
1182                                 parent.Compiler.Report.Error (1624, method.Location,
1183                                               "The body of `{0}' cannot be an iterator block " +
1184                                               "because `{1}' is not an iterator interface type",
1185                                               method.GetSignatureForError (),
1186                                               ret.GetSignatureForError ());
1187                                 return;
1188                         }
1189
1190                         ParametersCompiled parameters = method.ParameterInfo;
1191                         for (int i = 0; i < parameters.Count; i++) {
1192                                 Parameter p = parameters [i];
1193                                 Parameter.Modifier mod = p.ModFlags;
1194                                 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
1195                                         parent.Compiler.Report.Error (1623, p.Location,
1196                                                 "Iterators cannot have ref or out parameters");
1197                                         return;
1198                                 }
1199
1200                                 if (p is ArglistParameter) {
1201                                         parent.Compiler.Report.Error (1636, method.Location,
1202                                                 "__arglist is not allowed in parameter list of iterators");
1203                                         return;
1204                                 }
1205
1206                                 if (parameters.Types [i].IsPointer) {
1207                                         parent.Compiler.Report.Error (1637, p.Location,
1208                                                 "Iterators cannot have unsafe parameters or yield types");
1209                                         return;
1210                                 }
1211                         }
1212
1213                         if ((modifiers & Modifiers.UNSAFE) != 0) {
1214                                 parent.Compiler.Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
1215                         }
1216
1217                         method.Block = method.Block.ConvertToIterator (method, parent, iterator_type, is_enumerable);
1218                 }
1219
1220                 static bool CheckType (TypeSpec ret, TypeContainer parent, out TypeSpec original_iterator_type, out bool is_enumerable)
1221                 {
1222                         original_iterator_type = null;
1223                         is_enumerable = false;
1224
1225                         if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerable) {
1226                                 original_iterator_type = parent.Compiler.BuiltinTypes.Object;
1227                                 is_enumerable = true;
1228                                 return true;
1229                         }
1230                         if (ret.BuiltinType == BuiltinTypeSpec.Type.IEnumerator) {
1231                                 original_iterator_type = parent.Compiler.BuiltinTypes.Object;
1232                                 is_enumerable = false;
1233                                 return true;
1234                         }
1235
1236                         InflatedTypeSpec inflated = ret as InflatedTypeSpec;
1237                         if (inflated == null)
1238                                 return false;
1239
1240                         var member_definition = inflated.MemberDefinition;
1241                         PredefinedType ptype = parent.Module.PredefinedTypes.IEnumerableGeneric;
1242
1243                         if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) {
1244                                 original_iterator_type = inflated.TypeArguments[0];
1245                                 is_enumerable = true;
1246                                 return true;
1247                         }
1248
1249                         ptype = parent.Module.PredefinedTypes.IEnumeratorGeneric;
1250                         if (ptype.Define () && ptype.TypeSpec.MemberDefinition == member_definition) {
1251                                 original_iterator_type = inflated.TypeArguments[0];
1252                                 is_enumerable = false;
1253                                 return true;
1254                         }
1255
1256                         return false;
1257                 }
1258         }
1259 }
1260