Merge pull request #1107 from akoeplinger/fix-numericupdown
[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 (inside_try_block == null) {
78                                 resume_pc = machine_initializer.AddResumePoint (this);
79                         } else {
80                                 resume_pc = inside_try_block.AddResumePoint (this, resume_pc, machine_initializer);
81                                 unwind_protect = true;
82                                 inside_try_block = null;
83                         }
84                 }
85         }
86
87         public class Yield : YieldStatement<Iterator>
88         {
89                 public Yield (Expression expr, Location loc)
90                         : base (expr, loc)
91                 {
92                 }
93
94                 public static bool CheckContext (BlockContext bc, Location loc)
95                 {
96                         if (!bc.CurrentAnonymousMethod.IsIterator) {
97                                 bc.Report.Error (1621, loc,
98                                         "The yield statement cannot be used inside anonymous method blocks");
99                                 return false;
100                         }
101
102                         if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
103                                 bc.Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
104                                 return false;
105                         }
106
107                         return true;
108                 }
109
110                 public override bool Resolve (BlockContext bc)
111                 {
112                         if (!CheckContext (bc, loc))
113                                 return false;
114
115                         if (bc.HasAny (ResolveContext.Options.TryWithCatchScope)) {
116                                 bc.Report.Error (1626, loc, "Cannot yield a value in the body of a try block with a catch clause");
117                         }
118
119                         if (bc.HasSet (ResolveContext.Options.CatchScope)) {
120                                 bc.Report.Error (1631, loc, "Cannot yield a value in the body of a catch clause");
121                         }
122
123                         if (!base.Resolve (bc))
124                                 return false;
125
126                         var otype = bc.CurrentIterator.OriginalIteratorType;
127                         if (expr.Type != otype) {
128                                 expr = Convert.ImplicitConversionRequired (bc, expr, otype, loc);
129                                 if (expr == null)
130                                         return false;
131                         }
132
133                         return true;
134                 }
135                 
136                 public override object Accept (StructuralVisitor visitor)
137                 {
138                         return visitor.Visit (this);
139                 }
140         }
141
142         public class YieldBreak : ExitStatement
143         {
144                 Iterator iterator;
145
146                 public YieldBreak (Location l)
147                 {
148                         loc = l;
149                 }
150
151                 protected override bool IsLocalExit {
152                         get {
153                                 return false;
154                         }
155                 }
156
157                 protected override void CloneTo (CloneContext clonectx, Statement target)
158                 {
159                         throw new NotSupportedException ();
160                 }
161
162                 protected override bool DoResolve (BlockContext bc)
163                 {
164                         iterator = bc.CurrentIterator;
165                         return Yield.CheckContext (bc, loc);
166                 }
167
168                 protected override void DoEmit (EmitContext ec)
169                 {
170                         iterator.EmitYieldBreak (ec, unwind_protect);
171                 }
172
173                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
174                 {
175                         return true;
176                 }
177
178                 public override Reachability MarkReachable (Reachability rc)
179                 {
180                         base.MarkReachable (rc);
181                         return Reachability.CreateUnreachable ();
182                 }
183                 
184                 public override object Accept (StructuralVisitor visitor)
185                 {
186                         return visitor.Visit (this);
187                 }
188         }
189
190         public abstract class StateMachine : AnonymousMethodStorey
191         {
192                 public enum State
193                 {
194                         Running = -3, // Used only in CurrentPC, never stored into $PC
195                         Uninitialized = -2,
196                         After = -1,
197                         Start = 0
198                 }
199
200                 Field pc_field;
201                 StateMachineMethod method;
202                 int local_name_idx;
203
204                 protected StateMachine (ParametersBlock block, TypeDefinition parent, MemberBase host, TypeParameters tparams, string name, MemberKind kind)
205                         : base (block, parent, host, tparams, name, kind)
206                 {
207                         OriginalTypeParameters = tparams;
208                 }
209
210                 #region Properties
211
212                 public TypeParameters OriginalTypeParameters { get; private set; }
213
214                 public StateMachineMethod StateMachineMethod {
215                         get {
216                                 return method;
217                         }
218                 }
219
220                 public Field PC {
221                         get {
222                                 return pc_field;
223                         }
224                 }
225
226                 #endregion
227
228                 public void AddEntryMethod (StateMachineMethod method)
229                 {
230                         if (this.method != null)
231                                 throw new InternalErrorException ();
232
233                         this.method = method;
234                         Members.Add (method);
235                 }
236
237                 protected override bool DoDefineMembers ()
238                 {
239                         pc_field = AddCompilerGeneratedField ("$PC", new TypeExpression (Compiler.BuiltinTypes.Int, Location));
240
241                         return base.DoDefineMembers ();
242                 }
243
244                 protected override string GetVariableMangledName (LocalVariable local_info)
245                 {
246                         if (local_info.IsCompilerGenerated)
247                                 return base.GetVariableMangledName (local_info);
248
249                         return "<" + local_info.Name + ">__" + local_name_idx++.ToString ("X");
250                 }
251         }
252
253         class IteratorStorey : StateMachine
254         {
255                 class GetEnumeratorMethod : StateMachineMethod
256                 {
257                         sealed class GetEnumeratorStatement : Statement
258                         {
259                                 readonly IteratorStorey host;
260                                 readonly StateMachineMethod host_method;
261
262                                 Expression new_storey;
263
264                                 public GetEnumeratorStatement (IteratorStorey host, StateMachineMethod host_method)
265                                 {
266                                         this.host = host;
267                                         this.host_method = host_method;
268                                         loc = host_method.Location;
269                                 }
270
271                                 protected override void CloneTo (CloneContext clonectx, Statement target)
272                                 {
273                                         throw new NotSupportedException ();
274                                 }
275
276                                 public override bool Resolve (BlockContext ec)
277                                 {
278                                         TypeExpression storey_type_expr = new TypeExpression (host.Definition, loc);
279                                         List<Expression> init = null;
280                                         if (host.hoisted_this != null) {
281                                                 init = new List<Expression> (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
282                                                 HoistedThis ht = host.hoisted_this;
283                                                 FieldExpr from = new FieldExpr (ht.Field, loc);
284                                                 from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
285                                                 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
286                                         }
287
288                                         if (host.hoisted_params != null) {
289                                                 if (init == null)
290                                                         init = new List<Expression> (host.HoistedParameters.Count);
291
292                                                 for (int i = 0; i < host.hoisted_params.Count; ++i) {
293                                                         HoistedParameter hp = host.hoisted_params [i];
294                                                         HoistedParameter hp_cp = host.hoisted_params_copy [i] ?? hp;
295
296                                                         FieldExpr from = new FieldExpr (hp_cp.Field, loc);
297                                                         from.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
298
299                                                         init.Add (new ElementInitializer (hp.Field.Name, from, loc));
300                                                 }
301                                         }
302
303                                         if (init != null) {
304                                                 new_storey = new NewInitialize (storey_type_expr, null,
305                                                         new CollectionOrObjectInitializers (init, loc), loc);
306                                         } else {
307                                                 new_storey = new New (storey_type_expr, null, loc);
308                                         }
309
310                                         new_storey = new_storey.Resolve (ec);
311                                         if (new_storey != null)
312                                                 new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
313
314                                         return true;
315                                 }
316
317                                 protected override void DoEmit (EmitContext ec)
318                                 {
319                                         Label label_init = ec.DefineLabel ();
320
321                                         ec.EmitThis ();
322                                         ec.Emit (OpCodes.Ldflda, host.PC.Spec);
323                                         ec.EmitInt ((int) State.Start);
324                                         ec.EmitInt ((int) State.Uninitialized);
325
326                                         var m = ec.Module.PredefinedMembers.InterlockedCompareExchange.Resolve (loc);
327                                         if (m != null)
328                                                 ec.Emit (OpCodes.Call, m);
329
330                                         ec.EmitInt ((int) State.Uninitialized);
331                                         ec.Emit (OpCodes.Bne_Un_S, label_init);
332
333                                         ec.EmitThis ();
334                                         ec.Emit (OpCodes.Ret);
335
336                                         ec.MarkLabel (label_init);
337
338                                         new_storey.Emit (ec);
339                                         ec.Emit (OpCodes.Ret);
340                                 }
341
342                                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
343                                 {
344                                         throw new NotImplementedException ();
345                                 }
346
347                                 public override Reachability MarkReachable (Reachability rc)
348                                 {
349                                         base.MarkReachable (rc);
350                                         return Reachability.CreateUnreachable ();
351                                 }
352                         }
353
354                         GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
355                                 : base (host, null, returnType, Modifiers.DEBUGGER_HIDDEN, name, ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
356                         {
357                         }
358
359                         public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name)
360                         {
361                                 return Create (host, returnType, name, null);
362                         }
363
364                         public static GetEnumeratorMethod Create (IteratorStorey host, FullNamedExpression returnType, MemberName name, Statement statement)
365                         {
366                                 var m = new GetEnumeratorMethod (host, returnType, name);
367                                 var stmt = statement ?? new GetEnumeratorStatement (host, m);
368                                 m.block.AddStatement (stmt);
369                                 return m;
370                         }
371                 }
372
373                 class DisposeMethod : StateMachineMethod
374                 {
375                         sealed class DisposeMethodStatement : Statement
376                         {
377                                 Iterator iterator;
378
379                                 public DisposeMethodStatement (Iterator iterator)
380                                 {
381                                         this.iterator = iterator;
382                                         this.loc = iterator.Location;
383                                 }
384
385                                 protected override void CloneTo (CloneContext clonectx, Statement target)
386                                 {
387                                         throw new NotSupportedException ();
388                                 }
389
390                                 public override bool Resolve (BlockContext ec)
391                                 {
392                                         return true;
393                                 }
394
395                                 protected override void DoEmit (EmitContext ec)
396                                 {
397                                         ec.CurrentAnonymousMethod = iterator;
398                                         iterator.EmitDispose (ec);
399                                 }
400
401                                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
402                                 {
403                                         throw new NotImplementedException ();
404                                 }
405                         }
406
407                         public DisposeMethod (IteratorStorey host)
408                                 : base (host, null, new TypeExpression (host.Compiler.BuiltinTypes.Void, host.Location), Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
409                                         new MemberName ("Dispose", host.Location), ToplevelBlock.Flags.CompilerGenerated | ToplevelBlock.Flags.NoFlowAnalysis)
410                         {
411                                 host.Members.Add (this);
412
413                                 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
414                         }
415                 }
416
417                 //
418                 // Uses Method as method info
419                 //
420                 class DynamicMethodGroupExpr : MethodGroupExpr
421                 {
422                         readonly Method method;
423
424                         public DynamicMethodGroupExpr (Method method, Location loc)
425                                 : base ((IList<MemberSpec>) null, null, loc)
426                         {
427                                 this.method = method;
428                                 eclass = ExprClass.Unresolved;
429                         }
430
431                         protected override Expression DoResolve (ResolveContext ec)
432                         {
433                                 Methods = new List<MemberSpec> (1) { method.Spec };
434                                 type = method.Parent.Definition;
435                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
436                                 return base.DoResolve (ec);
437                         }
438                 }
439
440                 class DynamicFieldExpr : FieldExpr
441                 {
442                         readonly Field field;
443
444                         public DynamicFieldExpr (Field field, Location loc)
445                                 : base (loc)
446                         {
447                                 this.field = field;
448                         }
449
450                         protected override Expression DoResolve (ResolveContext ec)
451                         {
452                                 spec = field.Spec;
453                                 type = spec.MemberType;
454                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
455                                 return base.DoResolve (ec);
456                         }
457                 }
458
459                 public readonly Iterator Iterator;
460
461                 List<HoistedParameter> hoisted_params_copy;
462
463                 TypeExpr iterator_type_expr;
464                 Field current_field;
465                 Field disposing_field;
466
467                 TypeSpec generic_enumerator_type;
468                 TypeSpec generic_enumerable_type;
469
470                 public IteratorStorey (Iterator iterator)
471                         : base (iterator.Container.ParametersBlock, iterator.Host,
472                           iterator.OriginalMethod as MemberBase, iterator.OriginalMethod.CurrentTypeParameters, "Iterator", MemberKind.Class)
473                 {
474                         this.Iterator = iterator;
475                 }
476
477                 public Field CurrentField {
478                         get {
479                                 return current_field;
480                         }
481                 }
482
483                 public Field DisposingField {
484                         get {
485                                 return disposing_field;
486                         }
487                 }
488
489                 public IList<HoistedParameter> HoistedParameters {
490                         get { return hoisted_params; }
491                 }
492
493                 protected override Constructor DefineDefaultConstructor (bool is_static)
494                 {
495                         var ctor = base.DefineDefaultConstructor (is_static);
496                         ctor.ModFlags |= Modifiers.DEBUGGER_HIDDEN;
497                         return ctor;
498                 }
499
500                 protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
501                 {
502                         var mtype = Iterator.OriginalIteratorType;
503                         if (Mutator != null)
504                                 mtype = Mutator.Mutate (mtype);
505
506                         iterator_type_expr = new TypeExpression (mtype, Location);
507
508                         var ifaces = new List<TypeSpec> (5);
509                         if (Iterator.IsEnumerable) {
510                                 ifaces.Add (Compiler.BuiltinTypes.IEnumerable);
511
512                                 if (Module.PredefinedTypes.IEnumerableGeneric.Define ()) {
513                                         generic_enumerable_type = Module.PredefinedTypes.IEnumerableGeneric.TypeSpec.MakeGenericType (Module, new[] { mtype });
514                                         ifaces.Add (generic_enumerable_type);
515                                 }
516                         }
517
518                         ifaces.Add (Compiler.BuiltinTypes.IEnumerator);
519                         ifaces.Add (Compiler.BuiltinTypes.IDisposable);
520
521                         var ienumerator_generic = Module.PredefinedTypes.IEnumeratorGeneric;
522                         if (ienumerator_generic.Define ()) {
523                                 generic_enumerator_type = ienumerator_generic.TypeSpec.MakeGenericType (Module, new [] { mtype });
524                                 ifaces.Add (generic_enumerator_type);
525                         }
526
527                         base_class = null;
528
529                         base_type = Compiler.BuiltinTypes.Object;
530                         return ifaces.ToArray ();
531                 }
532
533                 protected override bool DoDefineMembers ()
534                 {
535                         current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
536                         disposing_field = AddCompilerGeneratedField ("$disposing", new TypeExpression (Compiler.BuiltinTypes.Bool, Location));
537
538                         if (Iterator.IsEnumerable && hoisted_params != null) {
539                                 //
540                                 // Iterators are independent, each GetEnumerator call has to
541                                 // create same enumerator therefore we have to keep original values
542                                 // around for re-initialization
543                                 //
544                                 hoisted_params_copy = new List<HoistedParameter> (hoisted_params.Count);
545                                 foreach (HoistedParameter hp in hoisted_params) {
546
547                                         //
548                                         // Don't create field copy for unmodified captured parameters
549                                         //
550                                         HoistedParameter hp_copy;
551                                         if (hp.IsAssigned) {
552                                                 hp_copy = new HoistedParameter (hp, "<$>" + hp.Field.Name);
553                                         } else {
554                                                 hp_copy = null;
555                                         }
556
557                                         hoisted_params_copy.Add (hp_copy);
558                                 }
559                         }
560
561                         if (generic_enumerator_type != null)
562                                 Define_Current (true);
563
564                         Define_Current (false);
565                         new DisposeMethod (this);
566                         Define_Reset ();
567
568                         if (Iterator.IsEnumerable) {
569                                 FullNamedExpression explicit_iface = new TypeExpression (Compiler.BuiltinTypes.IEnumerable, Location);
570                                 var name = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null);
571
572                                 if (generic_enumerator_type != null) {
573                                         explicit_iface = new TypeExpression (generic_enumerable_type, Location);
574                                         var gname = new MemberName ("GetEnumerator", null, explicit_iface, Location.Null);
575                                         Method gget_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (generic_enumerator_type, Location), gname);
576
577                                         //
578                                         // Just call generic GetEnumerator implementation
579                                         //
580                                         var stmt = new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), null), Location);
581                                         Method get_enumerator = GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name, stmt);
582
583                                         Members.Add (get_enumerator);
584                                         Members.Add (gget_enumerator);
585                                 } else {
586                                         Members.Add (GetEnumeratorMethod.Create (this, new TypeExpression (Compiler.BuiltinTypes.IEnumerator, Location), name));
587                                 }
588                         }
589
590                         return base.DoDefineMembers ();
591                 }
592
593                 void Define_Current (bool is_generic)
594                 {
595                         TypeExpr type;
596                         FullNamedExpression explicit_iface;
597
598                         if (is_generic) {
599                                 explicit_iface = new TypeExpression (generic_enumerator_type, Location);
600                                 type = iterator_type_expr;
601                         } else {
602                                 explicit_iface = new TypeExpression (Module.Compiler.BuiltinTypes.IEnumerator, Location);
603                                 type = new TypeExpression (Compiler.BuiltinTypes.Object, Location);
604                         }
605
606                         var name = new MemberName ("Current", null, explicit_iface, Location);
607
608                         ToplevelBlock get_block = new ToplevelBlock (Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location,
609                                 Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
610                         get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location));
611                                 
612                         Property current = new Property (this, type, Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED, name, null);
613                         current.Get = new Property.GetMethod (current, Modifiers.COMPILER_GENERATED, null, Location);
614                         current.Get.Block = get_block;
615
616                         Members.Add (current);
617                 }
618
619                 void Define_Reset ()
620                 {
621                         Method reset = new Method (
622                                 this, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
623                                 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
624                                 new MemberName ("Reset", Location),
625                                 ParametersCompiled.EmptyReadOnlyParameters, null);
626                         Members.Add (reset);
627
628                         reset.Block = new ToplevelBlock (Compiler, reset.ParameterInfo, Location,
629                                 Block.Flags.CompilerGenerated | Block.Flags.NoFlowAnalysis);
630
631                         TypeSpec ex_type = Module.PredefinedTypes.NotSupportedException.Resolve ();
632                         if (ex_type == null)
633                                 return;
634
635                         reset.Block.AddStatement (new Throw (new New (new TypeExpression (ex_type, Location), null, Location), Location));
636                 }
637
638                 protected override void EmitHoistedParameters (EmitContext ec, List<HoistedParameter> hoisted)
639                 {
640                         base.EmitHoistedParameters (ec, hoisted);
641                         if (hoisted_params_copy != null)
642                                 base.EmitHoistedParameters (ec, hoisted_params_copy);
643                 }
644         }
645
646         public class StateMachineMethod : Method
647         {
648                 readonly StateMachineInitializer expr;
649
650                 public StateMachineMethod (StateMachine host, StateMachineInitializer expr, FullNamedExpression returnType,
651                         Modifiers mod, MemberName name, ToplevelBlock.Flags blockFlags)
652                         : base (host, returnType, mod | Modifiers.COMPILER_GENERATED,
653                           name, ParametersCompiled.EmptyReadOnlyParameters, null)
654                 {
655                         this.expr = expr;
656                         Block = new ToplevelBlock (host.Compiler, ParametersCompiled.EmptyReadOnlyParameters, Location.Null, blockFlags);
657                 }
658
659                 public override EmitContext CreateEmitContext (ILGenerator ig, SourceMethodBuilder sourceMethod)
660                 {
661                         EmitContext ec = new EmitContext (this, ig, MemberType, sourceMethod);
662                         ec.CurrentAnonymousMethod = expr;
663
664                         if (expr is AsyncInitializer)
665                                 ec.With (BuilderContext.Options.AsyncBody, true);
666
667                         return ec;
668                 }
669         }
670
671         public abstract class StateMachineInitializer : AnonymousExpression
672         {
673                 sealed class MoveNextBodyStatement : Statement
674                 {
675                         readonly StateMachineInitializer state_machine;
676
677                         public MoveNextBodyStatement (StateMachineInitializer stateMachine)
678                         {
679                                 this.state_machine = stateMachine;
680                                 this.loc = stateMachine.Location;
681                         }
682
683                         protected override void CloneTo (CloneContext clonectx, Statement target)
684                         {
685                                 throw new NotSupportedException ();
686                         }
687
688                         public override bool Resolve (BlockContext ec)
689                         {
690                                 return true;
691                         }
692
693                         protected override void DoEmit (EmitContext ec)
694                         {
695                                 state_machine.EmitMoveNext (ec);
696                         }
697
698                         public override void Emit (EmitContext ec)
699                         {
700                                 // Don't create sequence point
701                                 DoEmit (ec);
702                         }
703
704                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
705                         {
706                                 return state_machine.ReturnType.Kind != MemberKind.Void;
707                         }
708
709                         public override Reachability MarkReachable (Reachability rc)
710                         {
711                                 base.MarkReachable (rc);
712
713                                 if (state_machine.ReturnType.Kind != MemberKind.Void)
714                                         rc = Reachability.CreateUnreachable ();
715
716                                 return rc;
717                         }
718                 }
719
720                 public readonly TypeDefinition Host;
721                 protected StateMachine storey;
722
723                 //
724                 // The state as we generate the machine
725                 //
726                 Label move_next_ok;
727                 protected Label move_next_error;
728                 LocalBuilder skip_finally;
729                 protected LocalBuilder current_pc;
730                 protected List<ResumableStatement> resume_points;
731
732                 protected StateMachineInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
733                         : base (block, returnType, block.StartLocation)
734                 {
735                         this.Host = host;
736                 }
737
738                 #region Properties
739
740                 public Label BodyEnd { get; set; }
741
742                 public LocalBuilder CurrentPC
743                 {
744                         get {
745                                 return current_pc;
746                         }
747                 }
748
749                 public LocalBuilder SkipFinally {
750                         get {
751                                 return skip_finally;
752                         }
753                 }
754
755                 public override AnonymousMethodStorey Storey {
756                         get {
757                                 return storey;
758                         }
759                 }
760
761                 #endregion
762
763                 public int AddResumePoint (ResumableStatement stmt)
764                 {
765                         if (resume_points == null)
766                                 resume_points = new List<ResumableStatement> ();
767
768                         resume_points.Add (stmt);
769                         return resume_points.Count;
770                 }
771
772                 public override Expression CreateExpressionTree (ResolveContext ec)
773                 {
774                         throw new NotSupportedException ("ET");
775                 }
776
777                 protected virtual BlockContext CreateBlockContext (BlockContext bc)
778                 {
779                         var ctx = new BlockContext (bc, block, bc.ReturnType);
780                         ctx.CurrentAnonymousMethod = this;
781
782                         ctx.AssignmentInfoOffset = bc.AssignmentInfoOffset;
783                         ctx.EnclosingLoop = bc.EnclosingLoop;
784                         ctx.EnclosingLoopOrSwitch = bc.EnclosingLoopOrSwitch;
785                         ctx.Switch = bc.Switch;
786
787                         return ctx;
788                 }
789
790                 protected override Expression DoResolve (ResolveContext rc)
791                 {
792                         var bc = (BlockContext) rc;
793                         var ctx = CreateBlockContext (bc);
794
795                         Block.Resolve (ctx);
796
797                         if (!rc.IsInProbingMode) {
798                                 var move_next = new StateMachineMethod (storey, this, new TypeExpression (ReturnType, loc), Modifiers.PUBLIC, new MemberName ("MoveNext", loc), 0);
799                                 move_next.Block.AddStatement (new MoveNextBodyStatement (this));
800                                 storey.AddEntryMethod (move_next);
801                         }
802
803                         bc.AssignmentInfoOffset = ctx.AssignmentInfoOffset;
804                         eclass = ExprClass.Value;
805                         return this;
806                 }
807
808                 public override void Emit (EmitContext ec)
809                 {
810                         //
811                         // Load state machine instance
812                         //
813                         storey.Instance.Emit (ec);
814                 }
815
816                 void EmitMoveNext_NoResumePoints (EmitContext ec)
817                 {
818                         ec.EmitThis ();
819                         ec.Emit (OpCodes.Ldfld, storey.PC.Spec);
820
821                         ec.EmitThis ();
822                         ec.EmitInt ((int) IteratorStorey.State.After);
823                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
824
825                         // We only care if the PC is zero (start executing) or non-zero (don't do anything)
826                         ec.Emit (OpCodes.Brtrue, move_next_error);
827
828                         BodyEnd = ec.DefineLabel ();
829
830                         block.EmitEmbedded (ec);
831
832                         ec.MarkLabel (BodyEnd);
833
834                         EmitMoveNextEpilogue (ec);
835
836                         ec.MarkLabel (move_next_error);
837
838                         if (ReturnType.Kind != MemberKind.Void) {
839                                 ec.EmitInt (0);
840                                 ec.Emit (OpCodes.Ret);
841                         }
842                 }
843
844                 void EmitMoveNext (EmitContext ec)
845                 {
846                         move_next_ok = ec.DefineLabel ();
847                         move_next_error = ec.DefineLabel ();
848
849                         if (resume_points == null) {
850                                 EmitMoveNext_NoResumePoints (ec);
851                                 return;
852                         }
853                         
854                         current_pc = ec.GetTemporaryLocal (ec.BuiltinTypes.UInt);
855                         ec.EmitThis ();
856                         ec.Emit (OpCodes.Ldfld, storey.PC.Spec);
857                         ec.Emit (OpCodes.Stloc, current_pc);
858
859                         // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
860                         ec.EmitThis ();
861                         ec.EmitInt ((int) IteratorStorey.State.After);
862                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
863
864                         Label[] labels = new Label[1 + resume_points.Count];
865                         labels[0] = ec.DefineLabel ();
866
867                         bool need_skip_finally = false;
868                         for (int i = 0; i < resume_points.Count; ++i) {
869                                 ResumableStatement s = resume_points[i];
870                                 need_skip_finally |= s is ExceptionStatement;
871                                 labels[i + 1] = s.PrepareForEmit (ec);
872                         }
873
874                         if (need_skip_finally) {
875                                 skip_finally = ec.GetTemporaryLocal (ec.BuiltinTypes.Bool);
876                                 ec.EmitInt (0);
877                                 ec.Emit (OpCodes.Stloc, skip_finally);
878                         }
879
880                         var async_init = this as AsyncInitializer;
881                         if (async_init != null)
882                                 ec.BeginExceptionBlock ();
883
884                         ec.Emit (OpCodes.Ldloc, current_pc);
885                         ec.Emit (OpCodes.Switch, labels);
886
887                         ec.Emit (async_init != null ? OpCodes.Leave : OpCodes.Br, move_next_error);
888
889                         ec.MarkLabel (labels[0]);
890
891                         BodyEnd = ec.DefineLabel ();
892
893                         block.EmitEmbedded (ec);
894
895                         ec.MarkLabel (BodyEnd);
896
897                         if (async_init != null) {
898                                 var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location);
899
900                                 ec.BeginCatchBlock (catch_value.Type);
901                                 catch_value.EmitAssign (ec);
902
903                                 ec.EmitThis ();
904                                 ec.EmitInt ((int) IteratorStorey.State.After);
905                                 ec.Emit (OpCodes.Stfld, storey.PC.Spec);
906
907                                 ((AsyncTaskStorey) async_init.Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location));
908
909                                 ec.Emit (OpCodes.Leave, move_next_ok);
910                                 ec.EndExceptionBlock ();
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