2010-05-19 Jb Evain <jbevain@novell.com>
[mcs.git] / mcs / iterators.cs
blobcbf983c856f35415037a809edd3143a6dcc8a7ee
1 //
2 // iterators.cs: Support for implementing iterators
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003 Ximian, Inc.
10 // Copyright 2003-2008 Novell, Inc.
13 // TODO:
14 // Flow analysis for Yield.
17 using System;
18 using System.Collections.Generic;
19 using System.Reflection;
20 using System.Reflection.Emit;
22 namespace Mono.CSharp {
24 public class Yield : ResumableStatement {
25 Expression expr;
26 bool unwind_protect;
27 Iterator iterator;
28 int resume_pc;
30 public Yield (Expression expr, Location l)
32 this.expr = expr;
33 loc = 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");
46 return false;
49 return true;
52 public override bool Resolve (BlockContext ec)
54 expr = expr.Resolve (ec);
55 if (expr == null)
56 return false;
58 Report.Debug (64, "RESOLVE YIELD #1", this, ec, expr, expr.GetType (),
59 ec.CurrentAnonymousMethod, ec.CurrentIterator);
61 if (!CheckContext (ec, loc))
62 return false;
64 iterator = ec.CurrentIterator;
65 if (expr.Type != iterator.OriginalIteratorType) {
66 expr = Convert.ImplicitConversionRequired (
67 ec, expr, iterator.OriginalIteratorType, loc);
68 if (expr == null)
69 return false;
72 if (!ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
73 unwind_protect = ec.CurrentBranching.AddResumePoint (this, loc, out resume_pc);
75 return true;
78 protected override void DoEmit (EmitContext ec)
80 iterator.MarkYield (ec, expr, resume_pc, unwind_protect, resume_point);
83 protected override void CloneTo (CloneContext clonectx, Statement t)
85 Yield target = (Yield) t;
87 target.expr = expr.Clone (clonectx);
91 public class YieldBreak : ExitStatement
93 Iterator iterator;
95 public YieldBreak (Location l)
97 loc = l;
100 public override void Error_FinallyClause (Report Report)
102 Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
105 protected override void CloneTo (CloneContext clonectx, Statement target)
107 throw new NotSupportedException ();
110 protected override bool DoResolve (BlockContext ec)
112 iterator = ec.CurrentIterator;
113 return Yield.CheckContext (ec, loc);
116 protected override void DoEmit (EmitContext ec)
118 iterator.EmitYieldBreak (ec, unwind_protect);
123 // Wraps method block into iterator wrapper block
125 class IteratorStatement : Statement
127 Iterator iterator;
128 Block original_block;
130 public IteratorStatement (Iterator iterator, Block original_block)
132 this.iterator = iterator;
133 this.original_block = original_block;
134 this.loc = iterator.Location;
137 protected override void CloneTo (CloneContext clonectx, Statement target)
139 IteratorStatement t = (IteratorStatement) target;
140 t.original_block = (ExplicitBlock) original_block.Clone (clonectx);
141 t.iterator = (Iterator) iterator.Clone (clonectx);
144 public override bool Resolve (BlockContext ec)
146 ec.StartFlowBranching (iterator);
147 bool ok = original_block.Resolve (ec);
148 ec.EndFlowBranching ();
149 return ok;
152 protected override void DoEmit (EmitContext ec)
154 iterator.EmitMoveNext (ec, original_block);
158 public class IteratorStorey : AnonymousMethodStorey
160 class IteratorMethod : Method
162 readonly IteratorStorey host;
164 public IteratorMethod (IteratorStorey host, FullNamedExpression returnType, Modifiers mod, MemberName name)
165 : base (host, null, returnType, mod | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
166 name, ParametersCompiled.EmptyReadOnlyParameters, null)
168 this.host = host;
170 Block = new ToplevelBlock (Compiler, host.Iterator.Container.Toplevel, ParametersCompiled.EmptyReadOnlyParameters, Location);
173 public override EmitContext CreateEmitContext (ILGenerator ig)
175 EmitContext ec = new EmitContext (this, ig, MemberType);
177 ec.CurrentAnonymousMethod = host.Iterator;
178 return ec;
182 class GetEnumeratorMethod : IteratorMethod
184 sealed class GetEnumeratorStatement : Statement
186 IteratorStorey host;
187 IteratorMethod host_method;
189 Expression new_storey;
191 public GetEnumeratorStatement (IteratorStorey host, IteratorMethod host_method)
193 this.host = host;
194 this.host_method = host_method;
195 loc = host_method.Location;
198 protected override void CloneTo (CloneContext clonectx, Statement target)
200 throw new NotSupportedException ();
203 public override bool Resolve (BlockContext ec)
205 TypeExpression storey_type_expr = new TypeExpression (host.Definition, loc);
206 List<Expression> init = null;
207 if (host.hoisted_this != null) {
208 init = new List<Expression> (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
209 HoistedThis ht = host.hoisted_this;
210 FieldExpr from = new FieldExpr (ht.Field, loc);
211 from.InstanceExpression = CompilerGeneratedThis.Instance;
212 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
215 if (host.hoisted_params != null) {
216 if (init == null)
217 init = new List<Expression> (host.HoistedParameters.Count);
219 for (int i = 0; i < host.hoisted_params.Count; ++i) {
220 HoistedParameter hp = (HoistedParameter) host.hoisted_params [i];
221 HoistedParameter hp_cp = (HoistedParameter) host.hoisted_params_copy [i];
223 FieldExpr from = new FieldExpr (hp_cp.Field, loc);
224 from.InstanceExpression = CompilerGeneratedThis.Instance;
226 init.Add (new ElementInitializer (hp.Field.Name, from, loc));
230 if (init != null) {
231 new_storey = new NewInitialize (storey_type_expr, null,
232 new CollectionOrObjectInitializers (init, loc), loc);
233 } else {
234 new_storey = new New (storey_type_expr, null, loc);
237 new_storey = new_storey.Resolve (ec);
238 if (new_storey != null)
239 new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
241 if (TypeManager.int_interlocked_compare_exchange == null) {
242 TypeSpec t = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Interlocked", MemberKind.Class, true);
243 if (t != null) {
244 var p = ParametersCompiled.CreateFullyResolved (
245 new[] {
246 new ParameterData (null, Parameter.Modifier.REF),
247 new ParameterData (null, Parameter.Modifier.NONE),
248 new ParameterData (null, Parameter.Modifier.NONE)
250 new[] {
251 TypeManager.int32_type, TypeManager.int32_type, TypeManager.int32_type
254 var f = new MemberFilter ("CompareExchange", 0, MemberKind.Method, p, TypeManager.int32_type);
255 TypeManager.int_interlocked_compare_exchange = TypeManager.GetPredefinedMethod (t, f, loc);
259 ec.CurrentBranching.CurrentUsageVector.Goto ();
260 return true;
263 protected override void DoEmit (EmitContext ec)
265 Label label_init = ec.DefineLabel ();
267 ec.Emit (OpCodes.Ldarg_0);
268 ec.Emit (OpCodes.Ldflda, host.PC.Spec);
269 ec.EmitInt ((int) Iterator.State.Start);
270 ec.EmitInt ((int) Iterator.State.Uninitialized);
271 ec.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange);
273 ec.EmitInt ((int) Iterator.State.Uninitialized);
274 ec.Emit (OpCodes.Bne_Un_S, label_init);
276 ec.Emit (OpCodes.Ldarg_0);
277 ec.Emit (OpCodes.Ret);
279 ec.MarkLabel (label_init);
281 new_storey.Emit (ec);
282 ec.Emit (OpCodes.Ret);
286 public GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
287 : base (host, returnType, 0, name)
289 Block.AddStatement (new GetEnumeratorStatement (host, this));
293 class DisposeMethod : IteratorMethod
295 sealed class DisposeMethodStatement : Statement
297 Iterator iterator;
299 public DisposeMethodStatement (Iterator iterator)
301 this.iterator = iterator;
302 this.loc = iterator.Location;
305 protected override void CloneTo (CloneContext clonectx, Statement target)
307 throw new NotSupportedException ();
310 public override bool Resolve (BlockContext ec)
312 return true;
315 protected override void DoEmit (EmitContext ec)
317 iterator.EmitDispose (ec);
321 public DisposeMethod (IteratorStorey host)
322 : base (host, TypeManager.system_void_expr, Modifiers.PUBLIC, new MemberName ("Dispose", host.Location))
324 host.AddMethod (this);
326 Block = new ToplevelBlock (Compiler, host.Iterator.Container, ParametersCompiled.EmptyReadOnlyParameters, Location);
327 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
332 // Uses Method as method info
334 class DynamicMethodGroupExpr : MethodGroupExpr
336 readonly Method method;
338 public DynamicMethodGroupExpr (Method method, Location loc)
339 : base (null, loc)
341 this.method = method;
342 eclass = ExprClass.Unresolved;
345 protected override Expression DoResolve (ResolveContext ec)
347 Methods = new List<MemberSpec> (1) { method.Spec };
348 type = method.Parent.Definition;
349 InstanceExpression = new CompilerGeneratedThis (type, Location);
350 return base.DoResolve (ec);
354 class DynamicFieldExpr : FieldExpr
356 readonly Field field;
358 public DynamicFieldExpr (Field field, Location loc)
359 : base (loc)
361 this.field = field;
364 protected override Expression DoResolve (ResolveContext ec)
366 spec = field.Spec;
367 type = spec.MemberType;
368 InstanceExpression = new CompilerGeneratedThis (type, Location);
369 return base.DoResolve (ec);
373 public readonly Iterator Iterator;
375 TypeExpr iterator_type_expr;
376 Field pc_field;
377 Field current_field;
379 TypeExpr enumerator_type;
380 TypeExpr enumerable_type;
381 TypeArguments generic_args;
382 TypeExpr generic_enumerator_type;
383 TypeExpr generic_enumerable_type;
385 List<HoistedParameter> hoisted_params_copy;
386 int local_name_idx;
388 public IteratorStorey (Iterator iterator)
389 : base (iterator.Container.Toplevel, iterator.Host,
390 iterator.OriginalMethod as MemberBase, iterator.GenericMethod, "Iterator")
392 this.Iterator = iterator;
395 public Field PC {
396 get { return pc_field; }
399 public Field CurrentField {
400 get { return current_field; }
403 public IList<HoistedParameter> HoistedParameters {
404 get { return hoisted_params; }
407 protected override TypeExpr [] ResolveBaseTypes (out TypeExpr base_class)
409 var mtype = Iterator.OriginalIteratorType;
410 if (Mutator != null)
411 mtype = Mutator.Mutate (mtype);
413 iterator_type_expr = new TypeExpression (mtype, Location);
414 generic_args = new TypeArguments (iterator_type_expr);
416 var list = new List<FullNamedExpression> ();
417 if (Iterator.IsEnumerable) {
418 enumerable_type = new TypeExpression (
419 TypeManager.ienumerable_type, Location);
420 list.Add (enumerable_type);
422 if (TypeManager.generic_ienumerable_type != null) {
423 generic_enumerable_type = new GenericTypeExpr (
424 TypeManager.generic_ienumerable_type,
425 generic_args, Location);
426 list.Add (generic_enumerable_type);
430 enumerator_type = new TypeExpression (
431 TypeManager.ienumerator_type, Location);
432 list.Add (enumerator_type);
434 list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
436 if (TypeManager.generic_ienumerator_type != null) {
437 generic_enumerator_type = new GenericTypeExpr (
438 TypeManager.generic_ienumerator_type,
439 generic_args, Location);
440 list.Add (generic_enumerator_type);
443 type_bases = list;
445 return base.ResolveBaseTypes (out base_class);
448 protected override string GetVariableMangledName (LocalInfo local_info)
450 return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
453 protected override bool DoDefineMembers ()
455 DefineIteratorMembers ();
456 return base.DoDefineMembers ();
459 void DefineIteratorMembers ()
461 pc_field = AddCompilerGeneratedField ("$PC", TypeManager.system_int32_expr);
462 current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
464 if (hoisted_params != null) {
466 // Iterators are independent, each GetEnumerator call has to
467 // create same enumerator therefore we have to keep original values
468 // around for re-initialization
470 // TODO: Do it for assigned/modified parameters only
472 hoisted_params_copy = new List<HoistedParameter> (hoisted_params.Count);
473 foreach (HoistedParameter hp in hoisted_params) {
474 hoisted_params_copy.Add (new HoistedParameter (hp, "<$>" + hp.Field.Name));
478 if (generic_enumerator_type != null)
479 Define_Current (true);
481 Define_Current (false);
482 new DisposeMethod (this);
483 Define_Reset ();
485 if (Iterator.IsEnumerable) {
486 MemberName name = new MemberName (QualifiedAliasMember.GlobalAlias, "System", null, Location);
487 name = new MemberName (name, "Collections", Location);
488 name = new MemberName (name, "IEnumerable", Location);
489 name = new MemberName (name, "GetEnumerator", Location);
491 if (generic_enumerator_type != null) {
492 Method get_enumerator = new IteratorMethod (this, enumerator_type, 0, name);
494 name = new MemberName (name.Left.Left, "Generic", Location);
495 name = new MemberName (name, "IEnumerable", generic_args, Location);
496 name = new MemberName (name, "GetEnumerator", Location);
497 Method gget_enumerator = new GetEnumeratorMethod (this, generic_enumerator_type, name);
500 // Just call generic GetEnumerator implementation
502 get_enumerator.Block.AddStatement (
503 new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), null), Location));
505 AddMethod (get_enumerator);
506 AddMethod (gget_enumerator);
507 } else {
508 AddMethod (new GetEnumeratorMethod (this, enumerator_type, name));
513 protected override void EmitHoistedParameters (EmitContext ec, IList<HoistedParameter> hoisted)
515 base.EmitHoistedParameters (ec, hoisted);
516 base.EmitHoistedParameters (ec, hoisted_params_copy);
519 void Define_Current (bool is_generic)
521 TypeExpr type;
523 MemberName name = new MemberName (QualifiedAliasMember.GlobalAlias, "System", null, Location);
524 name = new MemberName (name, "Collections", Location);
526 if (is_generic) {
527 name = new MemberName (name, "Generic", Location);
528 name = new MemberName (name, "IEnumerator", generic_args, Location);
529 type = iterator_type_expr;
530 } else {
531 name = new MemberName (name, "IEnumerator");
532 type = TypeManager.system_object_expr;
535 name = new MemberName (name, "Current", Location);
537 ToplevelBlock get_block = new ToplevelBlock (Compiler, Location);
538 get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location));
540 Accessor getter = new Accessor (get_block, 0, null, null, Location);
542 Property current = new Property (
543 this, type, Modifiers.DEBUGGER_HIDDEN, name, null, getter, null, false);
544 AddProperty (current);
547 void Define_Reset ()
549 Method reset = new Method (
550 this, null, TypeManager.system_void_expr,
551 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
552 new MemberName ("Reset", Location),
553 ParametersCompiled.EmptyReadOnlyParameters, null);
554 AddMethod (reset);
556 reset.Block = new ToplevelBlock (Compiler, Location);
558 TypeSpec ex_type = TypeManager.CoreLookupType (Compiler, "System", "NotSupportedException", MemberKind.Class, true);
559 if (ex_type == null)
560 return;
562 reset.Block.AddStatement (new Throw (new New (new TypeExpression (ex_type, Location), null, Location), Location));
567 // Iterators are implemented as hidden anonymous block
569 public class Iterator : AnonymousExpression {
570 public readonly IMethodData OriginalMethod;
571 AnonymousMethodMethod method;
572 public readonly TypeContainer Host;
573 public readonly bool IsEnumerable;
574 List<ResumableStatement> resume_points;
577 // The state as we generate the iterator
579 Label move_next_ok, move_next_error;
580 LocalBuilder skip_finally, current_pc;
582 public LocalBuilder SkipFinally {
583 get { return skip_finally; }
586 public LocalBuilder CurrentPC {
587 get { return current_pc; }
590 public Block Container {
591 get { return OriginalMethod.Block; }
594 public GenericMethod GenericMethod {
595 get { return OriginalMethod.GenericMethod; }
598 public readonly TypeSpec OriginalIteratorType;
600 readonly IteratorStorey IteratorHost;
602 public enum State {
603 Running = -3, // Used only in CurrentPC, never stored into $PC
604 Uninitialized = -2,
605 After = -1,
606 Start = 0
609 public void EmitYieldBreak (EmitContext ec, bool unwind_protect)
611 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
614 void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block)
616 ec.Emit (OpCodes.Ldarg_0);
617 ec.Emit (OpCodes.Ldfld, IteratorHost.PC.Spec);
619 ec.Emit (OpCodes.Ldarg_0);
620 ec.EmitInt ((int) State.After);
621 ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec);
623 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
624 ec.Emit (OpCodes.Brtrue, move_next_error);
626 SymbolWriter.StartIteratorBody (ec);
627 original_block.Emit (ec);
628 SymbolWriter.EndIteratorBody (ec);
630 ec.MarkLabel (move_next_error);
631 ec.Emit (OpCodes.Ldc_I4_0);
632 ec.Emit (OpCodes.Ret);
635 internal void EmitMoveNext (EmitContext ec, Block original_block)
637 move_next_ok = ec.DefineLabel ();
638 move_next_error = ec.DefineLabel ();
640 if (resume_points == null) {
641 EmitMoveNext_NoResumePoints (ec, original_block);
642 return;
645 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
646 ec.Emit (OpCodes.Ldarg_0);
647 ec.Emit (OpCodes.Ldfld, IteratorHost.PC.Spec);
648 ec.Emit (OpCodes.Stloc, current_pc);
650 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
651 ec.Emit (OpCodes.Ldarg_0);
652 ec.EmitInt ((int) State.After);
653 ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec);
655 Label [] labels = new Label [1 + resume_points.Count];
656 labels [0] = ec.DefineLabel ();
658 bool need_skip_finally = false;
659 for (int i = 0; i < resume_points.Count; ++i) {
660 ResumableStatement s = resume_points [i];
661 need_skip_finally |= s is ExceptionStatement;
662 labels [i+1] = s.PrepareForEmit (ec);
665 if (need_skip_finally) {
666 skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type);
667 ec.Emit (OpCodes.Ldc_I4_0);
668 ec.Emit (OpCodes.Stloc, skip_finally);
671 SymbolWriter.StartIteratorDispatcher (ec);
672 ec.Emit (OpCodes.Ldloc, current_pc);
673 ec.Emit (OpCodes.Switch, labels);
675 ec.Emit (OpCodes.Br, move_next_error);
676 SymbolWriter.EndIteratorDispatcher (ec);
678 ec.MarkLabel (labels [0]);
680 SymbolWriter.StartIteratorBody (ec);
681 original_block.Emit (ec);
682 SymbolWriter.EndIteratorBody (ec);
684 SymbolWriter.StartIteratorDispatcher (ec);
686 ec.Emit (OpCodes.Ldarg_0);
687 ec.EmitInt ((int) State.After);
688 ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec);
690 ec.MarkLabel (move_next_error);
691 ec.EmitInt (0);
692 ec.Emit (OpCodes.Ret);
694 ec.MarkLabel (move_next_ok);
695 ec.Emit (OpCodes.Ldc_I4_1);
696 ec.Emit (OpCodes.Ret);
698 SymbolWriter.EndIteratorDispatcher (ec);
701 public void EmitDispose (EmitContext ec)
703 Label end = ec.DefineLabel ();
705 Label [] labels = null;
706 int n_resume_points = resume_points == null ? 0 : resume_points.Count;
707 for (int i = 0; i < n_resume_points; ++i) {
708 ResumableStatement s = (ResumableStatement) resume_points [i];
709 Label ret = s.PrepareForDispose (ec, end);
710 if (ret.Equals (end) && labels == null)
711 continue;
712 if (labels == null) {
713 labels = new Label [resume_points.Count + 1];
714 for (int j = 0; j <= i; ++j)
715 labels [j] = end;
717 labels [i+1] = ret;
720 if (labels != null) {
721 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
722 ec.Emit (OpCodes.Ldarg_0);
723 ec.Emit (OpCodes.Ldfld, IteratorHost.PC.Spec);
724 ec.Emit (OpCodes.Stloc, current_pc);
727 ec.Emit (OpCodes.Ldarg_0);
728 ec.EmitInt ((int) State.After);
729 ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec);
731 if (labels != null) {
732 //SymbolWriter.StartIteratorDispatcher (ec.ig);
733 ec.Emit (OpCodes.Ldloc, current_pc);
734 ec.Emit (OpCodes.Switch, labels);
735 //SymbolWriter.EndIteratorDispatcher (ec.ig);
737 foreach (ResumableStatement s in resume_points)
738 s.EmitForDispose (ec, this, end, true);
741 ec.MarkLabel (end);
744 public int AddResumePoint (ResumableStatement stmt)
746 if (resume_points == null)
747 resume_points = new List<ResumableStatement> ();
749 resume_points.Add (stmt);
750 return resume_points.Count;
754 // Called back from Yield
756 public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
758 // Store the new current
759 ec.Emit (OpCodes.Ldarg_0);
760 expr.Emit (ec);
761 ec.Emit (OpCodes.Stfld, IteratorHost.CurrentField.Spec);
763 // store resume program-counter
764 ec.Emit (OpCodes.Ldarg_0);
765 ec.EmitInt (resume_pc);
766 ec.Emit (OpCodes.Stfld, IteratorHost.PC.Spec);
768 // mark finally blocks as disabled
769 if (unwind_protect && skip_finally != null) {
770 ec.EmitInt (1);
771 ec.Emit (OpCodes.Stloc, skip_finally);
774 // Return ok
775 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
777 ec.MarkLabel (resume_point);
780 public override string ContainerType {
781 get { return "iterator"; }
784 public override bool IsIterator {
785 get { return true; }
788 public override AnonymousMethodStorey Storey {
789 get { return IteratorHost; }
793 // Our constructor
795 private Iterator (CompilerContext ctx, IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
796 : base (
797 new ToplevelBlock (ctx, method.Block, ParametersCompiled.EmptyReadOnlyParameters, method.Block.StartLocation),
798 TypeManager.bool_type,
799 method.Location)
801 this.OriginalMethod = method;
802 this.OriginalIteratorType = iterator_type;
803 this.IsEnumerable = is_enumerable;
804 this.Host = host;
805 this.type = method.ReturnType;
807 IteratorHost = Block.ChangeToIterator (this, method.Block);
810 public override string GetSignatureForError ()
812 return OriginalMethod.GetSignatureForError ();
815 protected override Expression DoResolve (ResolveContext ec)
817 method = new AnonymousMethodMethod (Storey,
818 this, Storey, null, TypeManager.system_boolean_expr,
819 Modifiers.PUBLIC, OriginalMethod.GetSignatureForError (),
820 new MemberName ("MoveNext", Location),
821 ParametersCompiled.EmptyReadOnlyParameters);
823 if (Compatible (ec) == null)
824 return null;
826 eclass = ExprClass.Value;
827 return this;
830 public override void Emit (EmitContext ec)
833 // Load Iterator storey instance
835 method.Storey.Instance.Emit (ec);
838 // Initialize iterator PC when it's unitialized
840 if (IsEnumerable) {
841 ec.Emit (OpCodes.Dup);
842 ec.EmitInt ((int)State.Uninitialized);
844 var field = IteratorHost.PC.Spec;
845 if (Storey.MemberName.IsGeneric) {
846 field = MemberCache.GetMember (Storey.Instance.Type, field);
849 ec.Emit (OpCodes.Stfld, field);
853 public override Expression CreateExpressionTree (ResolveContext ec)
855 throw new NotSupportedException ("ET");
858 public static void CreateIterator (IMethodData method, TypeContainer parent, Modifiers modifiers, CompilerContext ctx)
860 bool is_enumerable;
861 TypeSpec iterator_type;
863 TypeSpec ret = method.ReturnType;
864 if (ret == null)
865 return;
867 if (!CheckType (ret, out iterator_type, out is_enumerable)) {
868 ctx.Report.Error (1624, method.Location,
869 "The body of `{0}' cannot be an iterator block " +
870 "because `{1}' is not an iterator interface type",
871 method.GetSignatureForError (),
872 TypeManager.CSharpName (ret));
873 return;
876 ParametersCompiled parameters = method.ParameterInfo;
877 for (int i = 0; i < parameters.Count; i++) {
878 Parameter p = parameters [i];
879 Parameter.Modifier mod = p.ModFlags;
880 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
881 ctx.Report.Error (1623, p.Location,
882 "Iterators cannot have ref or out parameters");
883 return;
886 if (p is ArglistParameter) {
887 ctx.Report.Error (1636, method.Location,
888 "__arglist is not allowed in parameter list of iterators");
889 return;
892 if (parameters.Types [i].IsPointer) {
893 ctx.Report.Error (1637, p.Location,
894 "Iterators cannot have unsafe parameters or " +
895 "yield types");
896 return;
900 if ((modifiers & Modifiers.UNSAFE) != 0) {
901 ctx.Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
902 return;
905 // TODO: Ugly leftover
906 new Iterator (ctx, method, parent, iterator_type, is_enumerable);
909 static bool CheckType (TypeSpec ret, out TypeSpec original_iterator_type, out bool is_enumerable)
911 original_iterator_type = null;
912 is_enumerable = false;
914 if (ret == TypeManager.ienumerable_type) {
915 original_iterator_type = TypeManager.object_type;
916 is_enumerable = true;
917 return true;
919 if (ret == TypeManager.ienumerator_type) {
920 original_iterator_type = TypeManager.object_type;
921 is_enumerable = false;
922 return true;
925 InflatedTypeSpec inflated = ret as InflatedTypeSpec;
926 if (inflated == null)
927 return false;
929 ret = inflated.GetDefinition ();
930 if (ret == TypeManager.generic_ienumerable_type) {
931 original_iterator_type = inflated.TypeArguments[0];
932 is_enumerable = true;
933 return true;
936 if (ret == TypeManager.generic_ienumerator_type) {
937 original_iterator_type = inflated.TypeArguments[0];
938 is_enumerable = false;
939 return true;
942 return false;