2 // iterators.cs: Support for implementing iterators
5 // Miguel de Icaza (miguel@ximian.com)
7 // (C) 2003 Ximian, Inc.
10 // Flow analysis for Yield.
11 // Emit calls to parent object constructor.
14 // Current should be defined to return T, and IEnumerator.Current returns object
18 using System
.Collections
;
19 using System
.Reflection
;
20 using System
.Reflection
.Emit
;
22 namespace Mono
.CSharp
{
24 public interface IIteratorContainer
{
27 // Invoked if a yield statement is found in the body
32 public class Yield
: Statement
{
33 public Expression expr
;
34 ArrayList finally_blocks
;
36 public Yield (Expression expr
, Location l
)
42 public static bool CheckContext (EmitContext ec
, Location loc
)
44 if (ec
.CurrentBranching
.InFinally (true)){
45 Report
.Error (1625, loc
, "Cannot yield in the body of a " +
49 if (ec
.CurrentBranching
.InCatch ()){
50 Report
.Error (1631, loc
, "Cannot yield in the body of a " +
54 if (ec
.CurrentAnonymousMethod
!= null){
55 Report
.Error (1621, loc
, "yield statement can not appear inside an anonymoud method");
60 // FIXME: Missing check for Yield inside try block that contains catch clauses
65 public override bool Resolve (EmitContext ec
)
67 expr
= expr
.Resolve (ec
);
70 if (!CheckContext (ec
, loc
))
73 Iterator iterator
= ec
.CurrentIterator
;
74 if (expr
.Type
!= iterator
.IteratorType
){
75 expr
= Convert
.ImplicitConversionRequired (
76 ec
, expr
, iterator
.IteratorType
, loc
);
81 ec
.CurrentBranching
.StealFinallyClauses (ref finally_blocks
);
85 protected override void DoEmit (EmitContext ec
)
87 ec
.CurrentIterator
.MarkYield (ec
, expr
, finally_blocks
);
91 public class YieldBreak
: Statement
{
93 public YieldBreak (Location l
)
98 public override bool Resolve (EmitContext ec
)
100 if (!Yield
.CheckContext (ec
, loc
))
103 ec
.CurrentBranching
.CurrentUsageVector
.Goto ();
107 protected override void DoEmit (EmitContext ec
)
109 ec
.CurrentIterator
.EmitYieldBreak (ec
.ig
);
113 public class Iterator
: Class
{
114 ToplevelBlock original_block
;
116 string original_name
;
119 TypeExpr iterator_type_expr
;
126 // The state as we generate the iterator
128 Label move_next_ok
, move_next_error
;
129 ArrayList resume_points
= new ArrayList ();
133 // Context from the original method
135 TypeContainer container
;
138 InternalParameters parameters
;
140 protected enum State
{
146 static int proxy_count
;
148 public void EmitYieldBreak (ILGenerator ig
)
150 ig
.Emit (OpCodes
.Ldarg_0
);
151 IntConstant
.EmitInt (ig
, (int) State
.After
);
152 ig
.Emit (OpCodes
.Stfld
, pc_field
.FieldBuilder
);
153 ig
.Emit (OpCodes
.Br
, move_next_error
);
156 public void EmitMoveNext (EmitContext ec
)
158 ILGenerator ig
= ec
.ig
;
160 move_next_ok
= ig
.DefineLabel ();
161 move_next_error
= ig
.DefineLabel ();
163 LocalBuilder retval
= ec
.GetTemporaryLocal (TypeManager
.int32_type
);
165 ig
.BeginExceptionBlock ();
167 Label dispatcher
= ig
.DefineLabel ();
168 ig
.Emit (OpCodes
.Br
, dispatcher
);
170 ResumePoint entry_point
= new ResumePoint (null);
171 resume_points
.Add (entry_point
);
172 entry_point
.Define (ig
);
174 ec
.EmitTopBlock (original_block
, parameters
, Location
);
177 ig
.MarkLabel (dispatcher
);
179 Label
[] labels
= new Label
[resume_points
.Count
];
180 for (int i
= 0; i
< labels
.Length
; i
++)
181 labels
[i
] = ((ResumePoint
) resume_points
[i
]).Label
;
183 ig
.Emit (OpCodes
.Ldarg_0
);
184 ig
.Emit (OpCodes
.Ldfld
, pc_field
.FieldBuilder
);
185 ig
.Emit (OpCodes
.Switch
, labels
);
187 Label end
= ig
.DefineLabel ();
189 ig
.MarkLabel (move_next_error
);
190 ig
.Emit (OpCodes
.Ldc_I4_0
);
191 ig
.Emit (OpCodes
.Stloc
, retval
);
192 ig
.Emit (OpCodes
.Leave
, end
);
194 ig
.MarkLabel (move_next_ok
);
195 ig
.Emit (OpCodes
.Ldc_I4_1
);
196 ig
.Emit (OpCodes
.Stloc
, retval
);
197 ig
.Emit (OpCodes
.Leave
, end
);
199 ig
.BeginFaultBlock ();
201 ig
.Emit (OpCodes
.Ldarg_0
);
202 ig
.Emit (OpCodes
.Callvirt
, dispose
.MethodBuilder
);
204 ig
.EndExceptionBlock ();
207 ig
.Emit (OpCodes
.Ldloc
, retval
);
208 ig
.Emit (OpCodes
.Ret
);
211 public void EmitDispose (EmitContext ec
)
213 ILGenerator ig
= ec
.ig
;
215 Label end
= ig
.DefineLabel ();
216 Label dispatcher
= ig
.DefineLabel ();
217 ig
.Emit (OpCodes
.Br
, dispatcher
);
219 ec
.RemapToProxy
= true;
220 Label
[] labels
= new Label
[resume_points
.Count
];
221 for (int i
= 0; i
< labels
.Length
; i
++) {
222 ResumePoint point
= (ResumePoint
) resume_points
[i
];
224 if (point
.FinallyBlocks
== null) {
229 labels
[i
] = ig
.DefineLabel ();
230 ig
.MarkLabel (labels
[i
]);
232 ig
.BeginExceptionBlock ();
233 ig
.BeginFinallyBlock ();
235 foreach (ExceptionStatement stmt
in point
.FinallyBlocks
) {
237 stmt
.EmitFinally (ec
);
240 ig
.EndExceptionBlock ();
241 ig
.Emit (OpCodes
.Br
, end
);
243 ec
.RemapToProxy
= false;
245 ig
.MarkLabel (dispatcher
);
246 ig
.Emit (OpCodes
.Ldarg_0
);
247 ig
.Emit (OpCodes
.Ldfld
, pc_field
.FieldBuilder
);
248 ig
.Emit (OpCodes
.Switch
, labels
);
250 ig
.Emit (OpCodes
.Ldarg_0
);
251 IntConstant
.EmitInt (ig
, (int) State
.After
);
252 ig
.Emit (OpCodes
.Stfld
, pc_field
.FieldBuilder
);
257 protected class ResumePoint
260 public readonly ExceptionStatement
[] FinallyBlocks
;
262 public ResumePoint (ArrayList list
)
265 FinallyBlocks
= new ExceptionStatement
[list
.Count
];
266 list
.CopyTo (FinallyBlocks
, 0);
270 public void Define (ILGenerator ig
)
272 Label
= ig
.DefineLabel ();
273 ig
.MarkLabel (Label
);
278 // Invoked when a local variable declaration needs to be mapped to
279 // a field in our proxy class
281 // Prefixes registered:
282 // v_ for EmitContext.MapVariable
285 public FieldBuilder
MapVariable (string pfx
, string name
, Type t
)
287 string full_name
= pfx
+ name
;
288 FieldBuilder fb
= (FieldBuilder
) fields
[full_name
];
292 fb
= TypeBuilder
.DefineField (full_name
, t
, FieldAttributes
.Private
);
293 fields
.Add (full_name
, fb
);
298 // Called back from Yield
300 public void MarkYield (EmitContext ec
, Expression expr
,
301 ArrayList finally_blocks
)
303 ILGenerator ig
= ec
.ig
;
305 // Store the new current
306 ig
.Emit (OpCodes
.Ldarg_0
);
308 ig
.Emit (OpCodes
.Stfld
, current_field
.FieldBuilder
);
312 ig
.Emit (OpCodes
.Ldarg_0
);
313 IntConstant
.EmitInt (ig
, pc
);
314 ig
.Emit (OpCodes
.Stfld
, pc_field
.FieldBuilder
);
317 ig
.Emit (OpCodes
.Br
, move_next_ok
);
319 ResumePoint point
= new ResumePoint (finally_blocks
);
320 resume_points
.Add (point
);
324 public void MarkFinally (EmitContext ec
, ArrayList finally_blocks
)
326 ILGenerator ig
= ec
.ig
;
330 ig
.Emit (OpCodes
.Ldarg_0
);
331 IntConstant
.EmitInt (ig
, pc
);
332 ig
.Emit (OpCodes
.Stfld
, pc_field
.FieldBuilder
);
334 ResumePoint point
= new ResumePoint (finally_blocks
);
335 resume_points
.Add (point
);
339 private static MemberName
MakeProxyName (string name
)
341 int pos
= name
.LastIndexOf ('.');
343 name
= name
.Substring (pos
+ 1);
345 return new MemberName ("<" + name
+ ">__" + (proxy_count
++));
351 public Iterator (TypeContainer container
, string name
, Type return_type
,
352 Type
[] param_types
, InternalParameters parameters
,
353 int modifiers
, ToplevelBlock block
, Location loc
)
354 : base (container
.NamespaceEntry
, container
, MakeProxyName (name
),
355 Modifiers
.PRIVATE
, null, loc
)
357 this.container
= container
;
358 this.return_type
= return_type
;
359 this.param_types
= param_types
;
360 this.parameters
= parameters
;
361 this.original_name
= name
;
362 this.original_block
= block
;
363 this.block
= new ToplevelBlock (loc
);
365 fields
= new Hashtable ();
367 is_static
= (modifiers
& Modifiers
.STATIC
) != 0;
370 public bool DefineIterator ()
372 if (!CheckType (return_type
)) {
375 "The body of `{0}' cannot be an iterator block " +
376 "because '{1}' is not an iterator interface type",
377 original_name
, TypeManager
.CSharpName (return_type
));
381 for (int i
= 0; i
< parameters
.Count
; i
++){
382 Parameter
.Modifier mod
= parameters
.ParameterModifier (i
);
383 if ((mod
& (Parameter
.Modifier
.REF
| Parameter
.Modifier
.OUT
)) != 0){
386 "Iterators cannot have ref or out parameters");
391 ArrayList list
= new ArrayList ();
393 list
.Add (new TypeExpression (
394 TypeManager
.ienumerable_type
, Location
));
395 list
.Add (new TypeExpression (TypeManager
.ienumerator_type
, Location
));
396 list
.Add (new TypeExpression (TypeManager
.idisposable_type
, Location
));
398 iterator_type_expr
= new TypeExpression (iterator_type
, Location
);
400 container
.AddIterator (this);
407 // Returns the new block for the method, or null on failure
409 protected override bool DefineNestedTypes ()
412 Define_Constructor ();
419 Define_GetEnumerator ();
423 return base.DefineNestedTypes ();
431 public Field this_field
;
432 public Field
[] parameter_fields
;
436 int first
= is_static
? 0 : 1;
438 ArrayList args
= new ArrayList ();
440 Type t
= container
.TypeBuilder
;
441 args
.Add (new Argument (
442 new ThisParameterReference (t
, 0, Location
)));
445 args
.Add (new Argument (new BoolLiteral (false)));
447 for (int i
= 0; i
< parameters
.Count
; i
++) {
448 Type t
= parameters
.ParameterType (i
);
449 args
.Add (new Argument (
450 new SimpleParameterReference (t
, first
+ i
, Location
)));
453 Expression new_expr
= new New (
454 new TypeExpression (TypeBuilder
, Location
), args
, Location
);
456 block
.AddStatement (new NoCheckReturn (new_expr
, Location
));
459 void Define_Fields ()
461 Location loc
= Location
.Null
;
463 pc_field
= new Field (
464 this, TypeManager
.system_int32_expr
, Modifiers
.PRIVATE
, "PC",
468 current_field
= new Field (
469 this, iterator_type_expr
, Modifiers
.PRIVATE
, "current",
471 AddField (current_field
);
474 this_field
= new Field (
476 new TypeExpression (container
.TypeBuilder
, Location
),
477 Modifiers
.PRIVATE
, "this", null, null, loc
);
478 AddField (this_field
);
481 parameter_fields
= new Field
[parameters
.Count
];
482 for (int i
= 0; i
< parameters
.Count
; i
++) {
483 string fname
= String
.Format (
484 "field{0}_{1}", i
, parameters
.ParameterName (i
));
486 parameter_fields
[i
] = new Field (
488 new TypeExpression (parameters
.ParameterType (i
), loc
),
489 Modifiers
.PRIVATE
, fname
, null, null, loc
);
490 AddField (parameter_fields
[i
]);
494 void Define_Constructor ()
496 Parameters ctor_params
;
498 ArrayList list
= new ArrayList ();
501 list
.Add (new Parameter (
502 new TypeExpression (container
.TypeBuilder
, Location
),
503 "this", Parameter
.Modifier
.NONE
, null));
504 list
.Add (new Parameter (
505 TypeManager
.system_boolean_expr
, "initialized",
506 Parameter
.Modifier
.NONE
, null));
508 Parameter
[] old_fixed
= parameters
.Parameters
.FixedParameters
;
509 if (old_fixed
!= null)
510 list
.AddRange (old_fixed
);
512 Parameter
[] fixed_params
= new Parameter
[list
.Count
];
513 list
.CopyTo (fixed_params
);
515 ctor_params
= new Parameters (
516 fixed_params
, parameters
.Parameters
.ArrayParameter
,
519 Constructor ctor
= new Constructor (
520 this, Name
, Modifiers
.PUBLIC
, ctor_params
,
521 new ConstructorBaseInitializer (
522 null, Parameters
.EmptyReadOnlyParameters
, Location
),
524 AddConstructor (ctor
);
526 ToplevelBlock block
= ctor
.Block
= new ToplevelBlock (Location
);
529 Type t
= container
.TypeBuilder
;
531 Assign assign
= new Assign (
532 new FieldExpression (this_field
),
533 new SimpleParameterReference (t
, 1, Location
),
536 block
.AddStatement (new StatementExpression (assign
, Location
));
539 int first
= is_static
? 2 : 3;
541 for (int i
= 0; i
< parameters
.Count
; i
++) {
542 Type t
= parameters
.ParameterType (i
);
544 Assign assign
= new Assign (
545 new FieldExpression (parameter_fields
[i
]),
546 new SimpleParameterReference (t
, first
+ i
, Location
),
549 block
.AddStatement (new StatementExpression (assign
, Location
));
552 State initial
= is_enumerable
? State
.Uninitialized
: State
.Running
;
553 block
.AddStatement (new SetState (this, initial
, Location
));
555 block
.AddStatement (new If (
556 new SimpleParameterReference (
557 TypeManager
.bool_type
, first
- 1, Location
),
558 new SetState (this, State
.Running
, Location
),
562 Statement
Create_ThrowInvalidOperation ()
564 TypeExpr ex_type
= new TypeExpression (
565 TypeManager
.invalid_operation_exception_type
, Location
);
567 return new Throw (new New (ex_type
, null, Location
), Location
);
570 Statement
Create_ThrowNotSupported ()
572 TypeExpr ex_type
= new TypeExpression (
573 TypeManager
.not_supported_exception_type
, Location
);
575 return new Throw (new New (ex_type
, null, Location
), Location
);
578 void Define_Current ()
580 ToplevelBlock get_block
= new ToplevelBlock (Location
);
581 MemberName left
= new MemberName ("System.Collections.IEnumerator");
582 MemberName name
= new MemberName (left
, "Current");
584 get_block
.AddStatement (new If (
586 Binary
.Operator
.LessThanOrEqual
,
587 new FieldExpression (pc_field
),
588 new IntLiteral ((int) State
.Running
), Location
),
589 Create_ThrowInvalidOperation (),
591 new FieldExpression (current_field
), Location
),
594 Accessor getter
= new Accessor (get_block
, 0, null, Location
);
596 Property current
= new Property (
597 this, iterator_type_expr
, 0,
598 false, name
, null, getter
, null, Location
);
599 AddProperty (current
);
602 void Define_MoveNext ()
604 Method move_next
= new Method (
605 this, TypeManager
.system_boolean_expr
,
606 Modifiers
.PUBLIC
, false, new MemberName ("MoveNext"),
607 Parameters
.EmptyReadOnlyParameters
, null,
609 AddMethod (move_next
);
611 ToplevelBlock block
= move_next
.Block
= new ToplevelBlock (Location
);
613 MoveNextMethod inline
= new MoveNextMethod (this, Location
);
614 block
.AddStatement (inline
);
617 void Define_GetEnumerator ()
619 MemberName left
= new MemberName ("System.Collections.IEnumerable");
620 MemberName name
= new MemberName (left
, "GetEnumerator");
622 Method get_enumerator
= new Method (
624 new TypeExpression (TypeManager
.ienumerator_type
, Location
),
626 Parameters
.EmptyReadOnlyParameters
, null,
628 AddMethod (get_enumerator
);
630 get_enumerator
.Block
= new ToplevelBlock (Location
);
632 Expression ce
= new MemberAccess (
633 new SimpleName ("System.Threading.Interlocked", Location
),
634 "CompareExchange", Location
);
636 Expression pc
= new FieldExpression (pc_field
);
637 Expression before
= new IntLiteral ((int) State
.Running
);
638 Expression uninitialized
= new IntLiteral ((int) State
.Uninitialized
);
640 ArrayList args
= new ArrayList ();
641 args
.Add (new Argument (pc
, Argument
.AType
.Ref
));
642 args
.Add (new Argument (before
, Argument
.AType
.Expression
));
643 args
.Add (new Argument (uninitialized
, Argument
.AType
.Expression
));
645 get_enumerator
.Block
.AddStatement (new If (
647 Binary
.Operator
.Equality
,
648 new Invocation (ce
, args
, Location
),
649 uninitialized
, Location
),
650 new Return (new This (block
, Location
), Location
),
653 args
= new ArrayList ();
655 args
.Add (new Argument (new FieldExpression (this_field
)));
657 args
.Add (new Argument (new BoolLiteral (true)));
659 for (int i
= 0; i
< parameters
.Count
; i
++)
660 args
.Add (new Argument (
661 new FieldExpression (parameter_fields
[i
])));
663 Expression new_expr
= new New (
664 new TypeExpression (TypeBuilder
, Location
), args
, Location
);
665 get_enumerator
.Block
.AddStatement (new Return (new_expr
, Location
));
668 protected class SimpleParameterReference
: Expression
672 public SimpleParameterReference (Type type
, int idx
, Location loc
)
677 eclass
= ExprClass
.Variable
;
680 public override Expression
DoResolve (EmitContext ec
)
685 public override void Emit (EmitContext ec
)
690 protected virtual void DoEmit (EmitContext ec
)
692 ParameterReference
.EmitLdArg (ec
.ig
, idx
);
696 protected class ThisParameterReference
: SimpleParameterReference
698 public ThisParameterReference (Type type
, int idx
, Location loc
)
699 : base (type
, idx
, loc
)
702 protected override void DoEmit (EmitContext ec
)
705 if (ec
.TypeContainer
is Struct
)
706 ec
.ig
.Emit (OpCodes
.Ldobj
, type
);
710 protected class FieldExpression
: Expression
714 public FieldExpression (Field field
)
719 public override Expression
DoResolve (EmitContext ec
)
721 FieldExpr fexpr
= new FieldExpr (field
.FieldBuilder
, loc
);
722 fexpr
.InstanceExpression
= ec
.GetThis (loc
);
723 return fexpr
.Resolve (ec
);
726 public override void Emit (EmitContext ec
)
728 throw new InvalidOperationException ();
732 protected class MoveNextMethod
: Statement
{
735 public MoveNextMethod (Iterator iterator
, Location loc
)
738 this.iterator
= iterator
;
741 public override bool Resolve (EmitContext ec
)
743 ec
.CurrentBranching
.CurrentUsageVector
.Return ();
747 protected override void DoEmit (EmitContext ec
)
749 int code_flags
= Modifiers
.METHOD_YIELDS
;
750 if (iterator
.is_static
)
751 code_flags
|= Modifiers
.STATIC
;
753 EmitContext new_ec
= new EmitContext (
754 iterator
.container
, loc
, ec
.ig
,
755 TypeManager
.int32_type
, code_flags
);
757 new_ec
.CurrentIterator
= iterator
;
759 iterator
.EmitMoveNext (new_ec
);
763 protected class DisposeMethod
: Statement
{
766 public DisposeMethod (Iterator iterator
, Location loc
)
769 this.iterator
= iterator
;
772 public override bool Resolve (EmitContext ec
)
777 protected override void DoEmit (EmitContext ec
)
779 iterator
.EmitDispose (ec
);
783 protected class StatementList
: Statement
{
784 ArrayList statements
;
786 public StatementList (Location loc
)
789 statements
= new ArrayList ();
792 public void Add (Statement statement
)
794 statements
.Add (statement
);
797 public override bool Resolve (EmitContext ec
)
799 foreach (Statement stmt
in statements
) {
800 if (!stmt
.Resolve (ec
))
807 protected override void DoEmit (EmitContext ec
)
809 foreach (Statement stmt
in statements
)
814 protected class SetState
: Statement
819 public SetState (Iterator iterator
, State state
, Location loc
)
821 this.iterator
= iterator
;
826 public override bool Resolve (EmitContext ec
)
831 protected override void DoEmit (EmitContext ec
)
833 ec
.ig
.Emit (OpCodes
.Ldarg_0
);
834 IntConstant
.EmitInt (ec
.ig
, (int) state
);
835 ec
.ig
.Emit (OpCodes
.Stfld
, iterator
.pc_field
.FieldBuilder
);
841 Method reset
= new Method (
842 this, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
,
843 false, new MemberName ("Reset"),
844 Parameters
.EmptyReadOnlyParameters
, null, Location
);
847 reset
.Block
= new ToplevelBlock (Location
);
848 reset
.Block
.AddStatement (Create_ThrowNotSupported ());
851 void Define_Dispose ()
853 dispose
= new Method (
854 this, TypeManager
.system_void_expr
, Modifiers
.PUBLIC
,
855 false, new MemberName ("Dispose"),
856 Parameters
.EmptyReadOnlyParameters
, null, Location
);
859 dispose
.Block
= new ToplevelBlock (Location
);
860 dispose
.Block
.AddStatement (new DisposeMethod (this, Location
));
863 public ToplevelBlock Block
{
864 get { return block; }
867 public Type IteratorType
{
868 get { return iterator_type; }
872 // This return statement tricks return into not flagging an error for being
873 // used in a Yields method
875 class NoCheckReturn
: Return
{
876 public NoCheckReturn (Expression expr
, Location loc
) : base (expr
, loc
)
880 public override bool Resolve (EmitContext ec
)
882 ec
.InIterator
= false;
883 bool ret_val
= base.Resolve (ec
);
884 ec
.InIterator
= true;
890 bool CheckType (Type t
)
892 if (t
== TypeManager
.ienumerable_type
) {
893 iterator_type
= TypeManager
.object_type
;
894 is_enumerable
= true;
896 } else if (t
== TypeManager
.ienumerator_type
) {
897 iterator_type
= TypeManager
.object_type
;
898 is_enumerable
= false;