**** Merged from MCS ****
[mono-project.git] / mcs / mcs / iterators.cs
blob06c92a90c71dfc6f2542ae1a94dad3c7d2dc20ed
1 //
2 // iterators.cs: Support for implementing iterators
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2003 Ximian, Inc.
8 //
9 // TODO:
10 // Flow analysis for Yield.
11 // Emit calls to parent object constructor.
13 // Generics note:
14 // Current should be defined to return T, and IEnumerator.Current returns object
17 using System;
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
29 void SetYields ();
32 public class Yield : Statement {
33 public Expression expr;
34 ArrayList finally_blocks;
36 public Yield (Expression expr, Location l)
38 this.expr = expr;
39 loc = 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 " +
46 "finally clause");
47 return false;
49 if (ec.CurrentBranching.InCatch ()){
50 Report.Error (1631, loc, "Cannot yield in the body of a " +
51 "catch clause");
52 return false;
54 if (ec.CurrentAnonymousMethod != null){
55 Report.Error (1621, loc, "yield statement can not appear inside an anonymoud method");
56 return false;
60 // FIXME: Missing check for Yield inside try block that contains catch clauses
62 return true;
65 public override bool Resolve (EmitContext ec)
67 expr = expr.Resolve (ec);
68 if (expr == null)
69 return false;
70 if (!CheckContext (ec, loc))
71 return false;
73 Iterator iterator = ec.CurrentIterator;
74 if (expr.Type != iterator.IteratorType){
75 expr = Convert.ImplicitConversionRequired (
76 ec, expr, iterator.IteratorType, loc);
77 if (expr == null)
78 return false;
81 ec.CurrentBranching.StealFinallyClauses (ref finally_blocks);
82 return true;
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)
95 loc = l;
98 public override bool Resolve (EmitContext ec)
100 if (!Yield.CheckContext (ec, loc))
101 return false;
103 ec.CurrentBranching.CurrentUsageVector.Goto ();
104 return true;
107 protected override void DoEmit (EmitContext ec)
109 ec.CurrentIterator.EmitYieldBreak (ec.ig);
113 public class Iterator : Class {
114 ToplevelBlock original_block;
115 ToplevelBlock block;
116 string original_name;
118 Type iterator_type;
119 TypeExpr iterator_type_expr;
120 bool is_enumerable;
121 bool is_static;
123 Hashtable fields;
126 // The state as we generate the iterator
128 Label move_next_ok, move_next_error;
129 ArrayList resume_points = new ArrayList ();
130 int pc;
133 // Context from the original method
135 TypeContainer container;
136 Type return_type;
137 Type [] param_types;
138 InternalParameters parameters;
140 protected enum State {
141 Uninitialized = -2,
142 After,
143 Running
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);
175 EmitYieldBreak (ig);
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 ();
206 ig.MarkLabel (end);
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) {
225 labels [i] = end;
226 continue;
229 labels [i] = ig.DefineLabel ();
230 ig.MarkLabel (labels [i]);
232 ig.BeginExceptionBlock ();
233 ig.BeginFinallyBlock ();
235 foreach (ExceptionStatement stmt in point.FinallyBlocks) {
236 if (stmt != null)
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);
254 ig.MarkLabel (end);
257 protected class ResumePoint
259 public Label Label;
260 public readonly ExceptionStatement[] FinallyBlocks;
262 public ResumePoint (ArrayList list)
264 if (list != null) {
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
283 // s_ for Storage
285 public FieldBuilder MapVariable (string pfx, string name, Type t)
287 string full_name = pfx + name;
288 FieldBuilder fb = (FieldBuilder) fields [full_name];
289 if (fb != null)
290 return fb;
292 fb = TypeBuilder.DefineField (full_name, t, FieldAttributes.Private);
293 fields.Add (full_name, fb);
294 return 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);
307 expr.Emit (ec);
308 ig.Emit (OpCodes.Stfld, current_field.FieldBuilder);
310 // increment pc
311 pc++;
312 ig.Emit (OpCodes.Ldarg_0);
313 IntConstant.EmitInt (ig, pc);
314 ig.Emit (OpCodes.Stfld, pc_field.FieldBuilder);
316 // Return ok
317 ig.Emit (OpCodes.Br, move_next_ok);
319 ResumePoint point = new ResumePoint (finally_blocks);
320 resume_points.Add (point);
321 point.Define (ig);
324 public void MarkFinally (EmitContext ec, ArrayList finally_blocks)
326 ILGenerator ig = ec.ig;
328 // increment pc
329 pc++;
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);
336 point.Define (ig);
339 private static MemberName MakeProxyName (string name)
341 int pos = name.LastIndexOf ('.');
342 if (pos > 0)
343 name = name.Substring (pos + 1);
345 return new MemberName ("<" + name + ">__" + (proxy_count++));
349 // Our constructor
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)) {
373 Report.Error (
374 1624, Location,
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));
378 return false;
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){
384 Report.Error (
385 1623, Location,
386 "Iterators cannot have ref or out parameters");
387 return false;
391 ArrayList list = new ArrayList ();
392 if (is_enumerable)
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);
402 Bases = list;
403 return true;
407 // Returns the new block for the method, or null on failure
409 protected override bool DefineNestedTypes ()
411 Define_Fields ();
412 Define_Constructor ();
413 Define_Current ();
414 Define_MoveNext ();
415 Define_Reset ();
416 Define_Dispose ();
418 if (is_enumerable)
419 Define_GetEnumerator ();
421 Create_Block ();
423 return base.DefineNestedTypes ();
427 Field pc_field;
428 Field current_field;
429 Method dispose;
431 public Field this_field;
432 public Field[] parameter_fields;
434 void Create_Block ()
436 int first = is_static ? 0 : 1;
438 ArrayList args = new ArrayList ();
439 if (!is_static) {
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",
465 null, null, loc);
466 AddField (pc_field);
468 current_field = new Field (
469 this, iterator_type_expr, Modifiers.PRIVATE, "current",
470 null, null, loc);
471 AddField (current_field);
473 if (!is_static) {
474 this_field = new Field (
475 this,
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 (
487 this,
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 ();
500 if (!is_static)
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,
517 Location);
519 Constructor ctor = new Constructor (
520 this, Name, Modifiers.PUBLIC, ctor_params,
521 new ConstructorBaseInitializer (
522 null, Parameters.EmptyReadOnlyParameters, Location),
523 Location);
524 AddConstructor (ctor);
526 ToplevelBlock block = ctor.Block = new ToplevelBlock (Location);
528 if (!is_static) {
529 Type t = container.TypeBuilder;
531 Assign assign = new Assign (
532 new FieldExpression (this_field),
533 new SimpleParameterReference (t, 1, Location),
534 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),
547 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),
559 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 (
585 new Binary (
586 Binary.Operator.LessThanOrEqual,
587 new FieldExpression (pc_field),
588 new IntLiteral ((int) State.Running), Location),
589 Create_ThrowInvalidOperation (),
590 new Return (
591 new FieldExpression (current_field), Location),
592 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,
608 Location.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 (
623 this,
624 new TypeExpression (TypeManager.ienumerator_type, Location),
625 0, false, name,
626 Parameters.EmptyReadOnlyParameters, null,
627 Location.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 (
646 new Binary (
647 Binary.Operator.Equality,
648 new Invocation (ce, args, Location),
649 uninitialized, Location),
650 new Return (new This (block, Location), Location),
651 Location));
653 args = new ArrayList ();
654 if (!is_static)
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
670 int idx;
672 public SimpleParameterReference (Type type, int idx, Location loc)
674 this.idx = idx;
675 this.loc = loc;
676 this.type = type;
677 eclass = ExprClass.Variable;
680 public override Expression DoResolve (EmitContext ec)
682 return this;
685 public override void Emit (EmitContext ec)
687 DoEmit (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)
704 base.DoEmit (ec);
705 if (ec.TypeContainer is Struct)
706 ec.ig.Emit (OpCodes.Ldobj, type);
710 protected class FieldExpression : Expression
712 Field field;
714 public FieldExpression (Field field)
716 this.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 {
733 Iterator iterator;
735 public MoveNextMethod (Iterator iterator, Location loc)
737 this.loc = loc;
738 this.iterator = iterator;
741 public override bool Resolve (EmitContext ec)
743 ec.CurrentBranching.CurrentUsageVector.Return ();
744 return true;
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 {
764 Iterator iterator;
766 public DisposeMethod (Iterator iterator, Location loc)
768 this.loc = loc;
769 this.iterator = iterator;
772 public override bool Resolve (EmitContext ec)
774 return true;
777 protected override void DoEmit (EmitContext ec)
779 iterator.EmitDispose (ec);
783 protected class StatementList : Statement {
784 ArrayList statements;
786 public StatementList (Location loc)
788 this.loc = 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))
801 return false;
804 return true;
807 protected override void DoEmit (EmitContext ec)
809 foreach (Statement stmt in statements)
810 stmt.Emit (ec);
814 protected class SetState : Statement
816 Iterator iterator;
817 State state;
819 public SetState (Iterator iterator, State state, Location loc)
821 this.iterator = iterator;
822 this.state = state;
823 this.loc = loc;
826 public override bool Resolve (EmitContext ec)
828 return true;
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);
839 void Define_Reset ()
841 Method reset = new Method (
842 this, TypeManager.system_void_expr, Modifiers.PUBLIC,
843 false, new MemberName ("Reset"),
844 Parameters.EmptyReadOnlyParameters, null, Location);
845 AddMethod (reset);
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);
857 AddMethod (dispose);
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;
886 return ret_val;
890 bool CheckType (Type t)
892 if (t == TypeManager.ienumerable_type) {
893 iterator_type = TypeManager.object_type;
894 is_enumerable = true;
895 return true;
896 } else if (t == TypeManager.ienumerator_type) {
897 iterator_type = TypeManager.object_type;
898 is_enumerable = false;
899 return true;
902 return false;