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.
14 // Flow analysis for Yield.
18 using System
.Collections
.Generic
;
19 using System
.Reflection
;
20 using System
.Reflection
.Emit
;
22 namespace Mono
.CSharp
{
24 public class Yield
: ResumableStatement
{
30 public Yield (Expression expr
, Location l
)
36 public static bool CheckContext (ResolveContext ec
, Location loc
)
39 // We can't use `ec.InUnsafe' here because it's allowed to have an iterator
40 // inside an unsafe class. See test-martin-29.cs for an example.
42 if (!ec
.CurrentAnonymousMethod
.IsIterator
) {
43 ec
.Report
.Error (1621, loc
,
44 "The yield statement cannot be used inside " +
45 "anonymous method blocks");
52 public override bool Resolve (BlockContext ec
)
54 expr
= expr
.Resolve (ec
);
58 Report
.Debug (64, "RESOLVE YIELD #1", this, ec
, expr
, expr
.GetType (),
59 ec
.CurrentAnonymousMethod
, ec
.CurrentIterator
);
61 if (!CheckContext (ec
, loc
))
64 iterator
= ec
.CurrentIterator
;
65 if (expr
.Type
!= iterator
.OriginalIteratorType
) {
66 expr
= Convert
.ImplicitConversionRequired (
67 ec
, expr
, iterator
.OriginalIteratorType
, loc
);
72 if (!ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
73 unwind_protect
= ec
.CurrentBranching
.AddResumePoint (this, loc
, out resume_pc
);
78 protected override void DoEmit (EmitContext ec
)
80 iterator
.MarkYield (ec
, expr
, resume_pc
, unwind_protect
, resume_point
);
83 protected override void CloneTo (CloneContext clonectx
, Statement t
)
85 Yield target
= (Yield
) t
;
87 target
.expr
= expr
.Clone (clonectx
);
91 public class YieldBreak
: ExitStatement
95 public YieldBreak (Location l
)
100 public override void Error_FinallyClause (Report Report
)
102 Report
.Error (1625, loc
, "Cannot yield in the body of a finally clause");
105 protected override void CloneTo (CloneContext clonectx
, Statement target
)
107 throw new NotSupportedException ();
110 protected override bool DoResolve (BlockContext ec
)
112 iterator
= ec
.CurrentIterator
;
113 return Yield
.CheckContext (ec
, loc
);
116 protected override void DoEmit (EmitContext ec
)
118 iterator
.EmitYieldBreak (ec
, unwind_protect
);
123 // Wraps method block into iterator wrapper block
125 class IteratorStatement
: Statement
128 Block original_block
;
130 public IteratorStatement (Iterator iterator
, Block original_block
)
132 this.iterator
= iterator
;
133 this.original_block
= original_block
;
134 this.loc
= iterator
.Location
;
137 protected override void CloneTo (CloneContext clonectx
, Statement target
)
139 IteratorStatement t
= (IteratorStatement
) target
;
140 t
.original_block
= (ExplicitBlock
) original_block
.Clone (clonectx
);
141 t
.iterator
= (Iterator
) iterator
.Clone (clonectx
);
144 public override bool Resolve (BlockContext ec
)
146 ec
.StartFlowBranching (iterator
);
147 bool ok
= original_block
.Resolve (ec
);
148 ec
.EndFlowBranching ();
152 protected override void DoEmit (EmitContext ec
)
154 iterator
.EmitMoveNext (ec
, original_block
);
158 public class IteratorStorey
: AnonymousMethodStorey
160 class IteratorMethod
: Method
162 readonly IteratorStorey host
;
164 public IteratorMethod (IteratorStorey host
, FullNamedExpression returnType
, Modifiers mod
, MemberName name
)
165 : base (host
, null, returnType
, mod
| Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.COMPILER_GENERATED
,
166 name
, ParametersCompiled
.EmptyReadOnlyParameters
, null)
170 Block
= new ToplevelBlock (Compiler
, host
.Iterator
.Container
.Toplevel
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
);
173 public override EmitContext
CreateEmitContext (ILGenerator ig
)
175 EmitContext ec
= new EmitContext (this, ig
, MemberType
);
177 ec
.CurrentAnonymousMethod
= host
.Iterator
;
182 class GetEnumeratorMethod
: IteratorMethod
184 sealed class GetEnumeratorStatement
: Statement
187 IteratorMethod host_method
;
189 Expression new_storey
;
191 public GetEnumeratorStatement (IteratorStorey host
, IteratorMethod host_method
)
194 this.host_method
= host_method
;
195 loc
= host_method
.Location
;
198 protected override void CloneTo (CloneContext clonectx
, Statement target
)
200 throw new NotSupportedException ();
203 public override bool Resolve (BlockContext ec
)
205 TypeExpression storey_type_expr
= new TypeExpression (host
.Definition
, loc
);
206 List
<Expression
> init
= null;
207 if (host
.hoisted_this
!= null) {
208 init
= new List
<Expression
> (host
.hoisted_params
== null ? 1 : host
.HoistedParameters
.Count
+ 1);
209 HoistedThis ht
= host
.hoisted_this
;
210 FieldExpr
from = new FieldExpr (ht
.Field
, loc
);
211 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
212 init
.Add (new ElementInitializer (ht
.Field
.Name
, from, loc
));
215 if (host
.hoisted_params
!= null) {
217 init
= new List
<Expression
> (host
.HoistedParameters
.Count
);
219 for (int i
= 0; i
< host
.hoisted_params
.Count
; ++i
) {
220 HoistedParameter hp
= (HoistedParameter
) host
.hoisted_params
[i
];
221 HoistedParameter hp_cp
= (HoistedParameter
) host
.hoisted_params_copy
[i
];
223 FieldExpr
from = new FieldExpr (hp_cp
.Field
, loc
);
224 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
226 init
.Add (new ElementInitializer (hp
.Field
.Name
, from, loc
));
231 new_storey
= new NewInitialize (storey_type_expr
, null,
232 new CollectionOrObjectInitializers (init
, loc
), loc
);
234 new_storey
= new New (storey_type_expr
, null, loc
);
237 new_storey
= new_storey
.Resolve (ec
);
238 if (new_storey
!= null)
239 new_storey
= Convert
.ImplicitConversionRequired (ec
, new_storey
, host_method
.MemberType
, loc
);
241 if (TypeManager
.int_interlocked_compare_exchange
== null) {
242 TypeSpec t
= TypeManager
.CoreLookupType (ec
.Compiler
, "System.Threading", "Interlocked", MemberKind
.Class
, true);
244 var p
= ParametersCompiled
.CreateFullyResolved (
246 new ParameterData (null, Parameter
.Modifier
.REF
),
247 new ParameterData (null, Parameter
.Modifier
.NONE
),
248 new ParameterData (null, Parameter
.Modifier
.NONE
)
251 TypeManager
.int32_type
, TypeManager
.int32_type
, TypeManager
.int32_type
254 var f
= new MemberFilter ("CompareExchange", 0, MemberKind
.Method
, p
, TypeManager
.int32_type
);
255 TypeManager
.int_interlocked_compare_exchange
= TypeManager
.GetPredefinedMethod (t
, f
, loc
);
259 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
263 protected override void DoEmit (EmitContext ec
)
265 Label label_init
= ec
.DefineLabel ();
267 ec
.Emit (OpCodes
.Ldarg_0
);
268 ec
.Emit (OpCodes
.Ldflda
, host
.PC
.Spec
);
269 ec
.EmitInt ((int) Iterator
.State
.Start
);
270 ec
.EmitInt ((int) Iterator
.State
.Uninitialized
);
271 ec
.Emit (OpCodes
.Call
, TypeManager
.int_interlocked_compare_exchange
);
273 ec
.EmitInt ((int) Iterator
.State
.Uninitialized
);
274 ec
.Emit (OpCodes
.Bne_Un_S
, label_init
);
276 ec
.Emit (OpCodes
.Ldarg_0
);
277 ec
.Emit (OpCodes
.Ret
);
279 ec
.MarkLabel (label_init
);
281 new_storey
.Emit (ec
);
282 ec
.Emit (OpCodes
.Ret
);
286 public GetEnumeratorMethod (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
287 : base (host
, returnType
, 0, name
)
289 Block
.AddStatement (new GetEnumeratorStatement (host
, this));
293 class DisposeMethod
: IteratorMethod
295 sealed class DisposeMethodStatement
: Statement
299 public DisposeMethodStatement (Iterator iterator
)
301 this.iterator
= iterator
;
302 this.loc
= iterator
.Location
;
305 protected override void CloneTo (CloneContext clonectx
, Statement target
)
307 throw new NotSupportedException ();
310 public override bool Resolve (BlockContext ec
)
315 protected override void DoEmit (EmitContext ec
)
317 iterator
.EmitDispose (ec
);
321 public DisposeMethod (IteratorStorey host
)
322 : base (host
, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
, new MemberName ("Dispose", host
.Location
))
324 host
.AddMethod (this);
326 Block
= new ToplevelBlock (Compiler
, host
.Iterator
.Container
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
);
327 Block
.AddStatement (new DisposeMethodStatement (host
.Iterator
));
332 // Uses Method as method info
334 class DynamicMethodGroupExpr
: MethodGroupExpr
336 readonly Method method
;
338 public DynamicMethodGroupExpr (Method method
, Location loc
)
341 this.method
= method
;
342 eclass
= ExprClass
.Unresolved
;
345 protected override Expression
DoResolve (ResolveContext ec
)
347 Methods
= new List
<MemberSpec
> (1) { method.Spec }
;
348 type
= method
.Parent
.Definition
;
349 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
350 return base.DoResolve (ec
);
354 class DynamicFieldExpr
: FieldExpr
356 readonly Field field
;
358 public DynamicFieldExpr (Field field
, Location loc
)
364 protected override Expression
DoResolve (ResolveContext ec
)
367 type
= spec
.MemberType
;
368 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
369 return base.DoResolve (ec
);
373 public readonly Iterator Iterator
;
375 TypeExpr iterator_type_expr
;
379 TypeExpr enumerator_type
;
380 TypeExpr enumerable_type
;
381 TypeArguments generic_args
;
382 TypeExpr generic_enumerator_type
;
383 TypeExpr generic_enumerable_type
;
385 List
<HoistedParameter
> hoisted_params_copy
;
388 public IteratorStorey (Iterator iterator
)
389 : base (iterator
.Container
.Toplevel
, iterator
.Host
,
390 iterator
.OriginalMethod
as MemberBase
, iterator
.GenericMethod
, "Iterator")
392 this.Iterator
= iterator
;
396 get { return pc_field; }
399 public Field CurrentField
{
400 get { return current_field; }
403 public IList
<HoistedParameter
> HoistedParameters
{
404 get { return hoisted_params; }
407 protected override TypeExpr
[] ResolveBaseTypes (out TypeExpr base_class
)
409 var mtype
= Iterator
.OriginalIteratorType
;
411 mtype
= Mutator
.Mutate (mtype
);
413 iterator_type_expr
= new TypeExpression (mtype
, Location
);
414 generic_args
= new TypeArguments (iterator_type_expr
);
416 var list
= new List
<FullNamedExpression
> ();
417 if (Iterator
.IsEnumerable
) {
418 enumerable_type
= new TypeExpression (
419 TypeManager
.ienumerable_type
, Location
);
420 list
.Add (enumerable_type
);
422 if (TypeManager
.generic_ienumerable_type
!= null) {
423 generic_enumerable_type
= new GenericTypeExpr (
424 TypeManager
.generic_ienumerable_type
,
425 generic_args
, Location
);
426 list
.Add (generic_enumerable_type
);
430 enumerator_type
= new TypeExpression (
431 TypeManager
.ienumerator_type
, Location
);
432 list
.Add (enumerator_type
);
434 list
.Add (new TypeExpression (TypeManager
.idisposable_type
, Location
));
436 if (TypeManager
.generic_ienumerator_type
!= null) {
437 generic_enumerator_type
= new GenericTypeExpr (
438 TypeManager
.generic_ienumerator_type
,
439 generic_args
, Location
);
440 list
.Add (generic_enumerator_type
);
445 return base.ResolveBaseTypes (out base_class
);
448 protected override string GetVariableMangledName (LocalInfo local_info
)
450 return "<" + local_info
.Name
+ ">__" + local_name_idx
++.ToString ();
453 protected override bool DoDefineMembers ()
455 DefineIteratorMembers ();
456 return base.DoDefineMembers ();
459 void DefineIteratorMembers ()
461 pc_field
= AddCompilerGeneratedField ("$PC", TypeManager
.system_int32_expr
);
462 current_field
= AddCompilerGeneratedField ("$current", iterator_type_expr
);
464 if (hoisted_params
!= null) {
466 // Iterators are independent, each GetEnumerator call has to
467 // create same enumerator therefore we have to keep original values
468 // around for re-initialization
470 // TODO: Do it for assigned/modified parameters only
472 hoisted_params_copy
= new List
<HoistedParameter
> (hoisted_params
.Count
);
473 foreach (HoistedParameter hp
in hoisted_params
) {
474 hoisted_params_copy
.Add (new HoistedParameter (hp
, "<$>" + hp
.Field
.Name
));
478 if (generic_enumerator_type
!= null)
479 Define_Current (true);
481 Define_Current (false);
482 new DisposeMethod (this);
485 if (Iterator
.IsEnumerable
) {
486 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
487 name
= new MemberName (name
, "Collections", Location
);
488 name
= new MemberName (name
, "IEnumerable", Location
);
489 name
= new MemberName (name
, "GetEnumerator", Location
);
491 if (generic_enumerator_type
!= null) {
492 Method get_enumerator
= new IteratorMethod (this, enumerator_type
, 0, name
);
494 name
= new MemberName (name
.Left
.Left
, "Generic", Location
);
495 name
= new MemberName (name
, "IEnumerable", generic_args
, Location
);
496 name
= new MemberName (name
, "GetEnumerator", Location
);
497 Method gget_enumerator
= new GetEnumeratorMethod (this, generic_enumerator_type
, name
);
500 // Just call generic GetEnumerator implementation
502 get_enumerator
.Block
.AddStatement (
503 new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator
, Location
), null), Location
));
505 AddMethod (get_enumerator
);
506 AddMethod (gget_enumerator
);
508 AddMethod (new GetEnumeratorMethod (this, enumerator_type
, name
));
513 protected override void EmitHoistedParameters (EmitContext ec
, IList
<HoistedParameter
> hoisted
)
515 base.EmitHoistedParameters (ec
, hoisted
);
516 base.EmitHoistedParameters (ec
, hoisted_params_copy
);
519 void Define_Current (bool is_generic
)
523 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
524 name
= new MemberName (name
, "Collections", Location
);
527 name
= new MemberName (name
, "Generic", Location
);
528 name
= new MemberName (name
, "IEnumerator", generic_args
, Location
);
529 type
= iterator_type_expr
;
531 name
= new MemberName (name
, "IEnumerator");
532 type
= TypeManager
.system_object_expr
;
535 name
= new MemberName (name
, "Current", Location
);
537 ToplevelBlock get_block
= new ToplevelBlock (Compiler
, Location
);
538 get_block
.AddStatement (new Return (new DynamicFieldExpr (CurrentField
, Location
), Location
));
540 Accessor getter
= new Accessor (get_block
, 0, null, null, Location
);
542 Property current
= new Property (
543 this, type
, Modifiers
.DEBUGGER_HIDDEN
, name
, null, getter
, null, false);
544 AddProperty (current
);
549 Method reset
= new Method (
550 this, null, TypeManager
.system_void_expr
,
551 Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
,
552 new MemberName ("Reset", Location
),
553 ParametersCompiled
.EmptyReadOnlyParameters
, null);
556 reset
.Block
= new ToplevelBlock (Compiler
, Location
);
558 TypeSpec ex_type
= TypeManager
.CoreLookupType (Compiler
, "System", "NotSupportedException", MemberKind
.Class
, true);
562 reset
.Block
.AddStatement (new Throw (new New (new TypeExpression (ex_type
, Location
), null, Location
), Location
));
567 // Iterators are implemented as hidden anonymous block
569 public class Iterator
: AnonymousExpression
{
570 public readonly IMethodData OriginalMethod
;
571 AnonymousMethodMethod method
;
572 public readonly TypeContainer Host
;
573 public readonly bool IsEnumerable
;
574 List
<ResumableStatement
> resume_points
;
577 // The state as we generate the iterator
579 Label move_next_ok
, move_next_error
;
580 LocalBuilder skip_finally
, current_pc
;
582 public LocalBuilder SkipFinally
{
583 get { return skip_finally; }
586 public LocalBuilder CurrentPC
{
587 get { return current_pc; }
590 public Block Container
{
591 get { return OriginalMethod.Block; }
594 public GenericMethod GenericMethod
{
595 get { return OriginalMethod.GenericMethod; }
598 public readonly TypeSpec OriginalIteratorType
;
600 readonly IteratorStorey IteratorHost
;
603 Running
= -3, // Used only in CurrentPC, never stored into $PC
609 public void EmitYieldBreak (EmitContext ec
, bool unwind_protect
)
611 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
614 void EmitMoveNext_NoResumePoints (EmitContext ec
, Block original_block
)
616 ec
.Emit (OpCodes
.Ldarg_0
);
617 ec
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
);
619 ec
.Emit (OpCodes
.Ldarg_0
);
620 ec
.EmitInt ((int) State
.After
);
621 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
);
623 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
624 ec
.Emit (OpCodes
.Brtrue
, move_next_error
);
626 SymbolWriter
.StartIteratorBody (ec
);
627 original_block
.Emit (ec
);
628 SymbolWriter
.EndIteratorBody (ec
);
630 ec
.MarkLabel (move_next_error
);
631 ec
.Emit (OpCodes
.Ldc_I4_0
);
632 ec
.Emit (OpCodes
.Ret
);
635 internal void EmitMoveNext (EmitContext ec
, Block original_block
)
637 move_next_ok
= ec
.DefineLabel ();
638 move_next_error
= ec
.DefineLabel ();
640 if (resume_points
== null) {
641 EmitMoveNext_NoResumePoints (ec
, original_block
);
645 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
646 ec
.Emit (OpCodes
.Ldarg_0
);
647 ec
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
);
648 ec
.Emit (OpCodes
.Stloc
, current_pc
);
650 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
651 ec
.Emit (OpCodes
.Ldarg_0
);
652 ec
.EmitInt ((int) State
.After
);
653 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
);
655 Label
[] labels
= new Label
[1 + resume_points
.Count
];
656 labels
[0] = ec
.DefineLabel ();
658 bool need_skip_finally
= false;
659 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
660 ResumableStatement s
= resume_points
[i
];
661 need_skip_finally
|= s
is ExceptionStatement
;
662 labels
[i
+1] = s
.PrepareForEmit (ec
);
665 if (need_skip_finally
) {
666 skip_finally
= ec
.GetTemporaryLocal (TypeManager
.bool_type
);
667 ec
.Emit (OpCodes
.Ldc_I4_0
);
668 ec
.Emit (OpCodes
.Stloc
, skip_finally
);
671 SymbolWriter
.StartIteratorDispatcher (ec
);
672 ec
.Emit (OpCodes
.Ldloc
, current_pc
);
673 ec
.Emit (OpCodes
.Switch
, labels
);
675 ec
.Emit (OpCodes
.Br
, move_next_error
);
676 SymbolWriter
.EndIteratorDispatcher (ec
);
678 ec
.MarkLabel (labels
[0]);
680 SymbolWriter
.StartIteratorBody (ec
);
681 original_block
.Emit (ec
);
682 SymbolWriter
.EndIteratorBody (ec
);
684 SymbolWriter
.StartIteratorDispatcher (ec
);
686 ec
.Emit (OpCodes
.Ldarg_0
);
687 ec
.EmitInt ((int) State
.After
);
688 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
);
690 ec
.MarkLabel (move_next_error
);
692 ec
.Emit (OpCodes
.Ret
);
694 ec
.MarkLabel (move_next_ok
);
695 ec
.Emit (OpCodes
.Ldc_I4_1
);
696 ec
.Emit (OpCodes
.Ret
);
698 SymbolWriter
.EndIteratorDispatcher (ec
);
701 public void EmitDispose (EmitContext ec
)
703 Label end
= ec
.DefineLabel ();
705 Label
[] labels
= null;
706 int n_resume_points
= resume_points
== null ? 0 : resume_points
.Count
;
707 for (int i
= 0; i
< n_resume_points
; ++i
) {
708 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
709 Label ret
= s
.PrepareForDispose (ec
, end
);
710 if (ret
.Equals (end
) && labels
== null)
712 if (labels
== null) {
713 labels
= new Label
[resume_points
.Count
+ 1];
714 for (int j
= 0; j
<= i
; ++j
)
720 if (labels
!= null) {
721 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
722 ec
.Emit (OpCodes
.Ldarg_0
);
723 ec
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
);
724 ec
.Emit (OpCodes
.Stloc
, current_pc
);
727 ec
.Emit (OpCodes
.Ldarg_0
);
728 ec
.EmitInt ((int) State
.After
);
729 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
);
731 if (labels
!= null) {
732 //SymbolWriter.StartIteratorDispatcher (ec.ig);
733 ec
.Emit (OpCodes
.Ldloc
, current_pc
);
734 ec
.Emit (OpCodes
.Switch
, labels
);
735 //SymbolWriter.EndIteratorDispatcher (ec.ig);
737 foreach (ResumableStatement s
in resume_points
)
738 s
.EmitForDispose (ec
, this, end
, true);
744 public int AddResumePoint (ResumableStatement stmt
)
746 if (resume_points
== null)
747 resume_points
= new List
<ResumableStatement
> ();
749 resume_points
.Add (stmt
);
750 return resume_points
.Count
;
754 // Called back from Yield
756 public void MarkYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
758 // Store the new current
759 ec
.Emit (OpCodes
.Ldarg_0
);
761 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.CurrentField
.Spec
);
763 // store resume program-counter
764 ec
.Emit (OpCodes
.Ldarg_0
);
765 ec
.EmitInt (resume_pc
);
766 ec
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
);
768 // mark finally blocks as disabled
769 if (unwind_protect
&& skip_finally
!= null) {
771 ec
.Emit (OpCodes
.Stloc
, skip_finally
);
775 ec
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_ok
);
777 ec
.MarkLabel (resume_point
);
780 public override string ContainerType
{
781 get { return "iterator"; }
784 public override bool IsIterator
{
788 public override AnonymousMethodStorey Storey
{
789 get { return IteratorHost; }
795 private Iterator (CompilerContext ctx
, IMethodData method
, TypeContainer host
, TypeSpec iterator_type
, bool is_enumerable
)
797 new ToplevelBlock (ctx
, method
.Block
, ParametersCompiled
.EmptyReadOnlyParameters
, method
.Block
.StartLocation
),
798 TypeManager
.bool_type
,
801 this.OriginalMethod
= method
;
802 this.OriginalIteratorType
= iterator_type
;
803 this.IsEnumerable
= is_enumerable
;
805 this.type
= method
.ReturnType
;
807 IteratorHost
= Block
.ChangeToIterator (this, method
.Block
);
810 public override string GetSignatureForError ()
812 return OriginalMethod
.GetSignatureForError ();
815 protected override Expression
DoResolve (ResolveContext ec
)
817 method
= new AnonymousMethodMethod (Storey
,
818 this, Storey
, null, TypeManager
.system_boolean_expr
,
819 Modifiers
.PUBLIC
, OriginalMethod
.GetSignatureForError (),
820 new MemberName ("MoveNext", Location
),
821 ParametersCompiled
.EmptyReadOnlyParameters
);
823 if (Compatible (ec
) == null)
826 eclass
= ExprClass
.Value
;
830 public override void Emit (EmitContext ec
)
833 // Load Iterator storey instance
835 method
.Storey
.Instance
.Emit (ec
);
838 // Initialize iterator PC when it's unitialized
841 ec
.Emit (OpCodes
.Dup
);
842 ec
.EmitInt ((int)State
.Uninitialized
);
844 var field
= IteratorHost
.PC
.Spec
;
845 if (Storey
.MemberName
.IsGeneric
) {
846 field
= MemberCache
.GetMember (Storey
.Instance
.Type
, field
);
849 ec
.Emit (OpCodes
.Stfld
, field
);
853 public override Expression
CreateExpressionTree (ResolveContext ec
)
855 throw new NotSupportedException ("ET");
858 public static void CreateIterator (IMethodData method
, TypeContainer parent
, Modifiers modifiers
, CompilerContext ctx
)
861 TypeSpec iterator_type
;
863 TypeSpec ret
= method
.ReturnType
;
867 if (!CheckType (ret
, out iterator_type
, out is_enumerable
)) {
868 ctx
.Report
.Error (1624, method
.Location
,
869 "The body of `{0}' cannot be an iterator block " +
870 "because `{1}' is not an iterator interface type",
871 method
.GetSignatureForError (),
872 TypeManager
.CSharpName (ret
));
876 ParametersCompiled parameters
= method
.ParameterInfo
;
877 for (int i
= 0; i
< parameters
.Count
; i
++) {
878 Parameter p
= parameters
[i
];
879 Parameter
.Modifier mod
= p
.ModFlags
;
880 if ((mod
& Parameter
.Modifier
.ISBYREF
) != 0) {
881 ctx
.Report
.Error (1623, p
.Location
,
882 "Iterators cannot have ref or out parameters");
886 if (p
is ArglistParameter
) {
887 ctx
.Report
.Error (1636, method
.Location
,
888 "__arglist is not allowed in parameter list of iterators");
892 if (parameters
.Types
[i
].IsPointer
) {
893 ctx
.Report
.Error (1637, p
.Location
,
894 "Iterators cannot have unsafe parameters or " +
900 if ((modifiers
& Modifiers
.UNSAFE
) != 0) {
901 ctx
.Report
.Error (1629, method
.Location
, "Unsafe code may not appear in iterators");
905 // TODO: Ugly leftover
906 new Iterator (ctx
, method
, parent
, iterator_type
, is_enumerable
);
909 static bool CheckType (TypeSpec ret
, out TypeSpec original_iterator_type
, out bool is_enumerable
)
911 original_iterator_type
= null;
912 is_enumerable
= false;
914 if (ret
== TypeManager
.ienumerable_type
) {
915 original_iterator_type
= TypeManager
.object_type
;
916 is_enumerable
= true;
919 if (ret
== TypeManager
.ienumerator_type
) {
920 original_iterator_type
= TypeManager
.object_type
;
921 is_enumerable
= false;
925 InflatedTypeSpec inflated
= ret
as InflatedTypeSpec
;
926 if (inflated
== null)
929 ret
= inflated
.GetDefinition ();
930 if (ret
== TypeManager
.generic_ienumerable_type
) {
931 original_iterator_type
= inflated
.TypeArguments
[0];
932 is_enumerable
= true;
936 if (ret
== TypeManager
.generic_ienumerator_type
) {
937 original_iterator_type
= inflated
.TypeArguments
[0];
938 is_enumerable
= false;