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.InAnonymousMethod){
55 Report.Error (1621, loc, "yield statement can not appear " +
56 "inside an anonymoud method");
61 // FIXME: Missing check for Yield inside try block that contains catch clauses
66 public override bool Resolve (EmitContext ec)
68 expr = expr.Resolve (ec);
71 if (!CheckContext (ec, loc))
74 Iterator iterator = ec.CurrentIterator;
75 if (expr.Type != iterator.IteratorType){
76 expr = Convert.ImplicitConversionRequired (
77 ec, expr, iterator.IteratorType, loc);
82 ec.CurrentBranching.StealFinallyClauses (ref finally_blocks);
86 protected override void DoEmit (EmitContext ec)
88 ec.CurrentIterator.MarkYield (ec, expr, finally_blocks);
92 public class YieldBreak : Statement {
94 public YieldBreak (Location l)
99 public override bool Resolve (EmitContext ec)
101 if (!Yield.CheckContext (ec, loc))
104 ec.CurrentBranching.CurrentUsageVector.Goto ();
108 protected override void DoEmit (EmitContext ec)
110 ec.CurrentIterator.EmitYieldBreak (ec.ig);
114 public class Iterator : Class {
115 string original_name;
116 Block original_block;
120 TypeExpr iterator_type_expr;
127 // The state as we generate the iterator
129 Label move_next_ok, move_next_error;
130 ArrayList resume_points = new ArrayList ();
134 // Context from the original method
136 TypeContainer container;
139 InternalParameters parameters;
141 Expression enumerator_type;
142 Expression enumerable_type;
143 Expression generic_enumerator_type;
144 Expression generic_enumerable_type;
145 TypeArguments generic_args;
147 protected enum State {
153 static int proxy_count;
155 public void EmitYieldBreak (ILGenerator ig)
157 ig.Emit (OpCodes.Ldarg_0);
158 IntConstant.EmitInt (ig, (int) State.After);
159 ig.Emit (OpCodes.Br, move_next_error);
162 public void EmitMoveNext (EmitContext ec)
164 ILGenerator ig = ec.ig;
166 move_next_ok = ig.DefineLabel ();
167 move_next_error = ig.DefineLabel ();
169 LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type);
171 ig.BeginExceptionBlock ();
173 Label dispatcher = ig.DefineLabel ();
174 ig.Emit (OpCodes.Br, dispatcher);
176 ResumePoint entry_point = new ResumePoint (null);
177 resume_points.Add (entry_point);
178 entry_point.Define (ig);
180 ec.EmitTopBlock (original_block, parameters, Location);
183 ig.MarkLabel (dispatcher);
185 Label [] labels = new Label [resume_points.Count];
186 for (int i = 0; i < labels.Length; i++)
187 labels [i] = ((ResumePoint) resume_points [i]).Label;
189 ig.Emit (OpCodes.Ldarg_0);
190 ig.Emit (OpCodes.Ldfld, pc_field.FieldBuilder);
191 ig.Emit (OpCodes.Switch, labels);
193 Label end = ig.DefineLabel ();
195 ig.MarkLabel (move_next_error);
196 ig.Emit (OpCodes.Ldc_I4_0);
197 ig.Emit (OpCodes.Stloc, retval);
198 ig.Emit (OpCodes.Leave, end);
200 ig.MarkLabel (move_next_ok);
201 ig.Emit (OpCodes.Ldc_I4_1);
202 ig.Emit (OpCodes.Stloc, retval);
203 ig.Emit (OpCodes.Leave, end);
205 ig.BeginFaultBlock ();
207 ig.Emit (OpCodes.Ldarg_0);
208 ig.Emit (OpCodes.Callvirt, dispose.MethodBuilder);
210 ig.EndExceptionBlock ();
213 ig.Emit (OpCodes.Ldloc, retval);
214 ig.Emit (OpCodes.Ret);
217 public void EmitDispose (EmitContext ec)
219 ILGenerator ig = ec.ig;
221 Label end = ig.DefineLabel ();
222 Label dispatcher = ig.DefineLabel ();
223 ig.Emit (OpCodes.Br, dispatcher);
225 Label [] labels = new Label [resume_points.Count];
226 for (int i = 0; i < labels.Length; i++) {
227 ResumePoint point = (ResumePoint) resume_points [i];
229 if (point.FinallyBlocks == null) {
234 labels [i] = ig.DefineLabel ();
235 ig.MarkLabel (labels [i]);
237 ig.BeginExceptionBlock ();
238 ig.BeginFinallyBlock ();
240 foreach (ExceptionStatement stmt in point.FinallyBlocks) {
242 stmt.EmitFinally (ec);
245 ig.EndExceptionBlock ();
246 ig.Emit (OpCodes.Br, end);
249 ig.MarkLabel (dispatcher);
250 ig.Emit (OpCodes.Ldarg_0);
251 ig.Emit (OpCodes.Ldfld, pc_field.FieldBuilder);
252 ig.Emit (OpCodes.Switch, labels);
254 ig.Emit (OpCodes.Ldarg_0);
255 IntConstant.EmitInt (ig, (int) State.After);
256 ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
261 protected class ResumePoint
264 public readonly ExceptionStatement[] FinallyBlocks;
266 public ResumePoint (ArrayList list)
269 FinallyBlocks = new ExceptionStatement [list.Count];
270 list.CopyTo (FinallyBlocks, 0);
274 public void Define (ILGenerator ig)
276 Label = ig.DefineLabel ();
277 ig.MarkLabel (Label);
282 // Invoked when a local variable declaration needs to be mapped to
283 // a field in our proxy class
285 // Prefixes registered:
286 // v_ for EmitContext.MapVariable
289 public FieldBuilder MapVariable (string pfx, string name, Type t)
291 string full_name = pfx + name;
292 FieldBuilder fb = (FieldBuilder) fields [full_name];
296 fb = TypeBuilder.DefineField (full_name, t, FieldAttributes.Private);
297 fields.Add (full_name, fb);
302 // Called back from Yield
304 public void MarkYield (EmitContext ec, Expression expr,
305 ArrayList finally_blocks)
307 ILGenerator ig = ec.ig;
309 // Store the new current
310 ig.Emit (OpCodes.Ldarg_0);
312 ig.Emit (OpCodes.Stfld, current_field.FieldBuilder);
316 ig.Emit (OpCodes.Ldarg_0);
317 IntConstant.EmitInt (ig, pc);
318 ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
321 ig.Emit (OpCodes.Br, move_next_ok);
323 ResumePoint point = new ResumePoint (finally_blocks);
324 resume_points.Add (point);
328 public void MarkFinally (EmitContext ec, ArrayList finally_blocks)
330 ILGenerator ig = ec.ig;
334 ig.Emit (OpCodes.Ldarg_0);
335 IntConstant.EmitInt (ig, pc);
336 ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
338 ResumePoint point = new ResumePoint (finally_blocks);
339 resume_points.Add (point);
343 private static MemberName MakeProxyName (string name)
345 int pos = name.LastIndexOf ('.');
347 name = name.Substring (pos + 1);
349 return new MemberName ("<" + name + ">__" + (proxy_count++));
355 public Iterator (TypeContainer container, string name, Type return_type,
356 Type [] param_types, InternalParameters parameters,
357 int modifiers, Block block, Location loc)
358 : base (container.NamespaceEntry, container, MakeProxyName (name),
359 Modifiers.PRIVATE, null, loc)
361 this.container = container;
362 this.return_type = return_type;
363 this.param_types = param_types;
364 this.parameters = parameters;
365 this.original_name = name;
366 this.original_block = block;
367 this.block = new Block (null);
369 fields = new Hashtable ();
371 is_static = (modifiers & Modifiers.STATIC) != 0;
374 public bool DefineIterator ()
376 if (!CheckType (return_type)) {
379 "The body of `{0}' cannot be an iterator block " +
380 "because '{1}' is not an iterator interface type",
381 original_name, TypeManager.CSharpName (return_type));
385 for (int i = 0; i < parameters.Count; i++){
386 Parameter.Modifier mod = parameters.ParameterModifier (i);
387 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
390 "Iterators cannot have ref or out parameters");
395 generic_args = new TypeArguments (Location);
396 generic_args.Add (new TypeExpression (iterator_type, Location));
398 ArrayList list = new ArrayList ();
400 enumerable_type = new TypeExpression (
401 TypeManager.ienumerable_type, Location);
402 list.Add (enumerable_type);
404 generic_enumerable_type = new ConstructedType (
405 TypeManager.generic_ienumerable_type,
406 generic_args, Location);
407 list.Add (generic_enumerable_type);
410 enumerator_type = new TypeExpression (
411 TypeManager.ienumerator_type, Location);
412 list.Add (enumerator_type);
414 list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
416 generic_enumerator_type = new ConstructedType (
417 TypeManager.generic_ienumerator_type,
418 generic_args, Location);
419 list.Add (generic_enumerator_type);
421 iterator_type_expr = new TypeExpression (iterator_type, Location);
423 container.AddIterator (this);
430 // Returns the new block for the method, or null on failure
432 protected override bool DefineNestedTypes ()
435 Define_Constructor ();
436 Define_Current (false);
437 Define_Current (true);
443 Define_GetEnumerator (false);
444 Define_GetEnumerator (true);
449 return base.DefineNestedTypes ();
457 public Field this_field;
458 public Field[] parameter_fields;
462 int first = is_static ? 0 : 1;
464 ArrayList args = new ArrayList ();
466 Type t = container.TypeBuilder;
467 args.Add (new Argument (
468 new ThisParameterReference (t, 0, Location)));
471 args.Add (new Argument (new BoolLiteral (false)));
473 for (int i = 0; i < parameters.Count; i++) {
474 Type t = parameters.ParameterType (i);
475 args.Add (new Argument (
476 new SimpleParameterReference (t, first + i, Location)));
479 Expression new_expr = new New (
480 new TypeExpression (TypeBuilder, Location), args, Location);
482 block.AddStatement (new NoCheckReturn (new_expr, Location));
485 void Define_Fields ()
487 Location loc = Location.Null;
489 pc_field = new Field (
490 this, TypeManager.system_int32_expr, Modifiers.PRIVATE, "PC",
494 current_field = new Field (
495 this, iterator_type_expr, Modifiers.PRIVATE, "current",
497 AddField (current_field);
500 this_field = new Field (
502 new TypeExpression (container.TypeBuilder, Location),
503 Modifiers.PRIVATE, "this", null, null, loc);
504 AddField (this_field);
507 parameter_fields = new Field [parameters.Count];
508 for (int i = 0; i < parameters.Count; i++) {
509 string fname = String.Format (
510 "field{0}_{1}", i, parameters.ParameterName (i));
512 parameter_fields [i] = new Field (
514 new TypeExpression (parameters.ParameterType (i), loc),
515 Modifiers.PRIVATE, fname, null, null, loc);
516 AddField (parameter_fields [i]);
520 void Define_Constructor ()
522 Parameters ctor_params;
524 ArrayList list = new ArrayList ();
527 list.Add (new Parameter (
528 new TypeExpression (container.TypeBuilder, Location),
529 "this", Parameter.Modifier.NONE, null));
530 list.Add (new Parameter (
531 TypeManager.system_boolean_expr, "initialized",
532 Parameter.Modifier.NONE, null));
534 Parameter[] old_fixed = parameters.Parameters.FixedParameters;
535 if (old_fixed != null)
536 list.AddRange (old_fixed);
538 Parameter[] fixed_params = new Parameter [list.Count];
539 list.CopyTo (fixed_params);
541 ctor_params = new Parameters (
542 fixed_params, parameters.Parameters.ArrayParameter,
545 Constructor ctor = new Constructor (
546 this, Name, Modifiers.PUBLIC, ctor_params,
547 new ConstructorBaseInitializer (
548 null, Parameters.EmptyReadOnlyParameters, Location),
550 AddConstructor (ctor);
552 Block block = ctor.Block = new Block (null);
555 Type t = container.TypeBuilder;
557 Assign assign = new Assign (
558 new FieldExpression (this_field),
559 new SimpleParameterReference (t, 1, Location),
562 block.AddStatement (new StatementExpression (assign, Location));
565 int first = is_static ? 2 : 3;
567 for (int i = 0; i < parameters.Count; i++) {
568 Type t = parameters.ParameterType (i);
570 Assign assign = new Assign (
571 new FieldExpression (parameter_fields [i]),
572 new SimpleParameterReference (t, first + i, Location),
575 block.AddStatement (new StatementExpression (assign, Location));
578 State initial = is_enumerable ? State.Uninitialized : State.Running;
579 block.AddStatement (new SetState (this, initial, Location));
581 block.AddStatement (new If (
582 new SimpleParameterReference (
583 TypeManager.bool_type, first - 1, Location),
584 new SetState (this, State.Running, Location),
588 Statement Create_ThrowInvalidOperation ()
590 TypeExpr ex_type = new TypeExpression (
591 TypeManager.invalid_operation_exception_type, Location);
593 return new Throw (new New (ex_type, null, Location), Location);
596 Statement Create_ThrowNotSupported ()
598 TypeExpr ex_type = new TypeExpression (
599 TypeManager.not_supported_exception_type, Location);
601 return new Throw (new New (ex_type, null, Location), Location);
604 void Define_Current (bool is_generic)
609 left = new MemberName (
610 "System.Collections.Generic.IEnumerator",
612 type = iterator_type_expr;
614 left = new MemberName ("System.Collections.IEnumerator");
615 type = TypeManager.system_object_expr;
618 MemberName name = new MemberName (left, "Current", null);
620 Block get_block = new Block (null);
622 get_block.AddStatement (new If (
624 Binary.Operator.LessThanOrEqual,
625 new FieldExpression (pc_field),
626 new IntLiteral ((int) State.Running), Location),
627 Create_ThrowInvalidOperation (),
629 new FieldExpression (current_field), Location),
632 Accessor getter = new Accessor (get_block, null);
634 Property current = new Property (
635 this, type, 0, false, name, null, getter, null, Location);
636 AddProperty (current);
639 void Define_MoveNext ()
641 Method move_next = new Method (
642 this, null, TypeManager.system_boolean_expr,
643 Modifiers.PUBLIC, false, new MemberName ("MoveNext"),
644 Parameters.EmptyReadOnlyParameters, null,
646 AddMethod (move_next);
648 Block block = move_next.Block = new Block (null);
650 MoveNextMethod inline = new MoveNextMethod (this, Location);
651 block.AddStatement (inline);
654 void Define_GetEnumerator (bool is_generic)
659 left = new MemberName (
660 "System.Collections.Generic.IEnumerable",
662 type = generic_enumerator_type;
664 left = new MemberName ("System.Collections.IEnumerable");
665 type = enumerator_type;
668 MemberName name = new MemberName (left, "GetEnumerator", null);
670 Method get_enumerator = new Method (
671 this, null, type, 0, false, name,
672 Parameters.EmptyReadOnlyParameters, null,
674 AddMethod (get_enumerator);
676 get_enumerator.Block = new Block (null);
678 Expression ce = new MemberAccess (
679 new SimpleName ("System.Threading.Interlocked", Location),
680 "CompareExchange", Location);
682 Expression pc = new FieldExpression (pc_field);
683 Expression before = new IntLiteral ((int) State.Running);
684 Expression uninitialized = new IntLiteral ((int) State.Uninitialized);
686 ArrayList args = new ArrayList ();
687 args.Add (new Argument (pc, Argument.AType.Ref));
688 args.Add (new Argument (before, Argument.AType.Expression));
689 args.Add (new Argument (uninitialized, Argument.AType.Expression));
691 get_enumerator.Block.AddStatement (new If (
693 Binary.Operator.Equality,
694 new Invocation (ce, args, Location),
695 uninitialized, Location),
696 new Return (new This (block, Location), Location),
699 args = new ArrayList ();
701 args.Add (new Argument (new FieldExpression (this_field)));
703 args.Add (new Argument (new BoolLiteral (true)));
705 for (int i = 0; i < parameters.Count; i++)
706 args.Add (new Argument (
707 new FieldExpression (parameter_fields [i])));
709 Expression new_expr = new New (
710 new TypeExpression (TypeBuilder, Location), args, Location);
711 get_enumerator.Block.AddStatement (new Return (new_expr, Location));
714 protected class SimpleParameterReference : Expression
718 public SimpleParameterReference (Type type, int idx, Location loc)
723 eclass = ExprClass.Variable;
726 public override Expression DoResolve (EmitContext ec)
731 public override void Emit (EmitContext ec)
736 protected virtual void DoEmit (EmitContext ec)
738 ParameterReference.EmitLdArg (ec.ig, idx);
742 protected class ThisParameterReference : SimpleParameterReference
744 public ThisParameterReference (Type type, int idx, Location loc)
745 : base (type, idx, loc)
748 protected override void DoEmit (EmitContext ec)
751 if (ec.TypeContainer is Struct)
752 ec.ig.Emit (OpCodes.Ldobj, type);
756 protected class FieldExpression : Expression
760 public FieldExpression (Field field)
765 public override Expression DoResolve (EmitContext ec)
767 FieldExpr fexpr = new FieldExpr (field.FieldBuilder, loc);
768 fexpr.InstanceExpression = ec.GetThis (loc);
769 return fexpr.Resolve (ec);
772 public override void Emit (EmitContext ec)
774 throw new InvalidOperationException ();
778 protected class MoveNextMethod : Statement {
781 public MoveNextMethod (Iterator iterator, Location loc)
784 this.iterator = iterator;
787 public override bool Resolve (EmitContext ec)
789 ec.CurrentBranching.CurrentUsageVector.Return ();
793 protected override void DoEmit (EmitContext ec)
795 int code_flags = Modifiers.METHOD_YIELDS;
796 if (iterator.is_static)
797 code_flags |= Modifiers.STATIC;
799 EmitContext new_ec = new EmitContext (
800 iterator.container, loc, ec.ig,
801 TypeManager.int32_type, code_flags);
803 new_ec.CurrentIterator = iterator;
805 iterator.EmitMoveNext (new_ec);
809 protected class DisposeMethod : Statement {
812 public DisposeMethod (Iterator iterator, Location loc)
815 this.iterator = iterator;
818 public override bool Resolve (EmitContext ec)
823 protected override void DoEmit (EmitContext ec)
825 iterator.EmitDispose (ec);
829 protected class StatementList : Statement {
830 ArrayList statements;
832 public StatementList (Location loc)
835 statements = new ArrayList ();
838 public void Add (Statement statement)
840 statements.Add (statement);
843 public override bool Resolve (EmitContext ec)
845 foreach (Statement stmt in statements) {
846 if (!stmt.Resolve (ec))
853 protected override void DoEmit (EmitContext ec)
855 foreach (Statement stmt in statements)
860 protected class SetState : Statement
865 public SetState (Iterator iterator, State state, Location loc)
867 this.iterator = iterator;
872 public override bool Resolve (EmitContext ec)
877 protected override void DoEmit (EmitContext ec)
879 ec.ig.Emit (OpCodes.Ldarg_0);
880 IntConstant.EmitInt (ec.ig, (int) state);
881 ec.ig.Emit (OpCodes.Stfld, iterator.pc_field.FieldBuilder);
887 Method reset = new Method (
888 this, null, TypeManager.system_void_expr, Modifiers.PUBLIC,
889 false, new MemberName ("Reset"),
890 Parameters.EmptyReadOnlyParameters, null, Location);
893 reset.Block = new Block (null);
894 reset.Block.AddStatement (Create_ThrowNotSupported ());
897 void Define_Dispose ()
899 dispose = new Method (
900 this, null, TypeManager.system_void_expr, Modifiers.PUBLIC,
901 false, new MemberName ("Dispose"),
902 Parameters.EmptyReadOnlyParameters, null, Location);
905 dispose.Block = new Block (null);
906 dispose.Block.AddStatement (new DisposeMethod (this, Location));
910 get { return block; }
913 public Type IteratorType {
914 get { return iterator_type; }
918 // This return statement tricks return into not flagging an error for being
919 // used in a Yields method
921 class NoCheckReturn : Return {
922 public NoCheckReturn (Expression expr, Location loc) : base (expr, loc)
926 public override bool Resolve (EmitContext ec)
928 ec.InIterator = false;
929 bool ret_val = base.Resolve (ec);
930 ec.InIterator = true;
936 bool CheckType (Type t)
938 if (t == TypeManager.ienumerable_type) {
939 iterator_type = TypeManager.object_type;
940 is_enumerable = true;
942 } else if (t == TypeManager.ienumerator_type) {
943 iterator_type = TypeManager.object_type;
944 is_enumerable = false;
948 if (!t.IsGenericInstance)
951 Type[] args = TypeManager.GetTypeArguments (t);
952 if (args.Length != 1)
955 Type gt = t.GetGenericTypeDefinition ();
956 if (gt == TypeManager.generic_ienumerable_type) {
957 iterator_type = args [0];
958 is_enumerable = true;
960 } else if (gt == TypeManager.generic_ienumerator_type) {
961 iterator_type = args [0];
962 is_enumerable = false;