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
;
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 (EmitContext ec
, Location loc
)
38 for (Block block
= ec
.CurrentBlock
; block
!= null; block
= block
.Parent
) {
42 Report
.Error (1629, loc
, "Unsafe code may not appear in iterators");
47 // We can't use `ec.InUnsafe' here because it's allowed to have an iterator
48 // inside an unsafe class. See test-martin-29.cs for an example.
50 if (!ec
.CurrentAnonymousMethod
.IsIterator
) {
51 Report
.Error (1621, loc
,
52 "The yield statement cannot be used inside " +
53 "anonymous method blocks");
60 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
62 expr
.MutateHoistedGenericType (storey
);
65 public override bool Resolve (EmitContext ec
)
67 expr
= expr
.Resolve (ec
);
71 Report
.Debug (64, "RESOLVE YIELD #1", this, ec
, expr
, expr
.GetType (),
72 ec
.CurrentAnonymousMethod
, ec
.CurrentIterator
);
74 if (!CheckContext (ec
, loc
))
77 Iterator iterator
= ec
.CurrentIterator
;
78 if (expr
.Type
!= iterator
.OriginalIteratorType
) {
79 expr
= Convert
.ImplicitConversionRequired (
80 ec
, expr
, iterator
.OriginalIteratorType
, loc
);
85 if (!ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
86 unwind_protect
= ec
.CurrentBranching
.AddResumePoint (this, loc
, out resume_pc
);
91 protected override void DoEmit (EmitContext ec
)
93 ec
.CurrentIterator
.MarkYield (ec
, expr
, resume_pc
, unwind_protect
, resume_point
);
96 protected override void CloneTo (CloneContext clonectx
, Statement t
)
98 Yield target
= (Yield
) t
;
100 target
.expr
= expr
.Clone (clonectx
);
104 public class YieldBreak
: ExitStatement
{
105 public YieldBreak (Location l
)
110 public override void Error_FinallyClause ()
112 Report
.Error (1625, loc
, "Cannot yield in the body of a finally clause");
115 protected override void CloneTo (CloneContext clonectx
, Statement target
)
117 throw new NotSupportedException ();
120 protected override bool DoResolve (EmitContext ec
)
122 return Yield
.CheckContext (ec
, loc
);
125 protected override void DoEmit (EmitContext ec
)
127 ec
.CurrentIterator
.EmitYieldBreak (ec
.ig
, unwind_protect
);
130 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
137 // Wraps method block into iterator wrapper block
139 class IteratorStatement
: Statement
142 ExplicitBlock original_block
;
144 public IteratorStatement (Iterator iterator
, ExplicitBlock original_block
)
146 this.iterator
= iterator
;
147 this.original_block
= original_block
;
148 this.loc
= iterator
.Location
;
151 protected override void CloneTo (CloneContext clonectx
, Statement target
)
153 IteratorStatement t
= (IteratorStatement
) target
;
154 t
.original_block
= (ExplicitBlock
) original_block
.Clone (clonectx
);
155 t
.iterator
= (Iterator
) iterator
.Clone (clonectx
);
158 public override bool Resolve (EmitContext ec
)
160 ec
.StartFlowBranching (iterator
);
161 bool ok
= original_block
.Resolve (ec
);
162 ec
.EndFlowBranching ();
166 protected override void DoEmit (EmitContext ec
)
168 iterator
.EmitMoveNext (ec
, original_block
);
171 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
173 original_block
.MutateHoistedGenericType (storey
);
174 iterator
.MutateHoistedGenericType (storey
);
178 public class IteratorStorey
: AnonymousMethodStorey
180 class IteratorMethod
: Method
182 readonly IteratorStorey host
;
184 public IteratorMethod (IteratorStorey host
, FullNamedExpression returnType
, int mod
, MemberName name
)
185 : base (host
, null, returnType
, mod
| Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.COMPILER_GENERATED
,
186 name
, Parameters
.EmptyReadOnlyParameters
, null)
190 Block
= new ToplevelBlock (host
.Iterator
.Container
.Toplevel
, null, Location
);
193 public override EmitContext
CreateEmitContext (DeclSpace tc
, ILGenerator ig
)
195 EmitContext ec
= new EmitContext (
196 this, tc
, this.ds
, Location
, ig
, MemberType
, ModFlags
, false);
198 ec
.CurrentAnonymousMethod
= host
.Iterator
;
203 class GetEnumeratorMethod
: IteratorMethod
205 sealed class GetEnumeratorStatement
: Statement
210 Expression new_storey
;
212 public GetEnumeratorStatement (IteratorStorey host
, Expression type
)
219 protected override void CloneTo (CloneContext clonectx
, Statement target
)
221 throw new NotSupportedException ();
224 public override bool Resolve (EmitContext ec
)
226 type
= type
.ResolveAsTypeTerminal (ec
, false);
227 if ((type
== null) || (type
.Type
== null))
230 TypeExpression storey_type_expr
= new TypeExpression (host
.TypeBuilder
, loc
);
231 ArrayList init
= null;
232 if (host
.hoisted_this
!= null) {
233 init
= new ArrayList (host
.hoisted_params
== null ? 1 : host
.HoistedParameters
.Count
+ 1);
234 HoistedThis ht
= host
.hoisted_this
;
235 FieldExpr
from = new FieldExpr (ht
.Field
.FieldBuilder
, loc
);
236 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
237 init
.Add (new ElementInitializer (ht
.Field
.Name
, from, loc
));
240 if (host
.hoisted_params
!= null) {
242 init
= new ArrayList (host
.HoistedParameters
.Count
);
244 for (int i
= 0; i
< host
.hoisted_params
.Count
; ++i
) {
245 HoistedParameter hp
= (HoistedParameter
) host
.hoisted_params
[i
];
246 HoistedParameter hp_cp
= (HoistedParameter
) host
.hoisted_params_copy
[i
];
248 FieldExpr
from = new FieldExpr (hp_cp
.Field
.FieldBuilder
, loc
);
249 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
251 init
.Add (new ElementInitializer (hp
.Field
.Name
, from, loc
));
256 new_storey
= new NewInitialize (storey_type_expr
, new ArrayList (0),
257 new CollectionOrObjectInitializers (init
, loc
), loc
);
259 new_storey
= new New (storey_type_expr
, new ArrayList (0), loc
);
262 new_storey
= new_storey
.Resolve (ec
);
263 if (new_storey
!= null)
264 new_storey
= Convert
.ImplicitConversionRequired (ec
, new_storey
, type
.Type
, loc
);
266 if (TypeManager
.int_interlocked_compare_exchange
== null) {
267 Type t
= TypeManager
.CoreLookupType ("System.Threading", "Interlocked", Kind
.Class
, true);
269 TypeManager
.int_interlocked_compare_exchange
= TypeManager
.GetPredefinedMethod (
270 t
, "CompareExchange", loc
, TypeManager
.int32_type
,
271 TypeManager
.int32_type
, TypeManager
.int32_type
);
275 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
279 protected override void DoEmit (EmitContext ec
)
281 ILGenerator ig
= ec
.ig
;
282 Label label_init
= ig
.DefineLabel ();
284 ig
.Emit (OpCodes
.Ldarg_0
);
285 ig
.Emit (OpCodes
.Ldflda
, host
.PC
.FieldBuilder
);
286 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Start
);
287 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
288 ig
.Emit (OpCodes
.Call
, TypeManager
.int_interlocked_compare_exchange
);
290 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
291 ig
.Emit (OpCodes
.Bne_Un_S
, label_init
);
293 ig
.Emit (OpCodes
.Ldarg_0
);
294 ig
.Emit (OpCodes
.Ret
);
296 ig
.MarkLabel (label_init
);
298 new_storey
.Emit (ec
);
299 ig
.Emit (OpCodes
.Ret
);
302 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
304 throw new NotSupportedException ();
308 public GetEnumeratorMethod (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
309 : base (host
, returnType
, 0, name
)
311 Block
.AddStatement (new GetEnumeratorStatement (host
, type_name
));
315 class DisposeMethod
: IteratorMethod
317 sealed class DisposeMethodStatement
: Statement
321 public DisposeMethodStatement (Iterator iterator
)
323 this.iterator
= iterator
;
324 this.loc
= iterator
.Location
;
327 protected override void CloneTo (CloneContext clonectx
, Statement target
)
329 throw new NotSupportedException ();
332 public override bool Resolve (EmitContext ec
)
337 protected override void DoEmit (EmitContext ec
)
339 iterator
.EmitDispose (ec
);
342 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
344 throw new NotSupportedException ();
348 public DisposeMethod (IteratorStorey host
)
349 : base (host
, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
, new MemberName ("Dispose", host
.Location
))
351 host
.AddMethod (this);
353 Block
= new ToplevelBlock (host
.Iterator
.Container
, null, Location
);
354 Block
.AddStatement (new DisposeMethodStatement (host
.Iterator
));
359 // Uses Method as method info
361 class DynamicMethodGroupExpr
: MethodGroupExpr
363 readonly Method method
;
365 public DynamicMethodGroupExpr (Method method
, Location loc
)
368 this.method
= method
;
371 public override Expression
DoResolve (EmitContext ec
)
373 Methods
= new MethodBase
[] { method.MethodBuilder }
;
374 type
= method
.Parent
.TypeBuilder
;
375 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
376 return base.DoResolve (ec
);
380 public readonly Iterator Iterator
;
382 TypeExpr iterator_type_expr
;
386 TypeExpr enumerator_type
;
387 TypeExpr enumerable_type
;
389 TypeArguments generic_args
;
390 TypeExpr generic_enumerator_type
;
391 TypeExpr generic_enumerable_type
;
393 const TypeArguments generic_args
= null;
396 ArrayList hoisted_params_copy
;
399 public IteratorStorey (Iterator iterator
)
400 : base (iterator
.Container
.Toplevel
, iterator
.Host
,
401 iterator
.OriginalMethod
as MemberBase
, iterator
.GenericMethod
, "Iterator")
403 this.Iterator
= iterator
;
404 HasHoistedVariables
= true;
408 get { return pc_field; }
411 public Field CurrentField
{
412 get { return current_field; }
415 public ArrayList HoistedParameters
{
416 get { return hoisted_params; }
419 protected override TypeExpr
[] ResolveBaseTypes (out TypeExpr base_class
)
421 iterator_type_expr
= new TypeExpression (MutateType (Iterator
.OriginalIteratorType
), Location
);
424 generic_args
= new TypeArguments (Location
);
425 generic_args
.Add (iterator_type_expr
);
428 ArrayList list
= new ArrayList ();
429 if (Iterator
.IsEnumerable
) {
430 enumerable_type
= new TypeExpression (
431 TypeManager
.ienumerable_type
, Location
);
432 list
.Add (enumerable_type
);
435 generic_enumerable_type
= new ConstructedType (
436 TypeManager
.generic_ienumerable_type
,
437 generic_args
, Location
);
438 list
.Add (generic_enumerable_type
);
442 enumerator_type
= new TypeExpression (
443 TypeManager
.ienumerator_type
, Location
);
444 list
.Add (enumerator_type
);
446 list
.Add (new TypeExpression (TypeManager
.idisposable_type
, Location
));
449 generic_enumerator_type
= new ConstructedType (
450 TypeManager
.generic_ienumerator_type
,
451 generic_args
, Location
);
452 list
.Add (generic_enumerator_type
);
457 return base.ResolveBaseTypes (out base_class
);
460 protected override string GetVariableMangledName (LocalInfo local_info
)
462 return "<" + local_info
.Name
+ ">__" + local_name_idx
++.ToString ();
465 public void DefineIteratorMembers ()
467 pc_field
= AddCompilerGeneratedField ("$PC", TypeManager
.system_int32_expr
);
468 current_field
= AddCompilerGeneratedField ("$current", iterator_type_expr
);
470 if (hoisted_params
!= null) {
472 // Iterators are independent, each GetEnumerator call has to
473 // create same enumerator therefore we have to keep original values
474 // around for re-initialization
476 // TODO: Do it for assigned/modified parameters only
478 hoisted_params_copy
= new ArrayList (hoisted_params
.Count
);
479 foreach (HoistedParameter hp
in hoisted_params
) {
480 hoisted_params_copy
.Add (new HoistedParameter (hp
, "<$>" + hp
.Field
.Name
));
485 Define_Current (true);
487 Define_Current (false);
488 new DisposeMethod (this);
491 if (Iterator
.IsEnumerable
) {
492 MemberName name
= new MemberName (
493 new MemberName ("System.Collections.IEnumerable", Location
), "GetEnumerator", Location
);
496 Method get_enumerator
= new IteratorMethod (this, enumerator_type
, 0, name
);
498 name
= new MemberName (
499 new MemberName ("System.Collections.Generic.IEnumerable", generic_args
, Location
), "GetEnumerator", Location
);
500 Method gget_enumerator
= new GetEnumeratorMethod (this, generic_enumerator_type
, name
);
503 // Just call generic GetEnumerator implementation
505 get_enumerator
.Block
.AddStatement (
506 new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator
, Location
), new ArrayList (0)), Location
));
508 AddMethod (get_enumerator
);
509 AddMethod (gget_enumerator
);
511 AddMethod (new GetEnumeratorMethod (this, enumerator_type
, name
));
516 protected override void EmitHoistedParameters (EmitContext ec
, ArrayList hoisted
)
518 base.EmitHoistedParameters (ec
, hoisted
);
519 base.EmitHoistedParameters (ec
, hoisted_params_copy
);
522 void Define_Current (bool is_generic
)
528 left
= new MemberName (
529 "System.Collections.Generic.IEnumerator",
530 generic_args
, Location
);
531 type
= iterator_type_expr
;
533 left
= new MemberName ("System.Collections.IEnumerator", Location
);
534 type
= TypeManager
.system_object_expr
;
537 MemberName name
= new MemberName (left
, "Current", null, Location
);
539 ToplevelBlock get_block
= new ToplevelBlock (Location
);
540 get_block
.AddStatement (new CurrentBlock (this, is_generic
));
542 Accessor getter
= new Accessor (get_block
, 0, null, null, Location
);
544 Property current
= new Property (
545 this, type
, Modifiers
.DEBUGGER_HIDDEN
, name
, null, getter
, null, false);
546 AddProperty (current
);
551 Method reset
= new Method (
552 this, null, TypeManager
.system_void_expr
,
553 Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
,
554 new MemberName ("Reset", Location
),
555 Parameters
.EmptyReadOnlyParameters
, null);
558 reset
.Block
= new ToplevelBlock (Location
);
559 reset
.Block
.AddStatement (Create_ThrowNotSupported ());
562 Statement
Create_ThrowNotSupported ()
564 TypeExpr ex_type
= new TypeLookupExpression ("System.NotSupportedException");
566 return new Throw (new New (ex_type
, null, Location
), Location
);
569 protected class CurrentBlock
: Statement
{
573 public CurrentBlock (IteratorStorey host
, bool is_generic
)
576 this.is_generic
= is_generic
;
580 protected override void CloneTo (CloneContext clonectx
, Statement target
)
582 throw new NotSupportedException ();
585 public override bool Resolve (EmitContext ec
)
587 // We emit a 'ret', so prevent the enclosing TopLevelBlock from emitting one too
588 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
592 protected override void DoEmit (EmitContext ec
)
594 ILGenerator ig
= ec
.ig
;
596 ig
.Emit (OpCodes
.Ldarg_0
);
597 ig
.Emit (OpCodes
.Ldfld
, host
.CurrentField
.FieldBuilder
);
599 ig
.Emit (OpCodes
.Box
, host
.CurrentField
.MemberType
);
600 ig
.Emit (OpCodes
.Ret
);
603 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
605 throw new NotSupportedException ();
611 // Iterators are implemented as hidden anonymous block
613 public class Iterator
: AnonymousExpression
{
614 public readonly IMethodData OriginalMethod
;
615 AnonymousMethodMethod method
;
616 public readonly TypeContainer Host
;
617 public readonly bool IsEnumerable
;
620 // The state as we generate the iterator
622 Label move_next_ok
, move_next_error
;
623 LocalBuilder skip_finally
, current_pc
;
625 public LocalBuilder SkipFinally
{
626 get { return skip_finally; }
629 public LocalBuilder CurrentPC
{
630 get { return current_pc; }
633 public Block Container
{
634 get { return OriginalMethod.Block; }
637 public GenericMethod GenericMethod
{
638 get { return OriginalMethod.GenericMethod; }
641 public readonly Type OriginalIteratorType
;
643 readonly IteratorStorey IteratorHost
;
646 Running
= -3, // Used only in CurrentPC, never stored into $PC
652 public override void AddStoreyReference (AnonymousMethodStorey storey
)
657 public void EmitYieldBreak (ILGenerator ig
, bool unwind_protect
)
659 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
662 void EmitMoveNext_NoResumePoints (EmitContext ec
, Block original_block
)
664 ILGenerator ig
= ec
.ig
;
666 ig
.Emit (OpCodes
.Ldarg_0
);
667 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
669 ig
.Emit (OpCodes
.Ldarg_0
);
670 IntConstant
.EmitInt (ig
, (int) State
.After
);
671 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
673 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
674 ig
.Emit (OpCodes
.Brtrue
, move_next_error
);
676 SymbolWriter
.StartIteratorBody (ec
.ig
);
677 original_block
.Emit (ec
);
678 SymbolWriter
.EndIteratorBody (ec
.ig
);
680 ig
.MarkLabel (move_next_error
);
681 ig
.Emit (OpCodes
.Ldc_I4_0
);
682 ig
.Emit (OpCodes
.Ret
);
685 internal void EmitMoveNext (EmitContext ec
, Block original_block
)
687 ILGenerator ig
= ec
.ig
;
689 move_next_ok
= ig
.DefineLabel ();
690 move_next_error
= ig
.DefineLabel ();
692 if (resume_points
== null) {
693 EmitMoveNext_NoResumePoints (ec
, original_block
);
697 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
698 ig
.Emit (OpCodes
.Ldarg_0
);
699 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
700 ig
.Emit (OpCodes
.Stloc
, current_pc
);
702 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
703 ig
.Emit (OpCodes
.Ldarg_0
);
704 IntConstant
.EmitInt (ig
, (int) State
.After
);
705 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
707 Label
[] labels
= new Label
[1 + resume_points
.Count
];
708 labels
[0] = ig
.DefineLabel ();
710 bool need_skip_finally
= false;
711 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
712 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
713 need_skip_finally
|= s
is ExceptionStatement
;
714 labels
[i
+1] = s
.PrepareForEmit (ec
);
717 if (need_skip_finally
) {
718 skip_finally
= ec
.GetTemporaryLocal (TypeManager
.bool_type
);
719 ig
.Emit (OpCodes
.Ldc_I4_0
);
720 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
723 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
724 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
725 ig
.Emit (OpCodes
.Switch
, labels
);
727 ig
.Emit (OpCodes
.Br
, move_next_error
);
728 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
730 ig
.MarkLabel (labels
[0]);
732 SymbolWriter
.StartIteratorBody (ec
.ig
);
733 original_block
.Emit (ec
);
734 SymbolWriter
.EndIteratorBody (ec
.ig
);
736 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
738 ig
.Emit (OpCodes
.Ldarg_0
);
739 IntConstant
.EmitInt (ig
, (int) State
.After
);
740 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
742 ig
.MarkLabel (move_next_error
);
743 ig
.Emit (OpCodes
.Ldc_I4_0
);
744 ig
.Emit (OpCodes
.Ret
);
746 ig
.MarkLabel (move_next_ok
);
747 ig
.Emit (OpCodes
.Ldc_I4_1
);
748 ig
.Emit (OpCodes
.Ret
);
750 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
753 public void EmitDispose (EmitContext ec
)
755 ILGenerator ig
= ec
.ig
;
757 Label end
= ig
.DefineLabel ();
759 Label
[] labels
= null;
760 int n_resume_points
= resume_points
== null ? 0 : resume_points
.Count
;
761 for (int i
= 0; i
< n_resume_points
; ++i
) {
762 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
763 Label ret
= s
.PrepareForDispose (ec
, end
);
764 if (ret
.Equals (end
) && labels
== null)
766 if (labels
== null) {
767 labels
= new Label
[resume_points
.Count
+ 1];
768 for (int j
= 0; j
<= i
; ++j
)
774 if (labels
!= null) {
775 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
776 ig
.Emit (OpCodes
.Ldarg_0
);
777 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
778 ig
.Emit (OpCodes
.Stloc
, current_pc
);
781 ig
.Emit (OpCodes
.Ldarg_0
);
782 IntConstant
.EmitInt (ig
, (int) State
.After
);
783 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
785 if (labels
!= null) {
786 //SymbolWriter.StartIteratorDispatcher (ec.ig);
787 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
788 ig
.Emit (OpCodes
.Switch
, labels
);
789 //SymbolWriter.EndIteratorDispatcher (ec.ig);
791 foreach (ResumableStatement s
in resume_points
)
792 s
.EmitForDispose (ec
, this, end
, true);
799 ArrayList resume_points
;
800 public int AddResumePoint (ResumableStatement stmt
)
802 if (resume_points
== null)
803 resume_points
= new ArrayList ();
804 resume_points
.Add (stmt
);
805 return resume_points
.Count
;
809 // Called back from Yield
811 public void MarkYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
813 ILGenerator ig
= ec
.ig
;
815 // Store the new current
816 ig
.Emit (OpCodes
.Ldarg_0
);
818 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.CurrentField
.FieldBuilder
);
820 // store resume program-counter
821 ig
.Emit (OpCodes
.Ldarg_0
);
822 IntConstant
.EmitInt (ig
, resume_pc
);
823 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
825 // mark finally blocks as disabled
826 if (unwind_protect
&& skip_finally
!= null) {
827 ig
.Emit (OpCodes
.Ldc_I4_1
);
828 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
832 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_ok
);
834 ig
.MarkLabel (resume_point
);
837 public override string ContainerType
{
838 get { return "iterator"; }
841 public override bool IsIterator
{
845 public override AnonymousMethodStorey Storey
{
846 get { return IteratorHost; }
852 private Iterator (IMethodData method
, TypeContainer host
, Type iterator_type
, bool is_enumerable
)
854 new ToplevelBlock (method
.Block
, Parameters
.EmptyReadOnlyParameters
, method
.Block
.StartLocation
),
855 TypeManager
.bool_type
,
858 this.OriginalMethod
= method
;
859 this.OriginalIteratorType
= iterator_type
;
860 this.IsEnumerable
= is_enumerable
;
863 IteratorHost
= Block
.ChangeToIterator (this, method
.Block
);
866 public override string GetSignatureForError ()
868 return OriginalMethod
.GetSignatureForError ();
871 public override Expression
DoResolve (EmitContext ec
)
873 method
= new AnonymousMethodMethod (Storey
,
874 this, Storey
, null, TypeManager
.system_boolean_expr
,
875 Modifiers
.PUBLIC
, OriginalMethod
.GetSignatureForError (),
876 new MemberName ("MoveNext", Location
),
877 Parameters
.EmptyReadOnlyParameters
);
879 if (!Compatible (ec
))
882 IteratorHost
.DefineIteratorMembers ();
884 eclass
= ExprClass
.Value
;
885 type
= ec
.ReturnType
;
889 public override void Emit (EmitContext ec
)
892 // Load Iterator storey instance
894 method
.Storey
.Instance
.Emit (ec
);
897 // Initialize iterator PC when it's unitialized
900 ILGenerator ig
= ec
.ig
;
901 ig
.Emit (OpCodes
.Dup
);
902 IntConstant
.EmitInt (ig
, (int)State
.Uninitialized
);
904 FieldInfo field
= IteratorHost
.PC
.FieldBuilder
;
906 if (Storey
.MemberName
.IsGeneric
)
907 field
= TypeBuilder
.GetField (Storey
.Instance
.Type
, field
);
909 ig
.Emit (OpCodes
.Stfld
, field
);
913 public override Expression
CreateExpressionTree (EmitContext ec
)
915 throw new NotSupportedException ("ET");
918 public static void CreateIterator (IMethodData method
, TypeContainer parent
, int modifiers
)
923 Type ret
= method
.ReturnType
;
927 if (!CheckType (ret
, out iterator_type
, out is_enumerable
)) {
928 Report
.Error (1624, method
.Location
,
929 "The body of `{0}' cannot be an iterator block " +
930 "because `{1}' is not an iterator interface type",
931 method
.GetSignatureForError (),
932 TypeManager
.CSharpName (ret
));
936 Parameters parameters
= method
.ParameterInfo
;
937 for (int i
= 0; i
< parameters
.Count
; i
++) {
938 Parameter p
= parameters
[i
];
939 Parameter
.Modifier mod
= p
.ModFlags
;
940 if ((mod
& Parameter
.Modifier
.ISBYREF
) != 0) {
941 Report
.Error (1623, p
.Location
,
942 "Iterators cannot have ref or out parameters");
946 if ((mod
& Parameter
.Modifier
.ARGLIST
) != 0) {
947 Report
.Error (1636, method
.Location
,
948 "__arglist is not allowed in parameter list of iterators");
952 if (parameters
.Types
[i
].IsPointer
) {
953 Report
.Error (1637, p
.Location
,
954 "Iterators cannot have unsafe parameters or " +
960 if ((modifiers
& Modifiers
.UNSAFE
) != 0) {
961 Report
.Error (1629, method
.Location
, "Unsafe code may not appear in iterators");
965 Iterator iter
= new Iterator (method
, parent
, iterator_type
, is_enumerable
);
966 iter
.Storey
.DefineType ();
969 static bool CheckType (Type ret
, out Type original_iterator_type
, out bool is_enumerable
)
971 original_iterator_type
= null;
972 is_enumerable
= false;
974 if (ret
== TypeManager
.ienumerable_type
) {
975 original_iterator_type
= TypeManager
.object_type
;
976 is_enumerable
= true;
979 if (ret
== TypeManager
.ienumerator_type
) {
980 original_iterator_type
= TypeManager
.object_type
;
981 is_enumerable
= false;
985 if (!TypeManager
.IsGenericType (ret
))
988 Type
[] args
= TypeManager
.GetTypeArguments (ret
);
989 if (args
.Length
!= 1)
992 Type gt
= TypeManager
.DropGenericTypeArguments (ret
);
993 if (gt
== TypeManager
.generic_ienumerable_type
) {
994 original_iterator_type
= args
[0];
995 is_enumerable
= true;
999 if (gt
== TypeManager
.generic_ienumerator_type
) {
1000 original_iterator_type
= args
[0];
1001 is_enumerable
= false;