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