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 Block original_block
;
144 public IteratorStatement (Iterator iterator
, Block 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
, ParametersCompiled
.EmptyReadOnlyParameters
, null)
190 Block
= new ToplevelBlock (host
.Iterator
.Container
.Toplevel
, ParametersCompiled
.EmptyReadOnlyParameters
, 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
208 IteratorMethod host_method
;
210 Expression new_storey
;
212 public GetEnumeratorStatement (IteratorStorey host
, IteratorMethod host_method
)
215 this.host_method
= host_method
;
216 loc
= host_method
.Location
;
219 protected override void CloneTo (CloneContext clonectx
, Statement target
)
221 throw new NotSupportedException ();
224 public override bool Resolve (EmitContext ec
)
226 TypeExpression storey_type_expr
= new TypeExpression (host
.TypeBuilder
, loc
);
227 ArrayList init
= null;
228 if (host
.hoisted_this
!= null) {
229 init
= new ArrayList (host
.hoisted_params
== null ? 1 : host
.HoistedParameters
.Count
+ 1);
230 HoistedThis ht
= host
.hoisted_this
;
231 FieldExpr
from = new FieldExpr (ht
.Field
.FieldBuilder
, loc
);
232 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
233 init
.Add (new ElementInitializer (ht
.Field
.Name
, from, loc
));
236 if (host
.hoisted_params
!= null) {
238 init
= new ArrayList (host
.HoistedParameters
.Count
);
240 for (int i
= 0; i
< host
.hoisted_params
.Count
; ++i
) {
241 HoistedParameter hp
= (HoistedParameter
) host
.hoisted_params
[i
];
242 HoistedParameter hp_cp
= (HoistedParameter
) host
.hoisted_params_copy
[i
];
244 FieldExpr
from = new FieldExpr (hp_cp
.Field
.FieldBuilder
, loc
);
245 from.InstanceExpression
= CompilerGeneratedThis
.Instance
;
247 init
.Add (new ElementInitializer (hp
.Field
.Name
, from, loc
));
252 new_storey
= new NewInitialize (storey_type_expr
, null,
253 new CollectionOrObjectInitializers (init
, loc
), loc
);
255 new_storey
= new New (storey_type_expr
, null, loc
);
258 new_storey
= new_storey
.Resolve (ec
);
259 if (new_storey
!= null)
260 new_storey
= Convert
.ImplicitConversionRequired (ec
, new_storey
, host_method
.MemberType
, loc
);
262 if (TypeManager
.int_interlocked_compare_exchange
== null) {
263 Type t
= TypeManager
.CoreLookupType ("System.Threading", "Interlocked", Kind
.Class
, true);
265 TypeManager
.int_interlocked_compare_exchange
= TypeManager
.GetPredefinedMethod (
266 t
, "CompareExchange", loc
, TypeManager
.int32_type
,
267 TypeManager
.int32_type
, TypeManager
.int32_type
);
271 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
275 protected override void DoEmit (EmitContext ec
)
277 ILGenerator ig
= ec
.ig
;
278 Label label_init
= ig
.DefineLabel ();
280 ig
.Emit (OpCodes
.Ldarg_0
);
281 ig
.Emit (OpCodes
.Ldflda
, host
.PC
.FieldBuilder
);
282 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Start
);
283 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
284 ig
.Emit (OpCodes
.Call
, TypeManager
.int_interlocked_compare_exchange
);
286 IntConstant
.EmitInt (ig
, (int) Iterator
.State
.Uninitialized
);
287 ig
.Emit (OpCodes
.Bne_Un_S
, label_init
);
289 ig
.Emit (OpCodes
.Ldarg_0
);
290 ig
.Emit (OpCodes
.Ret
);
292 ig
.MarkLabel (label_init
);
294 new_storey
.Emit (ec
);
295 ig
.Emit (OpCodes
.Ret
);
298 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
300 throw new NotSupportedException ();
304 public GetEnumeratorMethod (IteratorStorey host
, FullNamedExpression returnType
, MemberName name
)
305 : base (host
, returnType
, 0, name
)
307 Block
.AddStatement (new GetEnumeratorStatement (host
, this));
311 class DisposeMethod
: IteratorMethod
313 sealed class DisposeMethodStatement
: Statement
317 public DisposeMethodStatement (Iterator iterator
)
319 this.iterator
= iterator
;
320 this.loc
= iterator
.Location
;
323 protected override void CloneTo (CloneContext clonectx
, Statement target
)
325 throw new NotSupportedException ();
328 public override bool Resolve (EmitContext ec
)
333 protected override void DoEmit (EmitContext ec
)
335 iterator
.EmitDispose (ec
);
338 public override void MutateHoistedGenericType (AnonymousMethodStorey storey
)
340 throw new NotSupportedException ();
344 public DisposeMethod (IteratorStorey host
)
345 : base (host
, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
, new MemberName ("Dispose", host
.Location
))
347 host
.AddMethod (this);
349 Block
= new ToplevelBlock (host
.Iterator
.Container
, ParametersCompiled
.EmptyReadOnlyParameters
, Location
);
350 Block
.AddStatement (new DisposeMethodStatement (host
.Iterator
));
355 // Uses Method as method info
357 class DynamicMethodGroupExpr
: MethodGroupExpr
359 readonly Method method
;
361 public DynamicMethodGroupExpr (Method method
, Location loc
)
364 this.method
= method
;
367 public override Expression
DoResolve (EmitContext ec
)
369 Methods
= new MethodBase
[] { method.MethodBuilder }
;
370 type
= method
.Parent
.TypeBuilder
;
371 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
372 return base.DoResolve (ec
);
376 class DynamicFieldExpr
: FieldExpr
378 readonly Field field
;
380 public DynamicFieldExpr (Field field
, Location loc
)
386 public override Expression
DoResolve (EmitContext ec
)
388 FieldInfo
= field
.FieldBuilder
;
389 type
= TypeManager
.TypeToCoreType (FieldInfo
.FieldType
);
390 InstanceExpression
= new CompilerGeneratedThis (type
, Location
);
391 return base.DoResolve (ec
);
395 public readonly Iterator Iterator
;
397 TypeExpr iterator_type_expr
;
401 TypeExpr enumerator_type
;
402 TypeExpr enumerable_type
;
403 TypeArguments generic_args
;
404 TypeExpr generic_enumerator_type
;
405 TypeExpr generic_enumerable_type
;
407 ArrayList hoisted_params_copy
;
410 public IteratorStorey (Iterator iterator
)
411 : base (iterator
.Container
.Toplevel
, iterator
.Host
,
412 iterator
.OriginalMethod
as MemberBase
, iterator
.GenericMethod
, "Iterator")
414 this.Iterator
= iterator
;
418 get { return pc_field; }
421 public Field CurrentField
{
422 get { return current_field; }
425 public ArrayList HoistedParameters
{
426 get { return hoisted_params; }
429 protected override TypeExpr
[] ResolveBaseTypes (out TypeExpr base_class
)
431 iterator_type_expr
= new TypeExpression (MutateType (Iterator
.OriginalIteratorType
), Location
);
432 generic_args
= new TypeArguments (iterator_type_expr
);
434 ArrayList list
= new ArrayList ();
435 if (Iterator
.IsEnumerable
) {
436 enumerable_type
= new TypeExpression (
437 TypeManager
.ienumerable_type
, Location
);
438 list
.Add (enumerable_type
);
440 if (TypeManager
.generic_ienumerable_type
!= null) {
441 generic_enumerable_type
= new GenericTypeExpr (
442 TypeManager
.generic_ienumerable_type
,
443 generic_args
, Location
);
444 list
.Add (generic_enumerable_type
);
448 enumerator_type
= new TypeExpression (
449 TypeManager
.ienumerator_type
, Location
);
450 list
.Add (enumerator_type
);
452 list
.Add (new TypeExpression (TypeManager
.idisposable_type
, Location
));
454 if (TypeManager
.generic_ienumerator_type
!= null) {
455 generic_enumerator_type
= new GenericTypeExpr (
456 TypeManager
.generic_ienumerator_type
,
457 generic_args
, Location
);
458 list
.Add (generic_enumerator_type
);
463 return base.ResolveBaseTypes (out base_class
);
466 protected override string GetVariableMangledName (LocalInfo local_info
)
468 return "<" + local_info
.Name
+ ">__" + local_name_idx
++.ToString ();
471 public void DefineIteratorMembers ()
473 pc_field
= AddCompilerGeneratedField ("$PC", TypeManager
.system_int32_expr
);
474 current_field
= AddCompilerGeneratedField ("$current", iterator_type_expr
);
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 ArrayList (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 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
499 name
= new MemberName (name
, "Collections", Location
);
500 name
= new MemberName (name
, "IEnumerable", Location
);
501 name
= new MemberName (name
, "GetEnumerator", Location
);
503 if (generic_enumerator_type
!= null) {
504 Method get_enumerator
= new IteratorMethod (this, enumerator_type
, 0, name
);
506 name
= new MemberName (name
.Left
.Left
, "Generic", Location
);
507 name
= new MemberName (name
, "IEnumerable", generic_args
, Location
);
508 name
= new MemberName (name
, "GetEnumerator", Location
);
509 Method gget_enumerator
= new GetEnumeratorMethod (this, generic_enumerator_type
, name
);
512 // Just call generic GetEnumerator implementation
514 get_enumerator
.Block
.AddStatement (
515 new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator
, Location
), null), Location
));
517 AddMethod (get_enumerator
);
518 AddMethod (gget_enumerator
);
520 AddMethod (new GetEnumeratorMethod (this, enumerator_type
, name
));
525 protected override void EmitHoistedParameters (EmitContext ec
, ArrayList hoisted
)
527 base.EmitHoistedParameters (ec
, hoisted
);
528 base.EmitHoistedParameters (ec
, hoisted_params_copy
);
531 void Define_Current (bool is_generic
)
535 MemberName name
= new MemberName (QualifiedAliasMember
.GlobalAlias
, "System", null, Location
);
536 name
= new MemberName (name
, "Collections", Location
);
539 name
= new MemberName (name
, "Generic", Location
);
540 name
= new MemberName (name
, "IEnumerator", generic_args
, Location
);
541 type
= iterator_type_expr
;
543 name
= new MemberName (name
, "IEnumerator");
544 type
= TypeManager
.system_object_expr
;
547 name
= new MemberName (name
, "Current", Location
);
549 ToplevelBlock get_block
= new ToplevelBlock (Location
);
550 get_block
.AddStatement (new Return (new DynamicFieldExpr (CurrentField
, Location
), Location
));
552 Accessor getter
= new Accessor (get_block
, 0, null, null, Location
);
554 Property current
= new Property (
555 this, type
, Modifiers
.DEBUGGER_HIDDEN
, name
, null, getter
, null, false);
556 AddProperty (current
);
561 Method reset
= new Method (
562 this, null, TypeManager
.system_void_expr
,
563 Modifiers
.PUBLIC
| Modifiers
.DEBUGGER_HIDDEN
,
564 new MemberName ("Reset", Location
),
565 ParametersCompiled
.EmptyReadOnlyParameters
, null);
568 reset
.Block
= new ToplevelBlock (Location
);
570 Type ex_type
= TypeManager
.CoreLookupType ("System", "NotSupportedException", Kind
.Class
, true);
574 reset
.Block
.AddStatement (new Throw (new New (new TypeExpression (ex_type
, Location
), null, Location
), Location
));
579 // Iterators are implemented as hidden anonymous block
581 public class Iterator
: AnonymousExpression
{
582 public readonly IMethodData OriginalMethod
;
583 AnonymousMethodMethod method
;
584 public readonly TypeContainer Host
;
585 public readonly bool IsEnumerable
;
588 // The state as we generate the iterator
590 Label move_next_ok
, move_next_error
;
591 LocalBuilder skip_finally
, current_pc
;
593 public LocalBuilder SkipFinally
{
594 get { return skip_finally; }
597 public LocalBuilder CurrentPC
{
598 get { return current_pc; }
601 public Block Container
{
602 get { return OriginalMethod.Block; }
605 public GenericMethod GenericMethod
{
606 get { return OriginalMethod.GenericMethod; }
609 public readonly Type OriginalIteratorType
;
611 readonly IteratorStorey IteratorHost
;
614 Running
= -3, // Used only in CurrentPC, never stored into $PC
620 public void EmitYieldBreak (ILGenerator ig
, bool unwind_protect
)
622 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_error
);
625 void EmitMoveNext_NoResumePoints (EmitContext ec
, Block original_block
)
627 ILGenerator ig
= ec
.ig
;
629 ig
.Emit (OpCodes
.Ldarg_0
);
630 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
632 ig
.Emit (OpCodes
.Ldarg_0
);
633 IntConstant
.EmitInt (ig
, (int) State
.After
);
634 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
636 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
637 ig
.Emit (OpCodes
.Brtrue
, move_next_error
);
639 SymbolWriter
.StartIteratorBody (ec
.ig
);
640 original_block
.Emit (ec
);
641 SymbolWriter
.EndIteratorBody (ec
.ig
);
643 ig
.MarkLabel (move_next_error
);
644 ig
.Emit (OpCodes
.Ldc_I4_0
);
645 ig
.Emit (OpCodes
.Ret
);
648 internal void EmitMoveNext (EmitContext ec
, Block original_block
)
650 ILGenerator ig
= ec
.ig
;
652 move_next_ok
= ig
.DefineLabel ();
653 move_next_error
= ig
.DefineLabel ();
655 if (resume_points
== null) {
656 EmitMoveNext_NoResumePoints (ec
, original_block
);
660 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
661 ig
.Emit (OpCodes
.Ldarg_0
);
662 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
663 ig
.Emit (OpCodes
.Stloc
, current_pc
);
665 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
666 ig
.Emit (OpCodes
.Ldarg_0
);
667 IntConstant
.EmitInt (ig
, (int) State
.After
);
668 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
670 Label
[] labels
= new Label
[1 + resume_points
.Count
];
671 labels
[0] = ig
.DefineLabel ();
673 bool need_skip_finally
= false;
674 for (int i
= 0; i
< resume_points
.Count
; ++i
) {
675 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
676 need_skip_finally
|= s
is ExceptionStatement
;
677 labels
[i
+1] = s
.PrepareForEmit (ec
);
680 if (need_skip_finally
) {
681 skip_finally
= ec
.GetTemporaryLocal (TypeManager
.bool_type
);
682 ig
.Emit (OpCodes
.Ldc_I4_0
);
683 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
686 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
687 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
688 ig
.Emit (OpCodes
.Switch
, labels
);
690 ig
.Emit (OpCodes
.Br
, move_next_error
);
691 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
693 ig
.MarkLabel (labels
[0]);
695 SymbolWriter
.StartIteratorBody (ec
.ig
);
696 original_block
.Emit (ec
);
697 SymbolWriter
.EndIteratorBody (ec
.ig
);
699 SymbolWriter
.StartIteratorDispatcher (ec
.ig
);
701 ig
.Emit (OpCodes
.Ldarg_0
);
702 IntConstant
.EmitInt (ig
, (int) State
.After
);
703 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
705 ig
.MarkLabel (move_next_error
);
706 ig
.Emit (OpCodes
.Ldc_I4_0
);
707 ig
.Emit (OpCodes
.Ret
);
709 ig
.MarkLabel (move_next_ok
);
710 ig
.Emit (OpCodes
.Ldc_I4_1
);
711 ig
.Emit (OpCodes
.Ret
);
713 SymbolWriter
.EndIteratorDispatcher (ec
.ig
);
716 public void EmitDispose (EmitContext ec
)
718 ILGenerator ig
= ec
.ig
;
720 Label end
= ig
.DefineLabel ();
722 Label
[] labels
= null;
723 int n_resume_points
= resume_points
== null ? 0 : resume_points
.Count
;
724 for (int i
= 0; i
< n_resume_points
; ++i
) {
725 ResumableStatement s
= (ResumableStatement
) resume_points
[i
];
726 Label ret
= s
.PrepareForDispose (ec
, end
);
727 if (ret
.Equals (end
) && labels
== null)
729 if (labels
== null) {
730 labels
= new Label
[resume_points
.Count
+ 1];
731 for (int j
= 0; j
<= i
; ++j
)
737 if (labels
!= null) {
738 current_pc
= ec
.GetTemporaryLocal (TypeManager
.uint32_type
);
739 ig
.Emit (OpCodes
.Ldarg_0
);
740 ig
.Emit (OpCodes
.Ldfld
, IteratorHost
.PC
.FieldBuilder
);
741 ig
.Emit (OpCodes
.Stloc
, current_pc
);
744 ig
.Emit (OpCodes
.Ldarg_0
);
745 IntConstant
.EmitInt (ig
, (int) State
.After
);
746 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
748 if (labels
!= null) {
749 //SymbolWriter.StartIteratorDispatcher (ec.ig);
750 ig
.Emit (OpCodes
.Ldloc
, current_pc
);
751 ig
.Emit (OpCodes
.Switch
, labels
);
752 //SymbolWriter.EndIteratorDispatcher (ec.ig);
754 foreach (ResumableStatement s
in resume_points
)
755 s
.EmitForDispose (ec
, this, end
, true);
762 ArrayList resume_points
;
763 public int AddResumePoint (ResumableStatement stmt
)
765 if (resume_points
== null)
766 resume_points
= new ArrayList ();
767 resume_points
.Add (stmt
);
768 return resume_points
.Count
;
772 // Called back from Yield
774 public void MarkYield (EmitContext ec
, Expression expr
, int resume_pc
, bool unwind_protect
, Label resume_point
)
776 ILGenerator ig
= ec
.ig
;
778 // Store the new current
779 ig
.Emit (OpCodes
.Ldarg_0
);
781 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.CurrentField
.FieldBuilder
);
783 // store resume program-counter
784 ig
.Emit (OpCodes
.Ldarg_0
);
785 IntConstant
.EmitInt (ig
, resume_pc
);
786 ig
.Emit (OpCodes
.Stfld
, IteratorHost
.PC
.FieldBuilder
);
788 // mark finally blocks as disabled
789 if (unwind_protect
&& skip_finally
!= null) {
790 ig
.Emit (OpCodes
.Ldc_I4_1
);
791 ig
.Emit (OpCodes
.Stloc
, skip_finally
);
795 ig
.Emit (unwind_protect
? OpCodes
.Leave
: OpCodes
.Br
, move_next_ok
);
797 ig
.MarkLabel (resume_point
);
800 public override string ContainerType
{
801 get { return "iterator"; }
804 public override bool IsIterator
{
808 public override AnonymousMethodStorey Storey
{
809 get { return IteratorHost; }
815 private Iterator (IMethodData method
, TypeContainer host
, Type iterator_type
, bool is_enumerable
)
817 new ToplevelBlock (method
.Block
, ParametersCompiled
.EmptyReadOnlyParameters
, method
.Block
.StartLocation
),
818 TypeManager
.bool_type
,
821 this.OriginalMethod
= method
;
822 this.OriginalIteratorType
= iterator_type
;
823 this.IsEnumerable
= is_enumerable
;
826 IteratorHost
= Block
.ChangeToIterator (this, method
.Block
);
829 public override string GetSignatureForError ()
831 return OriginalMethod
.GetSignatureForError ();
834 public override Expression
DoResolve (EmitContext ec
)
836 method
= new AnonymousMethodMethod (Storey
,
837 this, Storey
, null, TypeManager
.system_boolean_expr
,
838 Modifiers
.PUBLIC
, OriginalMethod
.GetSignatureForError (),
839 new MemberName ("MoveNext", Location
),
840 ParametersCompiled
.EmptyReadOnlyParameters
);
842 if (!Compatible (ec
))
845 IteratorHost
.DefineIteratorMembers ();
847 eclass
= ExprClass
.Value
;
848 type
= ec
.ReturnType
;
852 public override void Emit (EmitContext ec
)
855 // Load Iterator storey instance
857 method
.Storey
.Instance
.Emit (ec
);
860 // Initialize iterator PC when it's unitialized
863 ILGenerator ig
= ec
.ig
;
864 ig
.Emit (OpCodes
.Dup
);
865 IntConstant
.EmitInt (ig
, (int)State
.Uninitialized
);
867 FieldInfo field
= IteratorHost
.PC
.FieldBuilder
;
869 if (Storey
.MemberName
.IsGeneric
)
870 field
= TypeBuilder
.GetField (Storey
.Instance
.Type
, field
);
872 ig
.Emit (OpCodes
.Stfld
, field
);
876 public override Expression
CreateExpressionTree (EmitContext ec
)
878 throw new NotSupportedException ("ET");
881 public static void CreateIterator (IMethodData method
, TypeContainer parent
, int modifiers
)
886 Type ret
= method
.ReturnType
;
890 if (!CheckType (ret
, out iterator_type
, out is_enumerable
)) {
891 Report
.Error (1624, method
.Location
,
892 "The body of `{0}' cannot be an iterator block " +
893 "because `{1}' is not an iterator interface type",
894 method
.GetSignatureForError (),
895 TypeManager
.CSharpName (ret
));
899 ParametersCompiled parameters
= method
.ParameterInfo
;
900 for (int i
= 0; i
< parameters
.Count
; i
++) {
901 Parameter p
= parameters
[i
];
902 Parameter
.Modifier mod
= p
.ModFlags
;
903 if ((mod
& Parameter
.Modifier
.ISBYREF
) != 0) {
904 Report
.Error (1623, p
.Location
,
905 "Iterators cannot have ref or out parameters");
909 if (p
is ArglistParameter
) {
910 Report
.Error (1636, method
.Location
,
911 "__arglist is not allowed in parameter list of iterators");
915 if (parameters
.Types
[i
].IsPointer
) {
916 Report
.Error (1637, p
.Location
,
917 "Iterators cannot have unsafe parameters or " +
923 if ((modifiers
& Modifiers
.UNSAFE
) != 0) {
924 Report
.Error (1629, method
.Location
, "Unsafe code may not appear in iterators");
928 Iterator iter
= new Iterator (method
, parent
, iterator_type
, is_enumerable
);
929 iter
.Storey
.DefineType ();
932 static bool CheckType (Type ret
, out Type original_iterator_type
, out bool is_enumerable
)
934 original_iterator_type
= null;
935 is_enumerable
= false;
937 if (ret
== TypeManager
.ienumerable_type
) {
938 original_iterator_type
= TypeManager
.object_type
;
939 is_enumerable
= true;
942 if (ret
== TypeManager
.ienumerator_type
) {
943 original_iterator_type
= TypeManager
.object_type
;
944 is_enumerable
= false;
948 if (!TypeManager
.IsGenericType (ret
))
951 Type
[] args
= TypeManager
.GetTypeArguments (ret
);
952 if (args
.Length
!= 1)
955 Type gt
= TypeManager
.DropGenericTypeArguments (ret
);
956 if (gt
== TypeManager
.generic_ienumerable_type
) {
957 original_iterator_type
= TypeManager
.TypeToCoreType (args
[0]);
958 is_enumerable
= true;
962 if (gt
== TypeManager
.generic_ienumerator_type
) {
963 original_iterator_type
= TypeManager
.TypeToCoreType (args
[0]);
964 is_enumerable
= false;