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;
35 public Yield (Expression expr, Location l)
41 public static bool CheckContext (EmitContext ec, Location loc)
44 Report.Error (-208, loc, "yield statement can not appear in finally clause");
48 Report.Error (-209, loc, "yield statement can not appear in the catch clause");
51 if (ec.InAnonymousMethod){
52 Report.Error (-209, loc, "yield statement can not appear inside an anonymoud method");
57 // FIXME: Missing check for Yield inside try block that contains catch clauses
62 public override bool Resolve (EmitContext ec)
64 expr = expr.Resolve (ec);
67 if (!CheckContext (ec, loc))
70 Type iterator_type = IteratorHandler.Current.IteratorType;
71 if (expr.Type != iterator_type){
72 expr = Convert.ImplicitConversionRequired (ec, expr, iterator_type, loc);
79 protected override bool DoEmit (EmitContext ec)
81 IteratorHandler.Current.MarkYield (ec, expr);
87 public class YieldBreak : Statement {
89 public YieldBreak (Location l)
94 public override bool Resolve (EmitContext ec)
96 if (!Yield.CheckContext (ec, loc))
99 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowBranching.FlowReturns.Always;
100 ec.CurrentBranching.CurrentUsageVector.Returns = FlowBranching.FlowReturns.Always;
104 protected override bool DoEmit (EmitContext ec)
106 IteratorHandler.Current.EmitYieldBreak (ec.ig, true);
111 public class IteratorHandler {
113 // Points to the current iterator handler, will be probed by
114 // Yield and YieldBreak to get their context information
116 public static IteratorHandler Current;
119 // The typebuilder to the proxy class we create
121 TypeBuilder enumerator_proxy_class;
122 TypeBuilder enumerable_proxy_class;
125 // The type of this iterator, object by default.
127 public Type IteratorType;
130 // The members we create on the proxy class
132 MethodBuilder move_next_method;
133 MethodBuilder reset_method;
134 MethodBuilder get_current_method;
135 MethodBuilder dispose_method;
136 MethodBuilder getenumerator_method;
137 PropertyBuilder current_property;
138 ConstructorBuilder enumerator_proxy_constructor;
139 ConstructorBuilder enumerable_proxy_constructor;
142 // The PC for the state machine.
144 FieldBuilder pc_field;
147 // The value computed for Current
149 FieldBuilder current_field;
152 // Used to reference fields on the container class (instance methods)
154 public FieldBuilder this_field;
155 public FieldBuilder enumerable_this_field;
158 // References the parameters
161 public FieldBuilder [] parameter_fields;
162 FieldBuilder [] enumerable_parameter_fields;
165 // The state as we generate the iterator
167 ArrayList resume_labels = new ArrayList ();
171 // Context from the original method
174 TypeContainer container;
177 InternalParameters parameters;
178 Block original_block;
182 static int proxy_count;
183 string MakeProxyName ()
185 return String.Format ("__Proxy_{0}", proxy_count++);
188 public void EmitYieldBreak (ILGenerator ig, bool add_return)
190 ig.Emit (OpCodes.Ldarg_0);
191 IntConstant.EmitInt (ig, -1);
192 ig.Emit (OpCodes.Stfld, pc_field);
194 ig.Emit (OpCodes.Ldc_I4_0);
195 ig.Emit (OpCodes.Ret);
199 void EmitThrowInvalidOp (ILGenerator ig)
201 ig.Emit (OpCodes.Newobj, TypeManager.invalid_operation_ctor);
202 ig.Emit (OpCodes.Throw);
205 void Create_MoveNext ()
207 move_next_method = enumerator_proxy_class.DefineMethod (
208 "System.IEnumerator.MoveNext",
209 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
210 MethodAttributes.Virtual,
211 CallingConventions.HasThis, TypeManager.bool_type, TypeManager.NoTypes);
212 enumerator_proxy_class.DefineMethodOverride (move_next_method, TypeManager.bool_movenext_void);
214 ILGenerator ig = move_next_method.GetILGenerator ();
215 EmitContext ec = new EmitContext (
217 TypeManager.void_type, modifiers);
219 Label dispatcher = ig.DefineLabel ();
220 ig.Emit (OpCodes.Br, dispatcher);
221 Label entry_point = ig.DefineLabel ();
222 ig.MarkLabel (entry_point);
223 resume_labels.Add (entry_point);
226 SymbolWriter sw = CodeGen.SymbolWriter;
227 if ((sw != null) && !Location.IsNull (loc) && !Location.IsNull (original_block.EndLocation)) {
228 sw.OpenMethod (container, move_next_method, loc, original_block.EndLocation);
230 ec.EmitTopBlock (original_block, parameters, loc);
234 ec.EmitTopBlock (original_block, parameters, loc);
238 EmitYieldBreak (ig, true);
241 // FIXME: Split the switch in blocks that can be consumed by switch.
243 ig.MarkLabel (dispatcher);
245 Label [] labels = new Label [resume_labels.Count];
246 resume_labels.CopyTo (labels);
247 ig.Emit (OpCodes.Ldarg_0);
248 ig.Emit (OpCodes.Ldfld, pc_field);
249 ig.Emit (OpCodes.Switch, labels);
250 ig.Emit (OpCodes.Ldc_I4_0);
251 ig.Emit (OpCodes.Ret);
255 // Invoked when a local variable declaration needs to be mapped to
256 // a field in our proxy class
258 // Prefixes registered:
259 // v_ for EmitContext.MapVariable
262 public FieldBuilder MapVariable (string pfx, string name, Type t)
264 return enumerator_proxy_class.DefineField (pfx + name, t, FieldAttributes.Public);
269 reset_method = enumerator_proxy_class.DefineMethod (
270 "System.IEnumerator.Reset",
271 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
272 MethodAttributes.Virtual,
273 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
274 enumerator_proxy_class.DefineMethodOverride (reset_method, TypeManager.void_reset_void);
275 ILGenerator ig = reset_method.GetILGenerator ();
276 EmitThrowInvalidOp (ig);
279 void Create_Current ()
281 get_current_method = enumerator_proxy_class.DefineMethod (
282 "System.IEnumerator.get_Current",
283 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
284 MethodAttributes.NewSlot | MethodAttributes.Virtual,
285 CallingConventions.HasThis, TypeManager.object_type, TypeManager.NoTypes);
286 enumerator_proxy_class.DefineMethodOverride (get_current_method, TypeManager.object_getcurrent_void);
288 current_property = enumerator_proxy_class.DefineProperty (
290 PropertyAttributes.RTSpecialName | PropertyAttributes.SpecialName,
291 TypeManager.object_type, null);
293 current_property.SetGetMethod (get_current_method);
295 ILGenerator ig = get_current_method.GetILGenerator ();
297 ig.Emit (OpCodes.Ldarg_0);
298 ig.Emit (OpCodes.Ldfld, pc_field);
299 ig.Emit (OpCodes.Ldc_I4_0);
300 Label return_current = ig.DefineLabel ();
301 ig.Emit (OpCodes.Bgt, return_current);
302 EmitThrowInvalidOp (ig);
304 ig.MarkLabel (return_current);
305 ig.Emit (OpCodes.Ldarg_0);
306 ig.Emit (OpCodes.Ldfld, current_field);
307 ig.Emit (OpCodes.Ret);
310 void Create_Dispose ()
312 dispose_method = enumerator_proxy_class.DefineMethod (
313 "System.IDisposable.Dispose",
314 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
315 MethodAttributes.NewSlot | MethodAttributes.Virtual,
316 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
317 enumerator_proxy_class.DefineMethodOverride (dispose_method, TypeManager.void_dispose_void);
318 ILGenerator ig = dispose_method.GetILGenerator ();
320 EmitYieldBreak (ig, false);
321 ig.Emit (OpCodes.Ret);
324 void Create_GetEnumerator ()
326 getenumerator_method = enumerable_proxy_class.DefineMethod (
327 "IEnumerable.GetEnumerator",
328 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
329 MethodAttributes.NewSlot | MethodAttributes.Virtual,
330 CallingConventions.HasThis, TypeManager.ienumerator_type, TypeManager.NoTypes);
332 enumerable_proxy_class.DefineMethodOverride (getenumerator_method, TypeManager.ienumerable_getenumerator_void);
333 ILGenerator ig = getenumerator_method.GetILGenerator ();
335 if (enumerable_this_field != null){
336 ig.Emit (OpCodes.Ldarg_0);
337 ig.Emit (OpCodes.Ldfld, enumerable_this_field);
339 for (int i = 0; i < parameters.Count; i++){
340 ig.Emit (OpCodes.Ldarg_0);
341 ig.Emit (OpCodes.Ldfld, enumerable_parameter_fields [i]);
343 ig.Emit (OpCodes.Newobj, (ConstructorInfo) enumerator_proxy_constructor);
344 ig.Emit (OpCodes.Ret);
347 void LoadArgs (ILGenerator ig)
349 int count = parameters.Count;
350 if ((modifiers & Modifiers.STATIC) == 0)
353 for (int i = 0; i < count; i++)
354 ParameterReference.EmitLdArg (ig, i);
358 // Called back from Yield
360 public void MarkYield (EmitContext ec, Expression expr)
362 ILGenerator ig = ec.ig;
364 // Store the new current
365 ig.Emit (OpCodes.Ldarg_0);
367 ig.Emit (OpCodes.Stfld, current_field);
371 ig.Emit (OpCodes.Ldarg_0);
372 IntConstant.EmitInt (ig, pc);
373 ig.Emit (OpCodes.Stfld, pc_field);
376 ig.Emit (OpCodes.Ldc_I4_1);
377 ig.Emit (OpCodes.Ret);
379 Label resume_point = ig.DefineLabel ();
380 ig.MarkLabel (resume_point);
381 resume_labels.Add (resume_point);
384 void ComputeConstructorTypes (out Type [] constructor_types, out Parameters constructor_parameters)
386 bool is_static = (modifiers & Modifiers.STATIC) != 0;
388 if (is_static && parameters.Count == 0){
389 constructor_types = TypeManager.NoTypes;
390 constructor_parameters = Parameters.EmptyReadOnlyParameters;
394 int count = (is_static ? 0 : 1) + parameters.Count;
395 constructor_types = new Type [count];
396 Parameter [] pars = new Parameter [count];
397 constructor_parameters = new Parameters (pars, null, loc);
401 constructor_types [0] = container.TypeBuilder;
403 Parameter THIS = new Parameter (
404 new TypeExpr (container.TypeBuilder, loc), "this", Parameter.Modifier.NONE, null);
409 for (int j = 0; j < parameters.Count; j++, i++){
410 Type partype = parameters.ParameterType (j);
412 pars [i] = new Parameter (new TypeExpr (partype, loc),
413 parameters.ParameterName (j),
414 Parameter.Modifier.NONE, null);
415 constructor_types [i] = partype;
420 // Creates the IEnumerator Proxy class
422 void MakeEnumeratorProxy ()
424 Type [] proxy_base_interfaces = new Type [2];
425 proxy_base_interfaces [0] = TypeManager.ienumerator_type;
426 proxy_base_interfaces [1] = TypeManager.idisposable_type;
427 TypeBuilder container_builder = container.TypeBuilder;
432 enumerator_proxy_class = container_builder.DefineNestedType (
433 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
434 TypeManager.object_type, proxy_base_interfaces);
436 TypeManager.RegisterBuilder (enumerator_proxy_class, proxy_base_interfaces);
441 pc_field = enumerator_proxy_class.DefineField ("PC", TypeManager.int32_type, FieldAttributes.Private);
442 current_field = enumerator_proxy_class.DefineField ("current", IteratorType, FieldAttributes.Private);
443 if ((modifiers & Modifiers.STATIC) == 0)
444 this_field = enumerator_proxy_class.DefineField ("THIS", container.TypeBuilder, FieldAttributes.Private);
446 parameter_fields = new FieldBuilder [parameters.Count];
447 for (int i = 0; i < parameters.Count; i++){
448 parameter_fields [i] = enumerator_proxy_class.DefineField (
449 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
450 parameters.ParameterType (i), FieldAttributes.Private);
454 // Define a constructor
456 // FIXME: currently its parameterless
457 Type [] constructor_types;
458 Parameters constructor_parameters;
460 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
462 enumerator_proxy_constructor = enumerator_proxy_class.DefineConstructor (
463 MethodAttributes.Public | MethodAttributes.HideBySig |
464 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
465 CallingConventions.HasThis, constructor_types);
466 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
467 TypeManager.RegisterMethod (enumerator_proxy_constructor, parameter_info, constructor_types);
472 ILGenerator ig = enumerator_proxy_constructor.GetILGenerator ();
473 ig.Emit (OpCodes.Ldarg_0);
474 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
477 if (this_field != null){
479 ig.Emit (OpCodes.Ldarg_0);
480 ig.Emit (OpCodes.Ldarg_1);
481 ig.Emit (OpCodes.Stfld, this_field);
485 for (int i = 0; i < parameters.Count; i++){
486 ig.Emit (OpCodes.Ldarg_0);
487 ParameterReference.EmitLdArg (ig, i + arg_start);
488 ig.Emit (OpCodes.Stfld, parameter_fields [i]);
490 ig.Emit (OpCodes.Ret);
494 // Creates the IEnumerable proxy class
496 void MakeEnumerableProxy ()
498 TypeBuilder container_builder = container.TypeBuilder;
499 Type [] proxy_base_interfaces = new Type [1];
500 proxy_base_interfaces [0] = TypeManager.ienumerable_type;
503 // Creates the Enumerable proxy class.
505 enumerable_proxy_class = container_builder.DefineNestedType (
506 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
507 TypeManager.object_type, proxy_base_interfaces);
512 Type [] constructor_types;
513 Parameters constructor_parameters;
515 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
516 if ((modifiers & Modifiers.STATIC) == 0){
517 enumerable_this_field = enumerable_proxy_class.DefineField (
518 "THIS", container.TypeBuilder, FieldAttributes.Private);
520 enumerable_parameter_fields = new FieldBuilder [parameters.Count];
521 for (int i = 0; i < parameters.Count; i++){
522 enumerable_parameter_fields [i] = enumerable_proxy_class.DefineField (
523 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
524 parameters.ParameterType (i), FieldAttributes.Private);
527 enumerable_proxy_constructor = enumerable_proxy_class.DefineConstructor (
528 MethodAttributes.Public | MethodAttributes.HideBySig |
529 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
530 CallingConventions.HasThis, constructor_types);
531 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
532 TypeManager.RegisterMethod (enumerable_proxy_constructor, parameter_info, constructor_types);
534 ILGenerator ig = enumerable_proxy_constructor.GetILGenerator ();
535 ig.Emit (OpCodes.Ldarg_0);
536 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
539 if (enumerable_this_field != null){
540 ig.Emit (OpCodes.Ldarg_0);
541 ig.Emit (OpCodes.Ldarg_1);
542 ig.Emit (OpCodes.Stfld, enumerable_this_field);
547 for (int i = 0; i < parameters.Count; i++){
548 ig.Emit (OpCodes.Ldarg_0);
549 ParameterReference.EmitLdArg (ig, i + first_arg);
550 ig.Emit (OpCodes.Stfld, enumerable_parameter_fields [i]);
552 ig.Emit (OpCodes.Ret);
556 // Populates the Enumerator Proxy class
558 void PopulateProxy ()
560 RootContext.RegisterHelperClass (enumerator_proxy_class);
567 if (return_type == TypeManager.ienumerable_type){
568 Create_GetEnumerator ();
569 RootContext.RegisterHelperClass (enumerable_proxy_class);
575 // This is invoked by the EmitCode hook
577 void SetupIterator ()
585 public IteratorHandler (string name, TypeContainer container, Type return_type, Type [] param_types,
586 InternalParameters parameters, int modifiers, Location loc)
589 this.container = container;
590 this.return_type = return_type;
591 this.param_types = param_types;
592 this.parameters = parameters;
593 this.modifiers = modifiers;
596 IteratorType = TypeManager.object_type;
598 RootContext.EmitCodeHook += new RootContext.Hook (SetupIterator);
602 // This class is just an expression that evaluates to a type, and the
603 // type is our internal proxy class. Used in the generated new body
604 // of the original method
606 class NewInnerType : Expression {
607 IteratorHandler handler;
609 public NewInnerType (IteratorHandler handler, Location l)
611 this.handler = handler;
612 eclass = ExprClass.Value;
616 public override Expression DoResolve (EmitContext ec)
618 // Create the proxy class type.
619 handler.MakeEnumeratorProxy ();
621 if (handler.return_type == TypeManager.ienumerable_type)
622 handler.MakeEnumerableProxy ();
624 type = handler.return_type;
628 public override Expression ResolveAsTypeStep (EmitContext ec)
630 return DoResolve (ec);
633 public override void Emit (EmitContext ec)
635 handler.LoadArgs (ec.ig);
637 if (handler.return_type == TypeManager.ienumerable_type)
638 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerable_proxy_constructor);
640 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerator_proxy_constructor);
645 // This return statement tricks return into not flagging an error for being
646 // used in a Yields method
648 class NoCheckReturn : Return {
649 public NoCheckReturn (Expression expr, Location loc) : base (expr, loc)
653 public override bool Resolve (EmitContext ec)
655 ec.InIterator = false;
656 bool ret_val = base.Resolve (ec);
657 ec.InIterator = true;
664 // Returns the new block for the method, or null on failure
666 public Block Setup (Block block)
668 if (return_type != TypeManager.ienumerator_type &&
669 return_type != TypeManager.ienumerable_type){
671 -205, loc, String.Format (
672 "The method `{0}' contains a yield statement, but has an invalid return type for an iterator",
677 for (int i = 0; i < parameters.Count; i++){
678 Parameter.Modifier mod = parameters.ParameterModifier (i);
679 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
680 Report.Error (-207, loc, String.Format (
681 "Parameter {0} of `{1}' is {2} and not allowed for an iterator method",
682 i+1, name, parameters.ParameterDesc (i)));
687 original_block = block;
688 Block b = new Block (null);
690 // return new InnerClass ()
691 b.AddStatement (new NoCheckReturn (new NewInnerType (this, loc), loc));