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, bool isYieldBreak)
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, false))
77 Iterator iterator = ec.CurrentIterator;
78 if (expr.Type != iterator.OriginalIteratorType) {
79 expr = Convert.ImplicitConversionRequired (
80 ec, expr, iterator.OriginalIteratorType, loc);
85 unwind_protect = ec.CurrentBranching.AddResumePoint (this, loc, out resume_pc);
90 protected override void DoEmit (EmitContext ec)
92 ec.CurrentIterator.MarkYield (ec, expr, resume_pc, unwind_protect, resume_point);
95 protected override void CloneTo (CloneContext clonectx, Statement t)
97 Yield target = (Yield) t;
99 target.expr = expr.Clone (clonectx);
103 public class YieldBreak : ExitStatement {
104 public YieldBreak (Location l)
109 public override void Error_FinallyClause ()
111 Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
114 protected override bool DoResolve (EmitContext ec)
116 return Yield.CheckContext (ec, loc, true);
119 protected override void DoEmit (EmitContext ec)
121 ec.CurrentIterator.EmitYieldBreak (ec.ig, unwind_protect);
124 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
131 // Wraps method block into iterator wrapper block
133 class IteratorStatement : Statement
136 ExplicitBlock original_block;
138 public IteratorStatement (Iterator iterator, ExplicitBlock original_block)
140 this.iterator = iterator;
141 this.original_block = original_block;
142 this.loc = iterator.Location;
145 public override bool Resolve (EmitContext ec)
147 ec.StartFlowBranching (iterator);
148 bool ok = original_block.Resolve (ec);
149 ec.EndFlowBranching ();
153 protected override void DoEmit (EmitContext ec)
155 iterator.EmitMoveNext (ec, original_block);
158 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
160 original_block.MutateHoistedGenericType (storey);
161 iterator.MutateHoistedGenericType (storey);
165 public class IteratorStorey : AnonymousMethodStorey
167 public readonly Iterator Iterator;
169 TypeExpr iterator_type_expr;
173 TypeExpr enumerator_type;
174 TypeExpr enumerable_type;
175 TypeArguments generic_args;
176 TypeExpr generic_enumerator_type;
178 TypeExpr generic_enumerable_type;
183 public IteratorStorey (Iterator iterator)
184 : base (iterator.Container.Toplevel, iterator.Host,
185 iterator.OriginalMethod as MemberBase, iterator.GenericMethod, "Iterator")
187 this.Iterator = iterator;
188 HasHoistedVariables = true;
192 get { return pc_field; }
195 public Field CurrentField {
196 get { return current_field; }
199 public ArrayList HoistedParameters {
200 get { return hoisted_params; }
203 public override TypeExpr [] GetClassBases (out TypeExpr base_class)
205 iterator_type_expr = new TypeExpression (MutateType (Iterator.OriginalIteratorType), Location);
208 generic_args = new TypeArguments (Location);
209 generic_args.Add (iterator_type_expr);
212 ArrayList list = new ArrayList ();
213 if (Iterator.IsEnumerable) {
214 enumerable_type = new TypeExpression (
215 TypeManager.ienumerable_type, Location);
216 list.Add (enumerable_type);
219 generic_enumerable_type = new ConstructedType (
220 TypeManager.generic_ienumerable_type,
221 generic_args, Location);
222 list.Add (generic_enumerable_type);
226 enumerator_type = new TypeExpression (
227 TypeManager.ienumerator_type, Location);
228 list.Add (enumerator_type);
230 list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
233 generic_enumerator_type = new ConstructedType (
234 TypeManager.generic_ienumerator_type,
235 generic_args, Location);
236 list.Add (generic_enumerator_type);
241 return base.GetClassBases (out base_class);
244 protected override string GetVariableMangledName (LocalInfo local_info)
246 return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
249 public void DefineIteratorMembers ()
251 pc_field = AddCompilerGeneratedField ("$PC", TypeManager.system_int32_expr);
252 current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
255 Define_Current (true);
257 Define_Current (false);
258 new DisposeMethod (this);
261 if (Iterator.IsEnumerable) {
262 new GetEnumeratorMethod (this, false);
264 new GetEnumeratorMethod (this, true);
271 void Define_Current (bool is_generic)
277 left = new MemberName (
278 "System.Collections.Generic.IEnumerator",
279 generic_args, Location);
280 type = iterator_type_expr;
282 left = new MemberName ("System.Collections.IEnumerator", Location);
283 type = TypeManager.system_object_expr;
286 MemberName name = new MemberName (left, "Current", null, Location);
288 ToplevelBlock get_block = new ToplevelBlock (Location);
289 get_block.AddStatement (new CurrentBlock (this, is_generic));
291 Accessor getter = new Accessor (get_block, 0, null, Location);
293 Property current = new Property (
294 this, type, Modifiers.DEBUGGER_HIDDEN, false, name, null, getter, null, false);
295 AddProperty (current);
300 Method reset = new Method (
301 this, null, TypeManager.system_void_expr,
302 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
303 false, new MemberName ("Reset", Location),
304 Parameters.EmptyReadOnlyParameters, null);
307 reset.Block = new ToplevelBlock (Location);
308 reset.Block.AddStatement (Create_ThrowNotSupported ());
311 Statement Create_ThrowNotSupported ()
313 TypeExpr ex_type = new TypeLookupExpression ("System.NotSupportedException");
315 return new Throw (new New (ex_type, null, Location), Location);
318 protected class GetEnumeratorMethod : Method
320 public IteratorStorey Host;
322 static MemberName GetMemberName (IteratorStorey host, bool is_generic)
326 left = new MemberName (
327 "System.Collections.Generic.IEnumerable",
328 host.generic_args, host.Location);
330 left = new MemberName (
331 "System.Collections.IEnumerable", host.Location);
334 return new MemberName (left, "GetEnumerator", host.Location);
337 public GetEnumeratorMethod (IteratorStorey host, bool is_generic)
338 : base (host, null, is_generic ?
339 host.generic_enumerator_type : host.enumerator_type,
340 Modifiers.DEBUGGER_HIDDEN, false, GetMemberName (host, is_generic),
341 Parameters.EmptyReadOnlyParameters, null)
345 host.AddMethod (this);
347 Block = new ToplevelBlock (host.Iterator.Container.Toplevel, null, Location);
348 Block.AddStatement (new GetEnumeratorStatement (host, type_name));
351 public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
353 EmitContext ec = new EmitContext (
354 this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
356 ec.CurrentAnonymousMethod = Host.Iterator;
360 protected class GetEnumeratorStatement : Statement
367 public GetEnumeratorStatement (IteratorStorey host, Expression type)
374 public override bool Resolve (EmitContext ec)
376 type = type.ResolveAsTypeTerminal (ec, false);
377 if ((type == null) || (type.Type == null))
380 TypeExpression storey_type_expr = new TypeExpression (host.TypeBuilder, loc);
381 Expression new_storey;
382 if (host.hoisted_params != null) {
383 ArrayList init = new ArrayList (host.HoistedParameters.Count);
384 foreach (HoistedParameter hp in host.HoistedParameters) {
385 FieldExpr from = new FieldExpr (hp.Field.FieldBuilder, loc);
386 from.InstanceExpression = CompilerGeneratedThis.Instance;
387 init.Add (new ElementInitializer (hp.Field.Name, from, loc));
390 new_storey = new NewInitialize (storey_type_expr, new ArrayList (0),
391 new CollectionOrObjectInitializers (init, loc), loc);
393 new_storey = new New (storey_type_expr, new ArrayList (0), loc);
396 new_storey = new_storey.Resolve (ec);
397 if (new_storey != null)
398 cast = Convert.ImplicitConversionRequired (ec, new_storey, type.Type, loc);
400 if (TypeManager.int_interlocked_compare_exchange == null) {
401 Type t = TypeManager.CoreLookupType ("System.Threading", "Interlocked", Kind.Class, true);
403 TypeManager.int_interlocked_compare_exchange = TypeManager.GetPredefinedMethod (
404 t, "CompareExchange", loc, TypeManager.GetReferenceType (TypeManager.int32_type),
405 TypeManager.int32_type, TypeManager.int32_type);
409 ec.CurrentBranching.CurrentUsageVector.Goto ();
413 protected override void DoEmit (EmitContext ec)
415 ILGenerator ig = ec.ig;
416 Label label_init = ig.DefineLabel ();
418 ig.Emit (OpCodes.Ldarg_0);
419 ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
420 ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Start);
421 ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
422 ig.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange);
424 ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized);
425 ig.Emit (OpCodes.Bne_Un, label_init);
427 ig.Emit (OpCodes.Ldarg_0);
428 ig.Emit (OpCodes.Ret);
430 ig.MarkLabel (label_init);
433 ig.Emit (OpCodes.Ret);
436 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
438 throw new NotSupportedException ();
443 protected class DisposeMethod : Method
445 readonly IteratorStorey Host;
447 public DisposeMethod (IteratorStorey host)
448 : base (host, null, TypeManager.system_void_expr,
449 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
450 false, new MemberName ("Dispose", host.Location),
451 Parameters.EmptyReadOnlyParameters, null)
455 host.AddMethod (this);
457 Block = new ToplevelBlock (host.Iterator.Container, null, Location);
458 Block.AddStatement (new DisposeMethodStatement (Host.Iterator));
460 Report.Debug (64, "DISPOSE METHOD", host, Block);
463 public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
465 EmitContext ec = new EmitContext (
466 this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
468 ec.CurrentAnonymousMethod = Host.Iterator;
472 //public override void Emit ()
474 // if (Parent.MemberName.IsGeneric)
475 // block.MutateHoistedGenericType (Host.Iterator.Storey);
480 protected class DisposeMethodStatement : Statement
484 public DisposeMethodStatement (Iterator iterator)
486 this.iterator = iterator;
487 this.loc = iterator.Location;
490 public override bool Resolve (EmitContext ec)
495 protected override void DoEmit (EmitContext ec)
497 iterator.EmitDispose (ec);
500 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
502 throw new NotSupportedException ();
507 protected class CurrentBlock : Statement {
511 public CurrentBlock (IteratorStorey host, bool is_generic)
514 this.is_generic = is_generic;
518 public override bool Resolve (EmitContext ec)
520 // We emit a 'ret', so prevent the enclosing TopLevelBlock from emitting one too
521 ec.CurrentBranching.CurrentUsageVector.Goto ();
525 protected override void DoEmit (EmitContext ec)
527 ILGenerator ig = ec.ig;
529 ig.Emit (OpCodes.Ldarg_0);
530 ig.Emit (OpCodes.Ldfld, host.CurrentField.FieldBuilder);
532 ig.Emit (OpCodes.Box, host.CurrentField.MemberType);
533 ig.Emit (OpCodes.Ret);
536 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
538 throw new NotSupportedException ();
544 // Iterators are implemented as hidden anonymous block
546 public class Iterator : AnonymousExpression {
547 public readonly IMethodData OriginalMethod;
549 public readonly bool IsEnumerable;
552 // The state as we generate the iterator
554 Label move_next_ok, move_next_error;
555 LocalBuilder skip_finally, current_pc;
557 public LocalBuilder SkipFinally {
558 get { return skip_finally; }
561 public LocalBuilder CurrentPC {
562 get { return current_pc; }
565 public Block Container {
566 get { return OriginalMethod.Block; }
569 public GenericMethod GenericMethod {
570 get { return OriginalMethod.GenericMethod; }
573 public readonly Type OriginalIteratorType;
575 readonly IteratorStorey IteratorHost;
578 Running = -3, // Used only in CurrentPC, never stored into $PC
584 public override void AddStoreyReference (AnonymousMethodStorey storey)
589 public void EmitYieldBreak (ILGenerator ig, bool unwind_protect)
591 ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
594 void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block)
596 ILGenerator ig = ec.ig;
598 ig.Emit (OpCodes.Ldarg_0);
599 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
601 ig.Emit (OpCodes.Ldarg_0);
602 IntConstant.EmitInt (ig, (int) State.After);
603 ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
605 // We only care if the PC is zero (start executing) or non-zero (don't do anything)
606 ig.Emit (OpCodes.Brtrue, move_next_error);
608 SymbolWriter.StartIteratorBody (ec.ig);
609 original_block.Emit (ec);
610 SymbolWriter.EndIteratorBody (ec.ig);
612 ig.MarkLabel (move_next_error);
613 ig.Emit (OpCodes.Ldc_I4_0);
614 ig.Emit (OpCodes.Ret);
617 internal void EmitMoveNext (EmitContext ec, Block original_block)
619 ILGenerator ig = ec.ig;
621 move_next_ok = ig.DefineLabel ();
622 move_next_error = ig.DefineLabel ();
624 if (resume_points == null) {
625 EmitMoveNext_NoResumePoints (ec, original_block);
629 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
630 ig.Emit (OpCodes.Ldarg_0);
631 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
632 ig.Emit (OpCodes.Stloc, current_pc);
634 // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
635 ig.Emit (OpCodes.Ldarg_0);
636 IntConstant.EmitInt (ig, (int) State.After);
637 ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
639 Label [] labels = new Label [1 + resume_points.Count];
640 labels [0] = ig.DefineLabel ();
642 bool need_skip_finally = false;
643 for (int i = 0; i < resume_points.Count; ++i) {
644 ResumableStatement s = (ResumableStatement) resume_points [i];
645 need_skip_finally |= s is ExceptionStatement;
646 labels [i+1] = s.PrepareForEmit (ec);
649 if (need_skip_finally) {
650 skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type);
651 ig.Emit (OpCodes.Ldc_I4_0);
652 ig.Emit (OpCodes.Stloc, skip_finally);
655 SymbolWriter.StartIteratorDispatcher (ec.ig);
656 ig.Emit (OpCodes.Ldloc, current_pc);
657 ig.Emit (OpCodes.Switch, labels);
659 ig.Emit (OpCodes.Br, move_next_error);
660 SymbolWriter.EndIteratorDispatcher (ec.ig);
662 ig.MarkLabel (labels [0]);
664 SymbolWriter.StartIteratorBody (ec.ig);
665 original_block.Emit (ec);
666 SymbolWriter.EndIteratorBody (ec.ig);
668 SymbolWriter.StartIteratorDispatcher (ec.ig);
670 ig.Emit (OpCodes.Ldarg_0);
671 IntConstant.EmitInt (ig, (int) State.After);
672 ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
674 ig.MarkLabel (move_next_error);
675 ig.Emit (OpCodes.Ldc_I4_0);
676 ig.Emit (OpCodes.Ret);
678 ig.MarkLabel (move_next_ok);
679 ig.Emit (OpCodes.Ldc_I4_1);
680 ig.Emit (OpCodes.Ret);
682 SymbolWriter.EndIteratorDispatcher (ec.ig);
685 public void EmitDispose (EmitContext ec)
687 ILGenerator ig = ec.ig;
689 Label end = ig.DefineLabel ();
691 Label [] labels = null;
692 int n_resume_points = resume_points == null ? 0 : resume_points.Count;
693 for (int i = 0; i < n_resume_points; ++i) {
694 ResumableStatement s = (ResumableStatement) resume_points [i];
695 Label ret = s.PrepareForDispose (ec, end);
696 if (ret.Equals (end) && labels == null)
698 if (labels == null) {
699 labels = new Label [resume_points.Count + 1];
700 for (int j = 0; j <= i; ++j)
706 if (labels != null) {
707 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
708 ig.Emit (OpCodes.Ldarg_0);
709 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
710 ig.Emit (OpCodes.Stloc, current_pc);
713 ig.Emit (OpCodes.Ldarg_0);
714 IntConstant.EmitInt (ig, (int) State.After);
715 ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
717 if (labels != null) {
718 //SymbolWriter.StartIteratorDispatcher (ec.ig);
719 ig.Emit (OpCodes.Ldloc, current_pc);
720 ig.Emit (OpCodes.Switch, labels);
721 //SymbolWriter.EndIteratorDispatcher (ec.ig);
723 foreach (ResumableStatement s in resume_points)
724 s.EmitForDispose (ec, this, end, true);
731 ArrayList resume_points;
732 public int AddResumePoint (ResumableStatement stmt, Location loc)
734 if (resume_points == null)
735 resume_points = new ArrayList ();
736 resume_points.Add (stmt);
737 return resume_points.Count;
741 // Called back from Yield
743 public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
745 ILGenerator ig = ec.ig;
747 // Store the new current
748 ig.Emit (OpCodes.Ldarg_0);
750 ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
752 // store resume program-counter
753 ig.Emit (OpCodes.Ldarg_0);
754 IntConstant.EmitInt (ig, resume_pc);
755 ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
757 // mark finally blocks as disabled
758 if (unwind_protect && skip_finally != null) {
759 ig.Emit (OpCodes.Ldc_I4_1);
760 ig.Emit (OpCodes.Stloc, skip_finally);
764 ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
766 ig.MarkLabel (resume_point);
769 public override string ContainerType {
770 get { return "iterator"; }
773 public override bool IsIterator {
777 public override AnonymousMethodStorey Storey {
778 get { return IteratorHost; }
784 private Iterator (IMethodData method, TypeContainer host, Type iterator_type, bool is_enumerable)
786 new ToplevelBlock (method.Block, Parameters.EmptyReadOnlyParameters, method.Block.StartLocation),
787 TypeManager.bool_type,
790 this.OriginalMethod = method;
791 this.OriginalIteratorType = iterator_type;
792 this.IsEnumerable = is_enumerable;
794 IteratorHost = Block.ChangeToIterator (this, method.Block);
797 public override string GetSignatureForError ()
799 return OriginalMethod.GetSignatureForError ();
802 public override Expression DoResolve (EmitContext ec)
804 method = new AnonymousMethodMethod (Storey,
805 this, Storey, null, TypeManager.system_boolean_expr,
806 Modifiers.PUBLIC, OriginalMethod.GetSignatureForError (),
807 new MemberName ("MoveNext", Location),
808 Parameters.EmptyReadOnlyParameters);
810 if (!Compatible (ec))
813 IteratorHost.DefineIteratorMembers ();
815 eclass = ExprClass.Value;
816 type = ec.ReturnType;
820 public override void Emit (EmitContext ec)
823 // Load Iterator storey instance
825 method.Storey.Instance.Emit (ec);
828 // Initialize iterator PC when it's unitialized
831 ILGenerator ig = ec.ig;
832 ig.Emit (OpCodes.Dup);
833 IntConstant.EmitInt (ig, (int)State.Uninitialized);
835 FieldInfo field = IteratorHost.PC.FieldBuilder;
837 if (Storey.MemberName.IsGeneric)
838 field = TypeBuilder.GetField (Storey.Instance.Type, field);
840 ig.Emit (OpCodes.Stfld, field);
844 public override Expression CreateExpressionTree (EmitContext ec)
846 throw new NotSupportedException ("ET");
849 public static void CreateIterator (IMethodData method, TypeContainer parent, int modifiers)
854 Type ret = method.ReturnType;
858 if (!CheckType (ret, out iterator_type, out is_enumerable)) {
859 Report.Error (1624, method.Location,
860 "The body of `{0}' cannot be an iterator block " +
861 "because `{1}' is not an iterator interface type",
862 method.GetSignatureForError (),
863 TypeManager.CSharpName (ret));
867 Parameters parameters = method.ParameterInfo;
868 for (int i = 0; i < parameters.Count; i++) {
869 Parameter p = parameters [i];
870 Parameter.Modifier mod = p.ModFlags;
871 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
872 Report.Error (1623, p.Location,
873 "Iterators cannot have ref or out parameters");
877 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
878 Report.Error (1636, method.Location,
879 "__arglist is not allowed in parameter list of iterators");
883 if (p.ParameterType.IsPointer) {
884 Report.Error (1637, p.Location,
885 "Iterators cannot have unsafe parameters or " +
891 if ((modifiers & Modifiers.UNSAFE) != 0) {
892 Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
896 Iterator iter = new Iterator (method, parent, iterator_type, is_enumerable);
897 iter.Storey.DefineType ();
900 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
902 original_iterator_type = null;
903 is_enumerable = false;
905 if (ret == TypeManager.ienumerable_type) {
906 original_iterator_type = TypeManager.object_type;
907 is_enumerable = true;
910 if (ret == TypeManager.ienumerator_type) {
911 original_iterator_type = TypeManager.object_type;
912 is_enumerable = false;
916 if (!TypeManager.IsGenericType (ret))
919 Type[] args = TypeManager.GetTypeArguments (ret);
920 if (args.Length != 1)
923 Type gt = TypeManager.DropGenericTypeArguments (ret);
924 if (gt == TypeManager.generic_ienumerable_type) {
925 original_iterator_type = args [0];
926 is_enumerable = true;
930 if (gt == TypeManager.generic_ienumerator_type) {
931 original_iterator_type = args [0];
932 is_enumerable = false;