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 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 void MutateHoistedGenericType (AnonymousMethodStorey storey
)
54 expr
.MutateHoistedGenericType (storey
);
57 public override bool Resolve (BlockContext ec
)
59 expr
= expr
.Resolve (ec
);
63 Report
.Debug (64, "RESOLVE YIELD #1", this, ec
, expr
, expr
.GetType (),
64 ec
.CurrentAnonymousMethod
, ec
.CurrentIterator
);
66 if (!CheckContext (ec
, loc
))
69 iterator
= ec
.CurrentIterator
;
70 if (expr
.Type
!= iterator
.OriginalIteratorType
) {
71 expr
= Convert
.ImplicitConversionRequired (
72 ec
, expr
, iterator
.OriginalIteratorType
, loc
);
77 if (!ec
.CurrentBranching
.CurrentUsageVector
.IsUnreachable
)
78 unwind_protect
= ec
.CurrentBranching
.AddResumePoint (this, loc
, out resume_pc
);
83 protected override void DoEmit (EmitContext ec
)
85 iterator
.MarkYield (ec
, expr
, resume_pc
, unwind_protect
, resume_point
);
88 protected override void CloneTo (CloneContext clonectx
, Statement t
)
90 Yield target
= (Yield
) t
;
92 target
.expr
= expr
.Clone (clonectx
);
96 public class YieldBreak
: ExitStatement
100 public YieldBreak (Location l
)
105 public override void Error_FinallyClause (Report Report
)
107 Report
.Error (1625, loc
, "Cannot yield in the body of a finally clause");
110 protected override void CloneTo (CloneContext clonectx
, Statement target
)
112 throw new NotSupportedException ();
115 protected override bool DoResolve (BlockContext ec
)
117 iterator
= ec
.CurrentIterator
;
118 return Yield
.CheckContext (ec
, loc
);
121 protected override void DoEmit (EmitContext ec
)
123 iterator
.EmitYieldBreak (ec
.ig
, unwind_protect
);
126 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
133 // Wraps method block into iterator wrapper block
135 class IteratorStatement
: Statement
138 Block original_block
;
140 public IteratorStatement (Iterator iterator
, Block original_block
)
142 this.iterator
= iterator
;
143 this.original_block
= original_block
;
144 this.loc
= iterator
.Location
;
147 protected override void CloneTo (CloneContext clonectx
, Statement target
)
149 IteratorStatement t
= (IteratorStatement
) target
;
150 t
.original_block
= (ExplicitBlock
) original_block
.Clone (clonectx
);
151 t
.iterator
= (Iterator
) iterator
.Clone (clonectx
);
154 public override bool Resolve (BlockContext ec
)
156 ec
.StartFlowBranching (iterator
);
157 bool ok
= original_block
.Resolve (ec
);
158 ec
.EndFlowBranching ();
162 protected override void DoEmit (EmitContext ec
)
164 iterator
.EmitMoveNext (ec
, original_block
);
167 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
169 original_block
.MutateHoistedGenericType (storey
);
170 iterator
.MutateHoistedGenericType (storey
);
174 public class IteratorStorey
: AnonymousMethodStorey
176 class IteratorMethod
: Method
178 readonly IteratorStorey host
;
180 public IteratorMethod (IteratorStorey host
, FullNamedExpression returnType
, Modifiers mod
, MemberName name
)
181 : base (host
, null, returnType
, mod
| Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.COMPILER_GENERATED
,
182 name
, ParametersCompiled
.EmptyReadOnlyParameters
, null)
186 Block
= new ToplevelBlock (Compiler
, host
.Iterator
.Container
.Toplevel
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
);
189 public override EmitContext
CreateEmitContext (ILGenerator ig
)
191 EmitContext ec
= new EmitContext (this, ig
, MemberType
);
193 ec
.CurrentAnonymousMethod
= host
.Iterator
;
198 class GetEnumeratorMethod
: IteratorMethod
200 sealed class GetEnumeratorStatement
: Statement
203 IteratorMethod host_method
;
205 Expression new_storey
;
207 public GetEnumeratorStatement (IteratorStorey host
, IteratorMethod host_method
)
210 this.host_method
= host_method
;
211 loc
= host_method
.Location
;
214 protected override void CloneTo (CloneContext clonectx
, Statement target
)
216 throw new NotSupportedException ();
219 public override bool Resolve (BlockContext ec
)
221 TypeExpression storey_type_expr
= new TypeExpression (host
.TypeBuilder
, loc
);
222 List
<Expression
> init
= null;
223 if (host
.hoisted_this
!= null) {
224 init
= new List
<Expression
> (host
.hoisted_params
== null ? 1 : host
.HoistedParameters
.Count
+ 1);
225 HoistedThis ht
= host
.hoisted_this
;
226 FieldExpr
from = new FieldExpr (ht
.Field
, loc
);
227 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
228 init
.Add (new ElementInitializer (ht
.Field
.Name
, from, loc
));
231 if (host
.hoisted_params
!= null) {
233 init
= new List
<Expression
> (host
.HoistedParameters
.Count
);
235 for (int i
= 0; i
< host
.hoisted_params
.Count
; ++i
) {
236 HoistedParameter hp
= (HoistedParameter
) host
.hoisted_params
[i
];
237 HoistedParameter hp_cp
= (HoistedParameter
) host
.hoisted_params_copy
[i
];
239 FieldExpr
from = new FieldExpr (hp_cp
.Field
, loc
);
240 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
242 init
.Add (new ElementInitializer (hp
.Field
.Name
, from, loc
));
247 new_storey
= new NewInitialize (storey_type_expr
, null,
248 new CollectionOrObjectInitializers (init
, loc
), loc
);
250 new_storey
= new New (storey_type_expr
, null, loc
);
253 new_storey
= new_storey
.Resolve (ec
);
254 if (new_storey
!= null)
255 new_storey
= Convert
.ImplicitConversionRequired (ec
, new_storey
, host_method
.MemberType
, loc
);
257 if (TypeManager
.int_interlocked_compare_exchange
== null) {
258 Type t
= TypeManager
.CoreLookupType (ec
.Compiler
, "System.Threading", "Interlocked", MemberKind
.Class
, true);
260 TypeManager
.int_interlocked_compare_exchange
= TypeManager
.GetPredefinedMethod (
261 t
, "CompareExchange", loc
, TypeManager
.int32_type
,
262 TypeManager
.int32_type
, TypeManager
.int32_type
);
266 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
270 protected override void DoEmit (EmitContext ec
)
272 ILGenerator ig
= ec
.ig
;
273 Label label_init
= ig
.DefineLabel ();
275 ig
.Emit (OpCodes
.Ldarg_0
);
276 ig
.Emit (OpCodes
.Ldflda
, host
.PC
.Spec
.MetaInfo
);
277 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Start
);
278 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
279 ig
.Emit (OpCodes
.Call
, (MethodInfo
) TypeManager
.int_interlocked_compare_exchange
.MetaInfo
);
281 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
282 ig
.Emit (OpCodes
.Bne_Un_S
, label_init
);
284 ig
.Emit (OpCodes
.Ldarg_0
);
285 ig
.Emit (OpCodes
.Ret
);
287 ig
.MarkLabel (label_init
);
289 new_storey
.Emit (ec
);
290 ig
.Emit (OpCodes
.Ret
);
293 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
295 throw new NotSupportedException ();
299 public GetEnumeratorMethod (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
300 : base (host
, returnType
, 0, name
)
302 Block
.AddStatement (new GetEnumeratorStatement (host
, this));
306 class DisposeMethod
: IteratorMethod
308 sealed class DisposeMethodStatement
: Statement
312 public DisposeMethodStatement (Iterator iterator
)
314 this.iterator
= iterator
;
315 this.loc
= iterator
.Location
;
318 protected override void CloneTo (CloneContext clonectx
, Statement target
)
320 throw new NotSupportedException ();
323 public override bool Resolve (BlockContext ec
)
328 protected override void DoEmit (EmitContext ec
)
330 iterator
.EmitDispose (ec
);
333 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
335 throw new NotSupportedException ();
339 public DisposeMethod (IteratorStorey host
)
340 : base (host
, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
, new MemberName ("Dispose", host
.Location
))
342 host
.AddMethod (this);
344 Block
= new ToplevelBlock (Compiler
, host
.Iterator
.Container
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
);
345 Block
.AddStatement (new DisposeMethodStatement (host
.Iterator
));
350 // Uses Method as method info
352 class DynamicMethodGroupExpr
: MethodGroupExpr
354 readonly Method method
;
356 public DynamicMethodGroupExpr (Method method
, Location loc
)
359 this.method
= method
;
360 eclass
= ExprClass
.Unresolved
;
363 protected override Expression
DoResolve (ResolveContext ec
)
365 Methods
= new [] { method.Spec }
;
366 type
= method
.Parent
.TypeBuilder
;
367 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
368 return base.DoResolve (ec
);
372 class DynamicFieldExpr
: FieldExpr
374 readonly Field field
;
376 public DynamicFieldExpr (Field field
, Location loc
)
382 protected override Expression
DoResolve (ResolveContext ec
)
385 type
= TypeManager
.TypeToCoreType (spec
.FieldType
);
386 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
387 return base.DoResolve (ec
);
391 public readonly Iterator Iterator
;
393 TypeExpr iterator_type_expr
;
397 TypeExpr enumerator_type
;
398 TypeExpr enumerable_type
;
399 TypeArguments generic_args
;
400 TypeExpr generic_enumerator_type
;
401 TypeExpr generic_enumerable_type
;
403 List
<HoistedParameter
> hoisted_params_copy
;
406 public IteratorStorey (Iterator iterator
)
407 : base (iterator
.Container
.Toplevel
, iterator
.Host
,
408 iterator
.OriginalMethod
as MemberBase
, iterator
.GenericMethod
, "Iterator")
410 this.Iterator
= iterator
;
414 get { return pc_field; }
417 public Field CurrentField
{
418 get { return current_field; }
421 public IList
<HoistedParameter
> HoistedParameters
{
422 get { return hoisted_params; }
425 protected override TypeExpr
[] ResolveBaseTypes (out TypeExpr base_class
)
427 iterator_type_expr
= new TypeExpression (MutateType (Iterator
.OriginalIteratorType
), Location
);
428 generic_args
= new TypeArguments (iterator_type_expr
);
430 var list
= new List
<FullNamedExpression
> ();
431 if (Iterator
.IsEnumerable
) {
432 enumerable_type
= new TypeExpression (
433 TypeManager
.ienumerable_type
, Location
);
434 list
.Add (enumerable_type
);
436 if (TypeManager
.generic_ienumerable_type
!= null) {
437 generic_enumerable_type
= new GenericTypeExpr (
438 TypeManager
.generic_ienumerable_type
,
439 generic_args
, Location
);
440 list
.Add (generic_enumerable_type
);
444 enumerator_type
= new TypeExpression (
445 TypeManager
.ienumerator_type
, Location
);
446 list
.Add (enumerator_type
);
448 list
.Add (new TypeExpression (TypeManager
.idisposable_type
, Location
));
450 if (TypeManager
.generic_ienumerator_type
!= null) {
451 generic_enumerator_type
= new GenericTypeExpr (
452 TypeManager
.generic_ienumerator_type
,
453 generic_args
, Location
);
454 list
.Add (generic_enumerator_type
);
459 return base.ResolveBaseTypes (out base_class
);
462 protected override string GetVariableMangledName (LocalInfo local_info
)
464 return "<" + local_info
.Name
+ ">__" + local_name_idx
++.ToString ();
467 public void DefineIteratorMembers ()
469 pc_field
= AddCompilerGeneratedField ("$PC", TypeManager
.system_int32_expr
);
470 current_field
= AddCompilerGeneratedField ("$current", iterator_type_expr
);
472 if (hoisted_params
!= null) {
474 // Iterators are independent, each GetEnumerator call has to
475 // create same enumerator therefore we have to keep original values
476 // around for re-initialization
478 // TODO: Do it for assigned/modified parameters only
480 hoisted_params_copy
= new List
<HoistedParameter
> (hoisted_params
.Count
);
481 foreach (HoistedParameter hp
in hoisted_params
) {
482 hoisted_params_copy
.Add (new HoistedParameter (hp
, "<$>" + hp
.Field
.Name
));
486 if (generic_enumerator_type
!= null)
487 Define_Current (true);
489 Define_Current (false);
490 new DisposeMethod (this);
493 if (Iterator
.IsEnumerable
) {
494 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
495 name
= new MemberName (name
, "Collections", Location
);
496 name
= new MemberName (name
, "IEnumerable", Location
);
497 name
= new MemberName (name
, "GetEnumerator", Location
);
499 if (generic_enumerator_type
!= null) {
500 Method get_enumerator
= new IteratorMethod (this, enumerator_type
, 0, name
);
502 name
= new MemberName (name
.Left
.Left
, "Generic", Location
);
503 name
= new MemberName (name
, "IEnumerable", generic_args
, Location
);
504 name
= new MemberName (name
, "GetEnumerator", Location
);
505 Method gget_enumerator
= new GetEnumeratorMethod (this, generic_enumerator_type
, name
);
508 // Just call generic GetEnumerator implementation
510 get_enumerator
.Block
.AddStatement (
511 new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator
, Location
), null), Location
));
513 AddMethod (get_enumerator
);
514 AddMethod (gget_enumerator
);
516 AddMethod (new GetEnumeratorMethod (this, enumerator_type
, name
));
521 protected override void EmitHoistedParameters (EmitContext ec
, IList
<HoistedParameter
> hoisted
)
523 base.EmitHoistedParameters (ec
, hoisted
);
524 base.EmitHoistedParameters (ec
, hoisted_params_copy
);
527 void Define_Current (bool is_generic
)
531 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
532 name
= new MemberName (name
, "Collections", Location
);
535 name
= new MemberName (name
, "Generic", Location
);
536 name
= new MemberName (name
, "IEnumerator", generic_args
, Location
);
537 type
= iterator_type_expr
;
539 name
= new MemberName (name
, "IEnumerator");
540 type
= TypeManager
.system_object_expr
;
543 name
= new MemberName (name
, "Current", Location
);
545 ToplevelBlock get_block
= new ToplevelBlock (Compiler
, Location
);
546 get_block
.AddStatement (new Return (new DynamicFieldExpr (CurrentField
, Location
), Location
));
548 Accessor getter
= new Accessor (get_block
, 0, null, null, Location
);
550 Property current
= new Property (
551 this, type
, Modifiers
.DEBUGGER_HIDDEN
, name
, null, getter
, null, false);
552 AddProperty (current
);
557 Method reset
= new Method (
558 this, null, TypeManager
.system_void_expr
,
559 Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
,
560 new MemberName ("Reset", Location
),
561 ParametersCompiled
.EmptyReadOnlyParameters
, null);
564 reset
.Block
= new ToplevelBlock (Compiler
, Location
);
566 Type ex_type
= TypeManager
.CoreLookupType (Compiler
, "System", "NotSupportedException", MemberKind
.Class
, true);
570 reset
.Block
.AddStatement (new Throw (new New (new TypeExpression (ex_type
, Location
), null, Location
), Location
));
575 // Iterators are implemented as hidden anonymous block
577 public class Iterator
: AnonymousExpression
{
578 public readonly IMethodData OriginalMethod
;
579 AnonymousMethodMethod method
;
580 public readonly TypeContainer Host
;
581 public readonly bool IsEnumerable
;
582 List
<ResumableStatement
> resume_points
;
585 // The state as we generate the iterator
587 Label move_next_ok
, move_next_error
;
588 LocalBuilder skip_finally
, current_pc
;
590 public LocalBuilder SkipFinally
{
591 get { return skip_finally; }
594 public LocalBuilder CurrentPC
{
595 get { return current_pc; }
598 public Block Container
{
599 get { return OriginalMethod.Block; }
602 public GenericMethod GenericMethod
{
603 get { return OriginalMethod.GenericMethod; }
606 public readonly Type OriginalIteratorType
;
608 readonly IteratorStorey IteratorHost
;
611 Running
= -3, // Used only in CurrentPC, never stored into $PC
617 public void EmitYieldBreak (ILGenerator ig
, bool unwind_protect
)
619 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
622 void EmitMoveNext_NoResumePoints (EmitContext ec
, Block original_block
)
624 ILGenerator ig
= ec
.ig
;
626 ig
.Emit (OpCodes
.Ldarg_0
);
627 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
629 ig
.Emit (OpCodes
.Ldarg_0
);
630 IntConstant
.EmitInt (ig
, (int) State
.After
);
631 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
633 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
634 ig
.Emit (OpCodes
.Brtrue
, move_next_error
);
636 SymbolWriter
.StartIteratorBody (ec
.ig
);
637 original_block
.Emit (ec
);
638 SymbolWriter
.EndIteratorBody (ec
.ig
);
640 ig
.MarkLabel (move_next_error
);
641 ig
.Emit (OpCodes
.Ldc_I4_0
);
642 ig
.Emit (OpCodes
.Ret
);
645 internal void EmitMoveNext (EmitContext ec
, Block original_block
)
647 ILGenerator ig
= ec
.ig
;
649 move_next_ok
= ig
.DefineLabel ();
650 move_next_error
= ig
.DefineLabel ();
652 if (resume_points
== null) {
653 EmitMoveNext_NoResumePoints (ec
, original_block
);
657 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
658 ig
.Emit (OpCodes
.Ldarg_0
);
659 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
660 ig
.Emit (OpCodes
.Stloc
, current_pc
);
662 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
663 ig
.Emit (OpCodes
.Ldarg_0
);
664 IntConstant
.EmitInt (ig
, (int) State
.After
);
665 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
667 Label
[] labels
= new Label
[1 + resume_points
.Count
];
668 labels
[0] = ig
.DefineLabel ();
670 bool need_skip_finally
= false;
671 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
672 ResumableStatement s
= resume_points
[i
];
673 need_skip_finally
|= s
is ExceptionStatement
;
674 labels
[i
+1] = s
.PrepareForEmit (ec
);
677 if (need_skip_finally
) {
678 skip_finally
= ec
.GetTemporaryLocal (TypeManager
.bool_type
);
679 ig
.Emit (OpCodes
.Ldc_I4_0
);
680 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
683 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
684 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
685 ig
.Emit (OpCodes
.Switch
, labels
);
687 ig
.Emit (OpCodes
.Br
, move_next_error
);
688 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
690 ig
.MarkLabel (labels
[0]);
692 SymbolWriter
.StartIteratorBody (ec
.ig
);
693 original_block
.Emit (ec
);
694 SymbolWriter
.EndIteratorBody (ec
.ig
);
696 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
698 ig
.Emit (OpCodes
.Ldarg_0
);
699 IntConstant
.EmitInt (ig
, (int) State
.After
);
700 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
702 ig
.MarkLabel (move_next_error
);
703 ig
.Emit (OpCodes
.Ldc_I4_0
);
704 ig
.Emit (OpCodes
.Ret
);
706 ig
.MarkLabel (move_next_ok
);
707 ig
.Emit (OpCodes
.Ldc_I4_1
);
708 ig
.Emit (OpCodes
.Ret
);
710 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
713 public void EmitDispose (EmitContext ec
)
715 ILGenerator ig
= ec
.ig
;
717 Label end
= ig
.DefineLabel ();
719 Label
[] labels
= null;
720 int n_resume_points
= resume_points
== null ? 0 : resume_points
.Count
;
721 for (int i
= 0; i
< n_resume_points
; ++i
) {
722 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
723 Label ret
= s
.PrepareForDispose (ec
, end
);
724 if (ret
.Equals (end
) && labels
== null)
726 if (labels
== null) {
727 labels
= new Label
[resume_points
.Count
+ 1];
728 for (int j
= 0; j
<= i
; ++j
)
734 if (labels
!= null) {
735 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
736 ig
.Emit (OpCodes
.Ldarg_0
);
737 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
738 ig
.Emit (OpCodes
.Stloc
, current_pc
);
741 ig
.Emit (OpCodes
.Ldarg_0
);
742 IntConstant
.EmitInt (ig
, (int) State
.After
);
743 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
745 if (labels
!= null) {
746 //SymbolWriter.StartIteratorDispatcher (ec.ig);
747 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
748 ig
.Emit (OpCodes
.Switch
, labels
);
749 //SymbolWriter.EndIteratorDispatcher (ec.ig);
751 foreach (ResumableStatement s
in resume_points
)
752 s
.EmitForDispose (ec
, this, end
, true);
758 public int AddResumePoint (ResumableStatement stmt
)
760 if (resume_points
== null)
761 resume_points
= new List
<ResumableStatement
> ();
763 resume_points
.Add (stmt
);
764 return resume_points
.Count
;
768 // Called back from Yield
770 public void MarkYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
772 ILGenerator ig
= ec
.ig
;
774 // Store the new current
775 ig
.Emit (OpCodes
.Ldarg_0
);
777 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.CurrentField
.Spec
.MetaInfo
);
779 // store resume program-counter
780 ig
.Emit (OpCodes
.Ldarg_0
);
781 IntConstant
.EmitInt (ig
, resume_pc
);
782 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.Spec
.MetaInfo
);
784 // mark finally blocks as disabled
785 if (unwind_protect
&& skip_finally
!= null) {
786 ig
.Emit (OpCodes
.Ldc_I4_1
);
787 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
791 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_ok
);
793 ig
.MarkLabel (resume_point
);
796 public override string ContainerType
{
797 get { return "iterator"; }
800 public override bool IsIterator
{
804 public override AnonymousMethodStorey Storey
{
805 get { return IteratorHost; }
811 private Iterator (CompilerContext ctx
, IMethodData method
, TypeContainer host
, Type iterator_type
, bool is_enumerable
)
813 new ToplevelBlock (ctx
, method
.Block
, ParametersCompiled
.EmptyReadOnlyParameters
, method
.Block
.StartLocation
),
814 TypeManager
.bool_type
,
817 this.OriginalMethod
= method
;
818 this.OriginalIteratorType
= iterator_type
;
819 this.IsEnumerable
= is_enumerable
;
821 this.type
= method
.ReturnType
;
823 IteratorHost
= Block
.ChangeToIterator (this, method
.Block
);
826 public override string GetSignatureForError ()
828 return OriginalMethod
.GetSignatureForError ();
831 protected override Expression
DoResolve (ResolveContext ec
)
833 method
= new AnonymousMethodMethod (Storey
,
834 this, Storey
, null, TypeManager
.system_boolean_expr
,
835 Modifiers
.PUBLIC
, OriginalMethod
.GetSignatureForError (),
836 new MemberName ("MoveNext", Location
),
837 ParametersCompiled
.EmptyReadOnlyParameters
);
839 if (Compatible (ec
) == null)
842 IteratorHost
.DefineIteratorMembers ();
844 eclass
= ExprClass
.Value
;
848 public override void Emit (EmitContext ec
)
851 // Load Iterator storey instance
853 method
.Storey
.Instance
.Emit (ec
);
856 // Initialize iterator PC when it's unitialized
859 ILGenerator ig
= ec
.ig
;
860 ig
.Emit (OpCodes
.Dup
);
861 IntConstant
.EmitInt (ig
, (int)State
.Uninitialized
);
863 FieldInfo field
= IteratorHost
.PC
.Spec
.MetaInfo
;
864 if (Storey
.MemberName
.IsGeneric
)
865 field
= TypeBuilder
.GetField (Storey
.Instance
.Type
, field
);
867 ig
.Emit (OpCodes
.Stfld
, field
);
871 public override Expression
CreateExpressionTree (ResolveContext ec
)
873 throw new NotSupportedException ("ET");
876 public static void CreateIterator (IMethodData method
, TypeContainer parent
, Modifiers modifiers
, CompilerContext ctx
)
881 Type ret
= method
.ReturnType
;
885 if (!CheckType (ret
, out iterator_type
, out is_enumerable
)) {
886 ctx
.Report
.Error (1624, method
.Location
,
887 "The body of `{0}' cannot be an iterator block " +
888 "because `{1}' is not an iterator interface type",
889 method
.GetSignatureForError (),
890 TypeManager
.CSharpName (ret
));
894 ParametersCompiled parameters
= method
.ParameterInfo
;
895 for (int i
= 0; i
< parameters
.Count
; i
++) {
896 Parameter p
= parameters
[i
];
897 Parameter
.Modifier mod
= p
.ModFlags
;
898 if ((mod
& Parameter
.Modifier
.ISBYREF
) != 0) {
899 ctx
.Report
.Error (1623, p
.Location
,
900 "Iterators cannot have ref or out parameters");
904 if (p
is ArglistParameter
) {
905 ctx
.Report
.Error (1636, method
.Location
,
906 "__arglist is not allowed in parameter list of iterators");
910 if (parameters
.Types
[i
].IsPointer
) {
911 ctx
.Report
.Error (1637, p
.Location
,
912 "Iterators cannot have unsafe parameters or " +
918 if ((modifiers
& Modifiers
.UNSAFE
) != 0) {
919 ctx
.Report
.Error (1629, method
.Location
, "Unsafe code may not appear in iterators");
923 Iterator iter
= new Iterator (ctx
, method
, parent
, iterator_type
, is_enumerable
);
924 iter
.Storey
.DefineType ();
927 static bool CheckType (Type ret
, out Type original_iterator_type
, out bool is_enumerable
)
929 original_iterator_type
= null;
930 is_enumerable
= false;
932 if (ret
== TypeManager
.ienumerable_type
) {
933 original_iterator_type
= TypeManager
.object_type
;
934 is_enumerable
= true;
937 if (ret
== TypeManager
.ienumerator_type
) {
938 original_iterator_type
= TypeManager
.object_type
;
939 is_enumerable
= false;
943 if (!TypeManager
.IsGenericType (ret
))
946 Type
[] args
= TypeManager
.GetTypeArguments (ret
);
947 if (args
.Length
!= 1)
950 Type gt
= TypeManager
.DropGenericTypeArguments (ret
);
951 if (gt
== TypeManager
.generic_ienumerable_type
) {
952 original_iterator_type
= TypeManager
.TypeToCoreType (args
[0]);
953 is_enumerable
= true;
957 if (gt
== TypeManager
.generic_ienumerator_type
) {
958 original_iterator_type
= TypeManager
.TypeToCoreType (args
[0]);
959 is_enumerable
= false;