Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/iterators.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

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