2 // iterators.cs: Support for implementing iterators
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
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.
15 // Flow analysis for Yield.
19 using System
.Collections
.Generic
;
20 using Mono
.CompilerServices
.SymbolWriter
;
23 using IKVM
.Reflection
.Emit
;
25 using System
.Reflection
.Emit
;
30 public abstract class YieldStatement
<T
> : ResumableStatement where T
: StateMachineInitializer
32 protected Expression expr
;
33 protected bool unwind_protect
;
34 protected T machine_initializer
;
37 protected YieldStatement (Expression expr
, Location l
)
43 public Expression Expr
{
44 get { return this.expr; }
47 protected override void CloneTo (CloneContext clonectx
, Statement t
)
49 var target
= (YieldStatement
<T
>) t
;
50 target
.expr
= expr
.Clone (clonectx
);
53 protected override void DoEmit (EmitContext ec
)
55 machine_initializer
.InjectYield (ec
, expr
, resume_pc
, unwind_protect
, resume_point
);
58 public override bool Resolve (BlockContext bc
)
60 expr
= expr
.Resolve (bc
);
64 machine_initializer
= bc
.CurrentAnonymousMethod
as T
;
66 if (!bc
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
67 unwind_protect
= bc
.CurrentBranching
.AddResumePoint (this, out resume_pc
);
73 public class Yield
: YieldStatement
<Iterator
>
75 public Yield (Expression expr
, Location loc
)
80 public static bool CheckContext (ResolveContext ec
, Location loc
)
82 if (!ec
.CurrentAnonymousMethod
.IsIterator
) {
83 ec
.Report
.Error (1621, loc
,
84 "The yield statement cannot be used inside anonymous method blocks");
91 public override bool Resolve (BlockContext bc
)
93 if (!CheckContext (bc
, loc
))
96 if (!base.Resolve (bc
))
99 var otype
= bc
.CurrentIterator
.OriginalIteratorType
;
100 if (expr
.Type
!= otype
) {
101 expr
= Convert
.ImplicitConversionRequired (bc
, expr
, otype
, loc
);
109 public override object Accept (StructuralVisitor visitor
)
111 return visitor
.Visit (this);
115 public class YieldBreak
: ExitStatement
119 public YieldBreak (Location l
)
124 public override void Error_FinallyClause (Report Report
)
126 Report
.Error (1625, loc
, "Cannot yield in the body of a finally clause");
129 protected override void CloneTo (CloneContext clonectx
, Statement target
)
131 throw new NotSupportedException ();
134 protected override bool DoResolve (BlockContext ec
)
136 iterator
= ec
.CurrentIterator
;
137 return Yield
.CheckContext (ec
, loc
);
140 protected override void DoEmit (EmitContext ec
)
142 iterator
.EmitYieldBreak (ec
, unwind_protect
);
145 public override object Accept (StructuralVisitor visitor
)
147 return visitor
.Visit (this);
151 public abstract class StateMachine
: AnonymousMethodStorey
155 Running
= -3, // Used only in CurrentPC, never stored into $PC
162 StateMachineMethod method
;
165 protected StateMachine (Block block
, TypeDefinition parent
, MemberBase host
, TypeParameters tparams
, string name
, MemberKind kind
)
166 : base (block
, parent
, host
, tparams
, name
, kind
)
172 public StateMachineMethod StateMachineMethod
{
186 public void AddEntryMethod (StateMachineMethod method
)
188 if (this.method
!= null)
189 throw new InternalErrorException ();
191 this.method
= method
;
192 Members
.Add (method
);
195 protected override bool DoDefineMembers ()
197 pc_field
= AddCompilerGeneratedField ("$PC", new TypeExpression (Compiler
.BuiltinTypes
.Int
, Location
));
199 return base.DoDefineMembers ();
202 protected override string GetVariableMangledName (LocalVariable local_info
)
204 if (local_info
.IsCompilerGenerated
)
205 return base.GetVariableMangledName (local_info
);
207 return "<" + local_info
.Name
+ ">__" + local_name_idx
++.ToString ("X");
211 class IteratorStorey
: StateMachine
213 class GetEnumeratorMethod
: StateMachineMethod
215 sealed class GetEnumeratorStatement
: Statement
217 readonly IteratorStorey host
;
218 readonly StateMachineMethod host_method
;
220 Expression new_storey
;
222 public GetEnumeratorStatement (IteratorStorey host
, StateMachineMethod host_method
)
225 this.host_method
= host_method
;
226 loc
= host_method
.Location
;
229 protected override void CloneTo (CloneContext clonectx
, Statement target
)
231 throw new NotSupportedException ();
234 public override bool Resolve (BlockContext ec
)
236 TypeExpression storey_type_expr
= new TypeExpression (host
.Definition
, loc
);
237 List
<Expression
> init
= null;
238 if (host
.hoisted_this
!= null) {
239 init
= new List
<Expression
> (host
.hoisted_params
== null ? 1 : host
.HoistedParameters
.Count
+ 1);
240 HoistedThis ht
= host
.hoisted_this
;
241 FieldExpr
from = new FieldExpr (ht
.Field
, loc
);
242 from.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
243 init
.Add (new ElementInitializer (ht
.Field
.Name
, from, loc
));
246 if (host
.hoisted_params
!= null) {
248 init
= new List
<Expression
> (host
.HoistedParameters
.Count
);
250 for (int i
= 0; i
< host
.hoisted_params
.Count
; ++i
) {
251 HoistedParameter hp
= host
.hoisted_params
[i
];
252 HoistedParameter hp_cp
= host
.hoisted_params_copy
[i
];
254 FieldExpr
from = new FieldExpr (hp_cp
.Field
, loc
);
255 from.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
257 init
.Add (new ElementInitializer (hp
.Field
.Name
, from, loc
));
262 new_storey
= new NewInitialize (storey_type_expr
, null,
263 new CollectionOrObjectInitializers (init
, loc
), loc
);
265 new_storey
= new New (storey_type_expr
, null, loc
);
268 new_storey
= new_storey
.Resolve (ec
);
269 if (new_storey
!= null)
270 new_storey
= Convert
.ImplicitConversionRequired (ec
, new_storey
, host_method
.MemberType
, loc
);
272 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
276 protected override void DoEmit (EmitContext ec
)
278 Label label_init
= ec
.DefineLabel ();
281 ec
.Emit (OpCodes
.Ldflda
, host
.PC
.Spec
);
282 ec
.EmitInt ((int) State
.Start
);
283 ec
.EmitInt ((int) State
.Uninitialized
);
285 var m
= ec
.Module
.PredefinedMembers
.InterlockedCompareExchange
.Resolve (loc
);
287 ec
.Emit (OpCodes
.Call
, m
);
289 ec
.EmitInt ((int) State
.Uninitialized
);
290 ec
.Emit (OpCodes
.Bne_Un_S
, label_init
);
293 ec
.Emit (OpCodes
.Ret
);
295 ec
.MarkLabel (label_init
);
297 new_storey
.Emit (ec
);
298 ec
.Emit (OpCodes
.Ret
);
302 GetEnumeratorMethod (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
303 : base (host
, null, returnType
, Modifiers
.DEBUGGER_HIDDEN
, name
)
307 public static GetEnumeratorMethod
Create (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
309 return Create (host
, returnType
, name
, null);
312 public static GetEnumeratorMethod
Create (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
, Statement statement
)
314 var m
= new GetEnumeratorMethod (host
, returnType
, name
);
315 var stmt
= statement
?? new GetEnumeratorStatement (host
, m
);
316 m
.block
.AddStatement (stmt
);
317 m
.block
.IsCompilerGenerated
= true;
322 class DisposeMethod
: StateMachineMethod
324 sealed class DisposeMethodStatement
: Statement
328 public DisposeMethodStatement (Iterator iterator
)
330 this.iterator
= iterator
;
331 this.loc
= iterator
.Location
;
334 protected override void CloneTo (CloneContext clonectx
, Statement target
)
336 throw new NotSupportedException ();
339 public override bool Resolve (BlockContext ec
)
344 protected override void DoEmit (EmitContext ec
)
346 ec
.CurrentAnonymousMethod
= iterator
;
347 iterator
.EmitDispose (ec
);
351 public DisposeMethod (IteratorStorey host
)
352 : base (host
, null, new TypeExpression (host
.Compiler
.BuiltinTypes
.Void
, host
.Location
), Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
,
353 new MemberName ("Dispose", host
.Location
))
355 host
.Members
.Add (this);
357 Block
.AddStatement (new DisposeMethodStatement (host
.Iterator
));
358 Block
.IsCompilerGenerated
= true;
363 // Uses Method as method info
365 class DynamicMethodGroupExpr
: MethodGroupExpr
367 readonly Method method
;
369 public DynamicMethodGroupExpr (Method method
, Location loc
)
370 : base ((IList
<MemberSpec
>) null, null, loc
)
372 this.method
= method
;
373 eclass
= ExprClass
.Unresolved
;
376 protected override Expression
DoResolve (ResolveContext ec
)
378 Methods
= new List
<MemberSpec
> (1) { method.Spec }
;
379 type
= method
.Parent
.Definition
;
380 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
381 return base.DoResolve (ec
);
385 class DynamicFieldExpr
: FieldExpr
387 readonly Field field
;
389 public DynamicFieldExpr (Field field
, Location loc
)
395 protected override Expression
DoResolve (ResolveContext ec
)
398 type
= spec
.MemberType
;
399 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
400 return base.DoResolve (ec
);
404 public readonly Iterator Iterator
;
406 List
<HoistedParameter
> hoisted_params_copy
;
408 TypeExpr iterator_type_expr
;
410 Field disposing_field
;
412 TypeSpec generic_enumerator_type
;
413 TypeSpec generic_enumerable_type
;
415 public IteratorStorey (Iterator iterator
)
416 : base (iterator
.Container
.ParametersBlock
, iterator
.Host
,
417 iterator
.OriginalMethod
as MemberBase
, iterator
.OriginalMethod
.CurrentTypeParameters
, "Iterator", MemberKind
.Class
)
419 this.Iterator
= iterator
;
422 public Field CurrentField
{
424 return current_field
;
428 public Field DisposingField
{
430 return disposing_field
;
434 public IList
<HoistedParameter
> HoistedParameters
{
435 get { return hoisted_params; }
438 protected override TypeSpec
[] ResolveBaseTypes (out FullNamedExpression base_class
)
440 var mtype
= Iterator
.OriginalIteratorType
;
442 mtype
= Mutator
.Mutate (mtype
);
444 iterator_type_expr
= new TypeExpression (mtype
, Location
);
446 var ifaces
= new List
<TypeSpec
> (5);
447 if (Iterator
.IsEnumerable
) {
448 ifaces
.Add (Compiler
.BuiltinTypes
.IEnumerable
);
450 if (Module
.PredefinedTypes
.IEnumerableGeneric
.Define ()) {
451 generic_enumerable_type
= Module
.PredefinedTypes
.IEnumerableGeneric
.TypeSpec
.MakeGenericType (Module
, new[] { mtype }
);
452 ifaces
.Add (generic_enumerable_type
);
456 ifaces
.Add (Compiler
.BuiltinTypes
.IEnumerator
);
457 ifaces
.Add (Compiler
.BuiltinTypes
.IDisposable
);
459 var ienumerator_generic
= Module
.PredefinedTypes
.IEnumeratorGeneric
;
460 if (ienumerator_generic
.Define ()) {
461 generic_enumerator_type
= ienumerator_generic
.TypeSpec
.MakeGenericType (Module
, new [] { mtype }
);
462 ifaces
.Add (generic_enumerator_type
);
467 base_type
= Compiler
.BuiltinTypes
.Object
;
468 return ifaces
.ToArray ();
471 protected override bool DoDefineMembers ()
473 current_field
= AddCompilerGeneratedField ("$current", iterator_type_expr
);
474 disposing_field
= AddCompilerGeneratedField ("$disposing", new TypeExpression (Compiler
.BuiltinTypes
.Bool
, Location
));
476 if (hoisted_params
!= null) {
478 // Iterators are independent, each GetEnumerator call has to
479 // create same enumerator therefore we have to keep original values
480 // around for re-initialization
482 // TODO: Do it for assigned/modified parameters only
484 hoisted_params_copy
= new List
<HoistedParameter
> (hoisted_params
.Count
);
485 foreach (HoistedParameter hp
in hoisted_params
) {
486 hoisted_params_copy
.Add (new HoistedParameter (hp
, "<$>" + hp
.Field
.Name
));
490 if (generic_enumerator_type
!= null)
491 Define_Current (true);
493 Define_Current (false);
494 new DisposeMethod (this);
497 if (Iterator
.IsEnumerable
) {
498 FullNamedExpression explicit_iface
= new TypeExpression (Compiler
.BuiltinTypes
.IEnumerable
, Location
);
499 var name
= new MemberName ("GetEnumerator", null, explicit_iface
, Location
.Null
);
501 if (generic_enumerator_type
!= null) {
502 explicit_iface
= new TypeExpression (generic_enumerable_type
, Location
);
503 var gname
= new MemberName ("GetEnumerator", null, explicit_iface
, Location
.Null
);
504 Method gget_enumerator
= GetEnumeratorMethod
.Create (this, new TypeExpression (generic_enumerator_type
, Location
), gname
);
507 // Just call generic GetEnumerator implementation
509 var stmt
= new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator
, Location
), null), Location
);
510 Method get_enumerator
= GetEnumeratorMethod
.Create (this, new TypeExpression (Compiler
.BuiltinTypes
.IEnumerator
, Location
), name
, stmt
);
512 Members
.Add (get_enumerator
);
513 Members
.Add (gget_enumerator
);
515 Members
.Add (GetEnumeratorMethod
.Create (this, new TypeExpression (Compiler
.BuiltinTypes
.IEnumerator
, Location
), name
));
519 return base.DoDefineMembers ();
522 void Define_Current (bool is_generic
)
525 FullNamedExpression explicit_iface
;
528 explicit_iface
= new TypeExpression (generic_enumerator_type
, Location
);
529 type
= iterator_type_expr
;
531 explicit_iface
= new TypeExpression (Module
.Compiler
.BuiltinTypes
.IEnumerator
, Location
);
532 type
= new TypeExpression (Compiler
.BuiltinTypes
.Object
, Location
);
535 var name
= new MemberName ("Current", null, explicit_iface
, Location
);
537 ToplevelBlock get_block
= new ToplevelBlock (Compiler
, Location
) {
538 IsCompilerGenerated
= true
540 get_block
.AddStatement (new Return (new DynamicFieldExpr (CurrentField
, Location
), Location
));
542 Property current
= new Property (this, type
, Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.COMPILER_GENERATED
, name
, null);
543 current
.Get
= new Property
.GetMethod (current
, Modifiers
.COMPILER_GENERATED
, null, Location
);
544 current
.Get
.Block
= get_block
;
546 Members
.Add (current
);
551 Method reset
= new Method (
552 this, new TypeExpression (Compiler
.BuiltinTypes
.Void
, Location
),
553 Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.COMPILER_GENERATED
,
554 new MemberName ("Reset", Location
),
555 ParametersCompiled
.EmptyReadOnlyParameters
, null);
558 reset
.Block
= new ToplevelBlock (Compiler
, Location
) {
559 IsCompilerGenerated
= true
562 TypeSpec ex_type
= Module
.PredefinedTypes
.NotSupportedException
.Resolve ();
566 reset
.Block
.AddStatement (new Throw (new New (new TypeExpression (ex_type
, Location
), null, Location
), Location
));
569 protected override void EmitHoistedParameters (EmitContext ec
, IList
<HoistedParameter
> hoisted
)
571 base.EmitHoistedParameters (ec
, hoisted
);
572 base.EmitHoistedParameters (ec
, hoisted_params_copy
);
576 public class StateMachineMethod
: Method
578 readonly StateMachineInitializer expr
;
580 public StateMachineMethod (StateMachine host
, StateMachineInitializer expr
, FullNamedExpression returnType
, Modifiers mod
, MemberName name
)
581 : base (host
, returnType
, mod
| Modifiers
.COMPILER_GENERATED
,
582 name
, ParametersCompiled
.EmptyReadOnlyParameters
, null)
585 Block
= new ToplevelBlock (host
.Compiler
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
.Null
);
588 public override EmitContext
CreateEmitContext (ILGenerator ig
, SourceMethodBuilder sourceMethod
)
590 EmitContext ec
= new EmitContext (this, ig
, MemberType
, sourceMethod
);
591 ec
.CurrentAnonymousMethod
= expr
;
593 if (expr
is AsyncInitializer
)
594 ec
.With (BuilderContext
.Options
.AsyncBody
, true);
600 public abstract class StateMachineInitializer
: AnonymousExpression
602 sealed class MoveNextBodyStatement
: Statement
604 readonly StateMachineInitializer state_machine
;
606 public MoveNextBodyStatement (StateMachineInitializer stateMachine
)
608 this.state_machine
= stateMachine
;
609 this.loc
= stateMachine
.Location
;
612 protected override void CloneTo (CloneContext clonectx
, Statement target
)
614 throw new NotSupportedException ();
617 public override bool Resolve (BlockContext ec
)
622 protected override void DoEmit (EmitContext ec
)
624 state_machine
.EmitMoveNext (ec
);
627 public override void Emit (EmitContext ec
)
629 // Don't create sequence point
634 public readonly TypeDefinition Host
;
635 protected StateMachine storey
;
638 // The state as we generate the machine
641 Label iterator_body_end
;
642 protected Label move_next_error
;
643 LocalBuilder skip_finally
;
644 protected LocalBuilder current_pc
;
645 protected List
<ResumableStatement
> resume_points
;
647 protected StateMachineInitializer (ParametersBlock block
, TypeDefinition host
, TypeSpec returnType
)
648 : base (block
, returnType
, block
.StartLocation
)
655 public Label BodyEnd
{
657 return iterator_body_end
;
661 public LocalBuilder CurrentPC
668 public LocalBuilder SkipFinally
{
674 public override AnonymousMethodStorey Storey
{
682 public int AddResumePoint (ResumableStatement stmt
)
684 if (resume_points
== null)
685 resume_points
= new List
<ResumableStatement
> ();
687 resume_points
.Add (stmt
);
688 return resume_points
.Count
;
691 public override Expression
CreateExpressionTree (ResolveContext ec
)
693 throw new NotSupportedException ("ET");
696 protected virtual BlockContext
CreateBlockContext (ResolveContext rc
)
698 var ctx
= new BlockContext (rc
, block
, ((BlockContext
) rc
).ReturnType
);
699 ctx
.CurrentAnonymousMethod
= this;
703 protected override Expression
DoResolve (ResolveContext ec
)
705 var ctx
= CreateBlockContext (ec
);
710 // Explicit return is required for Task<T> state machine
712 var task_storey
= storey
as AsyncTaskStorey
;
713 if (task_storey
== null || (task_storey
.ReturnType
!= null && !task_storey
.ReturnType
.IsGenericTask
))
714 ctx
.CurrentBranching
.CurrentUsageVector
.Goto ();
716 ctx
.EndFlowBranching ();
718 if (!ec
.IsInProbingMode
) {
719 var move_next
= new StateMachineMethod (storey
, this, new TypeExpression (ReturnType
, loc
), Modifiers
.PUBLIC
, new MemberName ("MoveNext", loc
));
720 move_next
.Block
.AddStatement (new MoveNextBodyStatement (this));
721 storey
.AddEntryMethod (move_next
);
724 eclass
= ExprClass
.Value
;
728 public override void Emit (EmitContext ec
)
731 // Load state machine instance
733 storey
.Instance
.Emit (ec
);
736 void EmitMoveNext_NoResumePoints (EmitContext ec
)
739 ec
.Emit (OpCodes
.Ldfld
, storey
.PC
.Spec
);
742 ec
.EmitInt ((int) IteratorStorey
.State
.After
);
743 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
745 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
746 ec
.Emit (OpCodes
.Brtrue
, move_next_error
);
748 iterator_body_end
= ec
.DefineLabel ();
750 block
.EmitEmbedded (ec
);
752 ec
.MarkLabel (iterator_body_end
);
754 EmitMoveNextEpilogue (ec
);
756 ec
.MarkLabel (move_next_error
);
758 if (ReturnType
.Kind
!= MemberKind
.Void
) {
760 ec
.Emit (OpCodes
.Ret
);
764 void EmitMoveNext (EmitContext ec
)
766 move_next_ok
= ec
.DefineLabel ();
767 move_next_error
= ec
.DefineLabel ();
769 if (resume_points
== null) {
770 EmitMoveNext_NoResumePoints (ec
);
774 current_pc
= ec
.GetTemporaryLocal (ec
.BuiltinTypes
.UInt
);
776 ec
.Emit (OpCodes
.Ldfld
, storey
.PC
.Spec
);
777 ec
.Emit (OpCodes
.Stloc
, current_pc
);
779 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
781 ec
.EmitInt ((int) IteratorStorey
.State
.After
);
782 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
784 Label
[] labels
= new Label
[1 + resume_points
.Count
];
785 labels
[0] = ec
.DefineLabel ();
787 bool need_skip_finally
= false;
788 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
789 ResumableStatement s
= resume_points
[i
];
790 need_skip_finally
|= s
is ExceptionStatement
;
791 labels
[i
+ 1] = s
.PrepareForEmit (ec
);
794 if (need_skip_finally
) {
795 skip_finally
= ec
.GetTemporaryLocal (ec
.BuiltinTypes
.Bool
);
797 ec
.Emit (OpCodes
.Stloc
, skip_finally
);
800 var async_init
= this as AsyncInitializer
;
801 if (async_init
!= null)
802 ec
.BeginExceptionBlock ();
804 ec
.Emit (OpCodes
.Ldloc
, current_pc
);
805 ec
.Emit (OpCodes
.Switch
, labels
);
807 ec
.Emit (async_init
!= null ? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
809 ec
.MarkLabel (labels
[0]);
811 iterator_body_end
= ec
.DefineLabel ();
813 block
.EmitEmbedded (ec
);
815 ec
.MarkLabel (iterator_body_end
);
817 if (async_init
!= null) {
818 var catch_value
= LocalVariable
.CreateCompilerGenerated (ec
.Module
.Compiler
.BuiltinTypes
.Exception
, block
, Location
);
820 ec
.BeginCatchBlock (catch_value
.Type
);
821 catch_value
.EmitAssign (ec
);
824 ec
.EmitInt ((int) IteratorStorey
.State
.After
);
825 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
827 ((AsyncTaskStorey
) async_init
.Storey
).EmitSetException (ec
, new LocalVariableReference (catch_value
, Location
));
829 ec
.Emit (OpCodes
.Leave
, move_next_ok
);
830 ec
.EndExceptionBlock ();
833 ec
.Mark (Block
.Original
.EndLocation
);
835 ec
.EmitInt ((int) IteratorStorey
.State
.After
);
836 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
838 EmitMoveNextEpilogue (ec
);
840 ec
.MarkLabel (move_next_error
);
842 if (ReturnType
.Kind
!= MemberKind
.Void
) {
844 ec
.Emit (OpCodes
.Ret
);
847 ec
.MarkLabel (move_next_ok
);
849 if (ReturnType
.Kind
!= MemberKind
.Void
) {
851 ec
.Emit (OpCodes
.Ret
);
855 protected virtual void EmitMoveNextEpilogue (EmitContext ec
)
859 public void EmitLeave (EmitContext ec
, bool unwind_protect
)
862 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_ok
);
866 // Called back from YieldStatement
868 public virtual void InjectYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
871 // Guard against being disposed meantime
873 Label disposed
= ec
.DefineLabel ();
874 var iterator
= storey
as IteratorStorey
;
875 if (iterator
!= null) {
877 ec
.Emit (OpCodes
.Ldfld
, iterator
.DisposingField
.Spec
);
878 ec
.Emit (OpCodes
.Brtrue_S
, disposed
);
882 // store resume program-counter
885 ec
.EmitInt (resume_pc
);
886 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
888 if (iterator
!= null) {
889 ec
.MarkLabel (disposed
);
892 // mark finally blocks as disabled
893 if (unwind_protect
&& skip_finally
!= null) {
895 ec
.Emit (OpCodes
.Stloc
, skip_finally
);
899 public void SetStateMachine (StateMachine stateMachine
)
901 this.storey
= stateMachine
;
906 // Iterators are implemented as hidden anonymous block
908 public class Iterator
: StateMachineInitializer
910 public readonly IMethodData OriginalMethod
;
911 public readonly bool IsEnumerable
;
912 public readonly TypeSpec OriginalIteratorType
;
914 public Iterator (ParametersBlock block
, IMethodData method
, TypeDefinition host
, TypeSpec iterator_type
, bool is_enumerable
)
915 : base (block
, host
, host
.Compiler
.BuiltinTypes
.Bool
)
917 this.OriginalMethod
= method
;
918 this.OriginalIteratorType
= iterator_type
;
919 this.IsEnumerable
= is_enumerable
;
920 this.type
= method
.ReturnType
;
923 public ToplevelBlock Container
{
924 get { return OriginalMethod.Block; }
927 public override string ContainerType
{
928 get { return "iterator"; }
931 public override bool IsIterator
{
935 public void EmitYieldBreak (EmitContext ec
, bool unwind_protect
)
937 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
940 public override string GetSignatureForError ()
942 return OriginalMethod
.GetSignatureForError ();
945 public override void Emit (EmitContext ec
)
948 // Load Iterator storey instance
950 storey
.Instance
.Emit (ec
);
953 // Initialize iterator PC when it's unitialized
956 ec
.Emit (OpCodes
.Dup
);
957 ec
.EmitInt ((int)IteratorStorey
.State
.Uninitialized
);
959 var field
= storey
.PC
.Spec
;
960 if (storey
.MemberName
.IsGeneric
) {
961 field
= MemberCache
.GetMember (Storey
.Instance
.Type
, field
);
964 ec
.Emit (OpCodes
.Stfld
, field
);
968 public void EmitDispose (EmitContext ec
)
970 Label end
= ec
.DefineLabel ();
972 Label
[] labels
= null;
973 int n_resume_points
= resume_points
== null ? 0 : resume_points
.Count
;
974 for (int i
= 0; i
< n_resume_points
; ++i
) {
975 ResumableStatement s
= resume_points
[i
];
976 Label ret
= s
.PrepareForDispose (ec
, end
);
977 if (ret
.Equals (end
) && labels
== null)
979 if (labels
== null) {
980 labels
= new Label
[resume_points
.Count
+ 1];
981 for (int j
= 0; j
<= i
; ++j
)
988 if (labels
!= null) {
989 current_pc
= ec
.GetTemporaryLocal (ec
.BuiltinTypes
.UInt
);
991 ec
.Emit (OpCodes
.Ldfld
, storey
.PC
.Spec
);
992 ec
.Emit (OpCodes
.Stloc
, current_pc
);
997 ec
.Emit (OpCodes
.Stfld
, ((IteratorStorey
) storey
).DisposingField
.Spec
);
1000 ec
.EmitInt ((int) IteratorStorey
.State
.After
);
1001 ec
.Emit (OpCodes
.Stfld
, storey
.PC
.Spec
);
1003 if (labels
!= null) {
1004 //SymbolWriter.StartIteratorDispatcher (ec.ig);
1005 ec
.Emit (OpCodes
.Ldloc
, current_pc
);
1006 ec
.Emit (OpCodes
.Switch
, labels
);
1007 //SymbolWriter.EndIteratorDispatcher (ec.ig);
1009 foreach (ResumableStatement s
in resume_points
)
1010 s
.EmitForDispose (ec
, current_pc
, end
, true);
1016 public override void EmitStatement (EmitContext ec
)
1018 throw new NotImplementedException ();
1021 public override void InjectYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
1023 // Store the new value into current
1024 var fe
= new FieldExpr (((IteratorStorey
) storey
).CurrentField
, loc
);
1025 fe
.InstanceExpression
= new CompilerGeneratedThis (storey
.CurrentType
, loc
);
1026 fe
.EmitAssign (ec
, expr
, false, false);
1028 base.InjectYield (ec
, expr
, resume_pc
, unwind_protect
, resume_point
);
1030 EmitLeave (ec
, unwind_protect
);
1032 ec
.MarkLabel (resume_point
);
1035 protected override BlockContext
CreateBlockContext (ResolveContext rc
)
1037 var bc
= base.CreateBlockContext (rc
);
1038 bc
.StartFlowBranching (this, rc
.CurrentBranching
);
1042 public static void CreateIterator (IMethodData method
, TypeDefinition parent
, Modifiers modifiers
)
1045 TypeSpec iterator_type
;
1047 TypeSpec ret
= method
.ReturnType
;
1051 if (!CheckType (ret
, parent
, out iterator_type
, out is_enumerable
)) {
1052 parent
.Compiler
.Report
.Error (1624, method
.Location
,
1053 "The body of `{0}' cannot be an iterator block " +
1054 "because `{1}' is not an iterator interface type",
1055 method
.GetSignatureForError (),
1056 TypeManager
.CSharpName (ret
));
1060 ParametersCompiled parameters
= method
.ParameterInfo
;
1061 for (int i
= 0; i
< parameters
.Count
; i
++) {
1062 Parameter p
= parameters
[i
];
1063 Parameter
.Modifier mod
= p
.ModFlags
;
1064 if ((mod
& Parameter
.Modifier
.RefOutMask
) != 0) {
1065 parent
.Compiler
.Report
.Error (1623, p
.Location
,
1066 "Iterators cannot have ref or out parameters");
1070 if (p
is ArglistParameter
) {
1071 parent
.Compiler
.Report
.Error (1636, method
.Location
,
1072 "__arglist is not allowed in parameter list of iterators");
1076 if (parameters
.Types
[i
].IsPointer
) {
1077 parent
.Compiler
.Report
.Error (1637, p
.Location
,
1078 "Iterators cannot have unsafe parameters or yield types");
1083 if ((modifiers
& Modifiers
.UNSAFE
) != 0) {
1084 parent
.Compiler
.Report
.Error (1629, method
.Location
, "Unsafe code may not appear in iterators");
1087 method
.Block
= method
.Block
.ConvertToIterator (method
, parent
, iterator_type
, is_enumerable
);
1090 static bool CheckType (TypeSpec ret
, TypeContainer parent
, out TypeSpec original_iterator_type
, out bool is_enumerable
)
1092 original_iterator_type
= null;
1093 is_enumerable
= false;
1095 if (ret
.BuiltinType
== BuiltinTypeSpec
.Type
.IEnumerable
) {
1096 original_iterator_type
= parent
.Compiler
.BuiltinTypes
.Object
;
1097 is_enumerable
= true;
1100 if (ret
.BuiltinType
== BuiltinTypeSpec
.Type
.IEnumerator
) {
1101 original_iterator_type
= parent
.Compiler
.BuiltinTypes
.Object
;
1102 is_enumerable
= false;
1106 InflatedTypeSpec inflated
= ret
as InflatedTypeSpec
;
1107 if (inflated
== null)
1110 var member_definition
= inflated
.MemberDefinition
;
1111 PredefinedType ptype
= parent
.Module
.PredefinedTypes
.IEnumerableGeneric
;
1113 if (ptype
.Define () && ptype
.TypeSpec
.MemberDefinition
== member_definition
) {
1114 original_iterator_type
= inflated
.TypeArguments
[0];
1115 is_enumerable
= true;
1119 ptype
= parent
.Module
.PredefinedTypes
.IEnumeratorGeneric
;
1120 if (ptype
.Define () && ptype
.TypeSpec
.MemberDefinition
== member_definition
) {
1121 original_iterator_type
= inflated
.TypeArguments
[0];
1122 is_enumerable
= false;