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 = FlowReturns.ALWAYS;
100 ec.CurrentBranching.CurrentUsageVector.Returns = 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 public FieldBuilder MapVariable (string name, Type t)
260 return enumerator_proxy_class.DefineField ("v" + name, t, FieldAttributes.Public);
265 reset_method = enumerator_proxy_class.DefineMethod (
266 "System.IEnumerator.Reset",
267 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
268 MethodAttributes.Virtual,
269 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
270 enumerator_proxy_class.DefineMethodOverride (reset_method, TypeManager.void_reset_void);
271 ILGenerator ig = reset_method.GetILGenerator ();
272 EmitThrowInvalidOp (ig);
275 void Create_Current ()
277 get_current_method = enumerator_proxy_class.DefineMethod (
278 "System.IEnumerator.get_Current",
279 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
280 MethodAttributes.NewSlot | MethodAttributes.Virtual,
281 CallingConventions.HasThis, TypeManager.object_type, TypeManager.NoTypes);
282 enumerator_proxy_class.DefineMethodOverride (get_current_method, TypeManager.object_getcurrent_void);
284 current_property = enumerator_proxy_class.DefineProperty (
286 PropertyAttributes.RTSpecialName | PropertyAttributes.SpecialName,
287 TypeManager.object_type, null);
289 current_property.SetGetMethod (get_current_method);
291 ILGenerator ig = get_current_method.GetILGenerator ();
293 ig.Emit (OpCodes.Ldarg_0);
294 ig.Emit (OpCodes.Ldfld, pc_field);
295 ig.Emit (OpCodes.Ldc_I4_0);
296 Label return_current = ig.DefineLabel ();
297 ig.Emit (OpCodes.Bgt, return_current);
298 EmitThrowInvalidOp (ig);
300 ig.MarkLabel (return_current);
301 ig.Emit (OpCodes.Ldarg_0);
302 ig.Emit (OpCodes.Ldfld, current_field);
303 ig.Emit (OpCodes.Ret);
306 void Create_Dispose ()
308 dispose_method = enumerator_proxy_class.DefineMethod (
309 "System.IDisposable.Dispose",
310 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
311 MethodAttributes.NewSlot | MethodAttributes.Virtual,
312 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
313 enumerator_proxy_class.DefineMethodOverride (dispose_method, TypeManager.void_dispose_void);
314 ILGenerator ig = dispose_method.GetILGenerator ();
316 EmitYieldBreak (ig, false);
317 ig.Emit (OpCodes.Ret);
320 void Create_GetEnumerator ()
322 getenumerator_method = enumerable_proxy_class.DefineMethod (
323 "IEnumerable.GetEnumerator",
324 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
325 MethodAttributes.NewSlot | MethodAttributes.Virtual,
326 CallingConventions.HasThis, TypeManager.ienumerator_type, TypeManager.NoTypes);
328 enumerable_proxy_class.DefineMethodOverride (getenumerator_method, TypeManager.ienumerable_getenumerator_void);
329 ILGenerator ig = getenumerator_method.GetILGenerator ();
331 if (enumerable_this_field != null){
332 ig.Emit (OpCodes.Ldarg_0);
333 ig.Emit (OpCodes.Ldfld, enumerable_this_field);
335 for (int i = 0; i < parameters.Count; i++){
336 ig.Emit (OpCodes.Ldarg_0);
337 ig.Emit (OpCodes.Ldfld, enumerable_parameter_fields [i]);
339 ig.Emit (OpCodes.Newobj, (ConstructorInfo) enumerator_proxy_constructor);
340 ig.Emit (OpCodes.Ret);
343 void LoadArgs (ILGenerator ig)
345 int count = parameters.Count;
346 if ((modifiers & Modifiers.STATIC) == 0)
349 for (int i = 0; i < count; i++)
350 ParameterReference.EmitLdArg (ig, i);
354 // Called back from Yield
356 public void MarkYield (EmitContext ec, Expression expr)
358 ILGenerator ig = ec.ig;
360 // Store the new current
361 ig.Emit (OpCodes.Ldarg_0);
363 ig.Emit (OpCodes.Stfld, current_field);
367 ig.Emit (OpCodes.Ldarg_0);
368 IntConstant.EmitInt (ig, pc);
369 ig.Emit (OpCodes.Stfld, pc_field);
372 ig.Emit (OpCodes.Ldc_I4_1);
373 ig.Emit (OpCodes.Ret);
375 Label resume_point = ig.DefineLabel ();
376 ig.MarkLabel (resume_point);
377 resume_labels.Add (resume_point);
380 void ComputeConstructorTypes (out Type [] constructor_types, out Parameters constructor_parameters)
382 bool is_static = (modifiers & Modifiers.STATIC) != 0;
384 if (is_static && parameters.Count == 0){
385 constructor_types = TypeManager.NoTypes;
386 constructor_parameters = Parameters.EmptyReadOnlyParameters;
390 int count = (is_static ? 0 : 1) + parameters.Count;
391 constructor_types = new Type [count];
392 Parameter [] pars = new Parameter [count];
393 constructor_parameters = new Parameters (pars, null, loc);
397 constructor_types [0] = container.TypeBuilder;
399 Parameter THIS = new Parameter (
400 new TypeExpr (container.TypeBuilder, loc), "this", Parameter.Modifier.NONE, null);
405 for (int j = 0; j < parameters.Count; j++, i++){
406 Type partype = parameters.ParameterType (j);
408 pars [i] = new Parameter (new TypeExpr (partype, loc),
409 parameters.ParameterName (j),
410 Parameter.Modifier.NONE, null);
411 constructor_types [i] = partype;
416 // Creates the IEnumerator Proxy class
418 void MakeEnumeratorProxy ()
420 Type [] proxy_base_interfaces = new Type [2];
421 proxy_base_interfaces [0] = TypeManager.ienumerator_type;
422 proxy_base_interfaces [1] = TypeManager.idisposable_type;
423 TypeBuilder container_builder = container.TypeBuilder;
428 enumerator_proxy_class = container_builder.DefineNestedType (
429 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
430 TypeManager.object_type, proxy_base_interfaces);
432 TypeManager.RegisterBuilder (enumerator_proxy_class, proxy_base_interfaces);
437 pc_field = enumerator_proxy_class.DefineField ("PC", TypeManager.int32_type, FieldAttributes.Private);
438 current_field = enumerator_proxy_class.DefineField ("current", IteratorType, FieldAttributes.Private);
439 if ((modifiers & Modifiers.STATIC) == 0)
440 this_field = enumerator_proxy_class.DefineField ("THIS", container.TypeBuilder, FieldAttributes.Private);
442 parameter_fields = new FieldBuilder [parameters.Count];
443 for (int i = 0; i < parameters.Count; i++){
444 parameter_fields [i] = enumerator_proxy_class.DefineField (
445 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
446 parameters.ParameterType (i), FieldAttributes.Private);
450 // Define a constructor
452 // FIXME: currently its parameterless
453 Type [] constructor_types;
454 Parameters constructor_parameters;
456 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
458 enumerator_proxy_constructor = enumerator_proxy_class.DefineConstructor (
459 MethodAttributes.Public | MethodAttributes.HideBySig |
460 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
461 CallingConventions.HasThis, constructor_types);
462 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
463 TypeManager.RegisterMethod (enumerator_proxy_constructor, parameter_info, constructor_types);
468 ILGenerator ig = enumerator_proxy_constructor.GetILGenerator ();
469 ig.Emit (OpCodes.Ldarg_0);
470 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
473 if (this_field != null){
475 ig.Emit (OpCodes.Ldarg_0);
476 ig.Emit (OpCodes.Ldarg_1);
477 ig.Emit (OpCodes.Stfld, this_field);
481 for (int i = 0; i < parameters.Count; i++){
482 ig.Emit (OpCodes.Ldarg_0);
483 ParameterReference.EmitLdArg (ig, i + arg_start);
484 ig.Emit (OpCodes.Stfld, parameter_fields [i]);
486 ig.Emit (OpCodes.Ret);
490 // Creates the IEnumerable proxy class
492 void MakeEnumerableProxy ()
494 TypeBuilder container_builder = container.TypeBuilder;
495 Type [] proxy_base_interfaces = new Type [1];
496 proxy_base_interfaces [0] = TypeManager.ienumerable_type;
499 // Creates the Enumerable proxy class.
501 enumerable_proxy_class = container_builder.DefineNestedType (
502 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
503 TypeManager.object_type, proxy_base_interfaces);
508 Type [] constructor_types;
509 Parameters constructor_parameters;
511 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
512 if ((modifiers & Modifiers.STATIC) == 0){
513 enumerable_this_field = enumerable_proxy_class.DefineField (
514 "THIS", container.TypeBuilder, FieldAttributes.Private);
516 enumerable_parameter_fields = new FieldBuilder [parameters.Count];
517 for (int i = 0; i < parameters.Count; i++){
518 enumerable_parameter_fields [i] = enumerable_proxy_class.DefineField (
519 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
520 parameters.ParameterType (i), FieldAttributes.Private);
523 enumerable_proxy_constructor = enumerable_proxy_class.DefineConstructor (
524 MethodAttributes.Public | MethodAttributes.HideBySig |
525 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
526 CallingConventions.HasThis, constructor_types);
527 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
528 TypeManager.RegisterMethod (enumerable_proxy_constructor, parameter_info, constructor_types);
530 ILGenerator ig = enumerable_proxy_constructor.GetILGenerator ();
531 ig.Emit (OpCodes.Ldarg_0);
532 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
535 if (enumerable_this_field != null){
536 ig.Emit (OpCodes.Ldarg_0);
537 ig.Emit (OpCodes.Ldarg_1);
538 ig.Emit (OpCodes.Stfld, enumerable_this_field);
543 for (int i = 0; i < parameters.Count; i++){
544 ig.Emit (OpCodes.Ldarg_0);
545 ParameterReference.EmitLdArg (ig, i + first_arg);
546 ig.Emit (OpCodes.Stfld, enumerable_parameter_fields [i]);
548 ig.Emit (OpCodes.Ret);
552 // Populates the Enumerator Proxy class
554 void PopulateProxy ()
556 RootContext.RegisterHelperClass (enumerator_proxy_class);
563 if (return_type == TypeManager.ienumerable_type){
564 Create_GetEnumerator ();
565 RootContext.RegisterHelperClass (enumerable_proxy_class);
571 // This is invoked by the EmitCode hook
573 void SetupIterator ()
581 public IteratorHandler (string name, TypeContainer container, Type return_type, Type [] param_types,
582 InternalParameters parameters, int modifiers, Location loc)
585 this.container = container;
586 this.return_type = return_type;
587 this.param_types = param_types;
588 this.parameters = parameters;
589 this.modifiers = modifiers;
592 IteratorType = TypeManager.object_type;
594 RootContext.EmitCodeHook += new RootContext.Hook (SetupIterator);
598 // This class is just an expression that evaluates to a type, and the
599 // type is our internal proxy class. Used in the generated new body
600 // of the original method
602 class NewInnerType : Expression {
603 IteratorHandler handler;
605 public NewInnerType (IteratorHandler handler, Location l)
607 this.handler = handler;
608 eclass = ExprClass.Value;
612 public override Expression DoResolve (EmitContext ec)
614 // Create the proxy class type.
615 handler.MakeEnumeratorProxy ();
617 if (handler.return_type == TypeManager.ienumerable_type)
618 handler.MakeEnumerableProxy ();
620 type = handler.return_type;
624 public override Expression ResolveAsTypeStep (EmitContext ec)
626 return DoResolve (ec);
629 public override void Emit (EmitContext ec)
631 handler.LoadArgs (ec.ig);
633 if (handler.return_type == TypeManager.ienumerable_type)
634 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerable_proxy_constructor);
636 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerator_proxy_constructor);
641 // This return statement tricks return into not flagging an error for being
642 // used in a Yields method
644 class NoCheckReturn : Return {
645 public NoCheckReturn (Expression expr, Location loc) : base (expr, loc)
649 public override bool Resolve (EmitContext ec)
651 ec.InIterator = false;
652 bool ret_val = base.Resolve (ec);
653 ec.InIterator = true;
660 // Returns the new block for the method, or null on failure
662 public Block Setup (Block block)
664 if (return_type != TypeManager.ienumerator_type &&
665 return_type != TypeManager.ienumerable_type){
667 -205, loc, String.Format (
668 "The method `{0}' contains a yield statement, but has an invalid return type for an iterator",
673 for (int i = 0; i < parameters.Count; i++){
674 Parameter.Modifier mod = parameters.ParameterModifier (i);
675 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
676 Report.Error (-207, loc, String.Format (
677 "Parameter {0} of `{1}' is {2} and not allowed for an iterator method",
678 i+1, name, parameters.ParameterDesc (i)));
683 original_block = block;
684 Block b = new Block (null);
686 // return new InnerClass ()
687 b.AddStatement (new NoCheckReturn (new NewInnerType (this, loc), loc));