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.Goto ();
103 protected override bool DoEmit (EmitContext ec)
105 IteratorHandler.Current.EmitYieldBreak (ec.ig, true);
110 public class IteratorHandler {
112 // Points to the current iterator handler, will be probed by
113 // Yield and YieldBreak to get their context information
115 public static IteratorHandler Current;
118 // The typebuilder to the proxy class we create
120 TypeBuilder enumerator_proxy_class;
121 TypeBuilder enumerable_proxy_class;
124 // The type of this iterator, object by default.
126 public Type IteratorType;
129 // The members we create on the proxy class
131 MethodBuilder move_next_method;
132 MethodBuilder reset_method;
133 MethodBuilder get_current_method;
134 MethodBuilder dispose_method;
135 MethodBuilder getenumerator_method;
136 PropertyBuilder current_property;
137 ConstructorBuilder enumerator_proxy_constructor;
138 ConstructorBuilder enumerable_proxy_constructor;
141 // The PC for the state machine.
143 FieldBuilder pc_field;
146 // The value computed for Current
148 FieldBuilder current_field;
151 // Used to reference fields on the container class (instance methods)
153 public FieldBuilder this_field;
154 public FieldBuilder enumerable_this_field;
157 // References the parameters
160 public FieldBuilder [] parameter_fields;
161 FieldBuilder [] enumerable_parameter_fields;
164 // The state as we generate the iterator
166 ArrayList resume_labels = new ArrayList ();
170 // Context from the original method
173 TypeContainer container;
176 InternalParameters parameters;
177 Block original_block;
181 static int proxy_count;
182 string MakeProxyName ()
184 return String.Format ("__Proxy_{0}", proxy_count++);
187 public void EmitYieldBreak (ILGenerator ig, bool add_return)
189 ig.Emit (OpCodes.Ldarg_0);
190 IntConstant.EmitInt (ig, -1);
191 ig.Emit (OpCodes.Stfld, pc_field);
193 ig.Emit (OpCodes.Ldc_I4_0);
194 ig.Emit (OpCodes.Ret);
198 void EmitThrowInvalidOp (ILGenerator ig)
200 ig.Emit (OpCodes.Newobj, TypeManager.invalid_operation_ctor);
201 ig.Emit (OpCodes.Throw);
204 void Create_MoveNext ()
206 move_next_method = enumerator_proxy_class.DefineMethod (
207 "System.IEnumerator.MoveNext",
208 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
209 MethodAttributes.Virtual,
210 CallingConventions.HasThis, TypeManager.bool_type, TypeManager.NoTypes);
211 enumerator_proxy_class.DefineMethodOverride (move_next_method, TypeManager.bool_movenext_void);
213 ILGenerator ig = move_next_method.GetILGenerator ();
214 EmitContext ec = new EmitContext (
216 TypeManager.void_type, modifiers);
218 Label dispatcher = ig.DefineLabel ();
219 ig.Emit (OpCodes.Br, dispatcher);
220 Label entry_point = ig.DefineLabel ();
221 ig.MarkLabel (entry_point);
222 resume_labels.Add (entry_point);
225 SymbolWriter sw = CodeGen.SymbolWriter;
226 if ((sw != null) && !Location.IsNull (loc) && !Location.IsNull (original_block.EndLocation)) {
227 sw.OpenMethod (container, move_next_method, loc, original_block.EndLocation);
229 ec.EmitTopBlock (original_block, parameters, loc);
233 ec.EmitTopBlock (original_block, parameters, loc);
237 EmitYieldBreak (ig, true);
240 // FIXME: Split the switch in blocks that can be consumed by switch.
242 ig.MarkLabel (dispatcher);
244 Label [] labels = new Label [resume_labels.Count];
245 resume_labels.CopyTo (labels);
246 ig.Emit (OpCodes.Ldarg_0);
247 ig.Emit (OpCodes.Ldfld, pc_field);
248 ig.Emit (OpCodes.Switch, labels);
249 ig.Emit (OpCodes.Ldc_I4_0);
250 ig.Emit (OpCodes.Ret);
254 // Invoked when a local variable declaration needs to be mapped to
255 // a field in our proxy class
257 // Prefixes registered:
258 // v_ for EmitContext.MapVariable
261 public FieldBuilder MapVariable (string pfx, string name, Type t)
263 return enumerator_proxy_class.DefineField (pfx + name, t, FieldAttributes.Public);
268 reset_method = enumerator_proxy_class.DefineMethod (
269 "System.IEnumerator.Reset",
270 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
271 MethodAttributes.Virtual,
272 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
273 enumerator_proxy_class.DefineMethodOverride (reset_method, TypeManager.void_reset_void);
274 ILGenerator ig = reset_method.GetILGenerator ();
275 EmitThrowInvalidOp (ig);
278 void Create_Current ()
280 get_current_method = enumerator_proxy_class.DefineMethod (
281 "System.IEnumerator.get_Current",
282 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
283 MethodAttributes.NewSlot | MethodAttributes.Virtual,
284 CallingConventions.HasThis, TypeManager.object_type, TypeManager.NoTypes);
285 enumerator_proxy_class.DefineMethodOverride (get_current_method, TypeManager.object_getcurrent_void);
287 current_property = enumerator_proxy_class.DefineProperty (
289 PropertyAttributes.RTSpecialName | PropertyAttributes.SpecialName,
290 TypeManager.object_type, null);
292 current_property.SetGetMethod (get_current_method);
294 ILGenerator ig = get_current_method.GetILGenerator ();
296 ig.Emit (OpCodes.Ldarg_0);
297 ig.Emit (OpCodes.Ldfld, pc_field);
298 ig.Emit (OpCodes.Ldc_I4_0);
299 Label return_current = ig.DefineLabel ();
300 ig.Emit (OpCodes.Bgt, return_current);
301 EmitThrowInvalidOp (ig);
303 ig.MarkLabel (return_current);
304 ig.Emit (OpCodes.Ldarg_0);
305 ig.Emit (OpCodes.Ldfld, current_field);
306 ig.Emit (OpCodes.Ret);
309 void Create_Dispose ()
311 dispose_method = enumerator_proxy_class.DefineMethod (
312 "System.IDisposable.Dispose",
313 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
314 MethodAttributes.NewSlot | MethodAttributes.Virtual,
315 CallingConventions.HasThis, TypeManager.void_type, TypeManager.NoTypes);
316 enumerator_proxy_class.DefineMethodOverride (dispose_method, TypeManager.void_dispose_void);
317 ILGenerator ig = dispose_method.GetILGenerator ();
319 EmitYieldBreak (ig, false);
320 ig.Emit (OpCodes.Ret);
323 void Create_GetEnumerator ()
325 getenumerator_method = enumerable_proxy_class.DefineMethod (
326 "IEnumerable.GetEnumerator",
327 MethodAttributes.HideBySig | MethodAttributes.SpecialName |
328 MethodAttributes.NewSlot | MethodAttributes.Virtual,
329 CallingConventions.HasThis, TypeManager.ienumerator_type, TypeManager.NoTypes);
331 enumerable_proxy_class.DefineMethodOverride (getenumerator_method, TypeManager.ienumerable_getenumerator_void);
332 ILGenerator ig = getenumerator_method.GetILGenerator ();
334 if (enumerable_this_field != null){
335 ig.Emit (OpCodes.Ldarg_0);
336 ig.Emit (OpCodes.Ldfld, enumerable_this_field);
338 for (int i = 0; i < parameters.Count; i++){
339 ig.Emit (OpCodes.Ldarg_0);
340 ig.Emit (OpCodes.Ldfld, enumerable_parameter_fields [i]);
342 ig.Emit (OpCodes.Newobj, (ConstructorInfo) enumerator_proxy_constructor);
343 ig.Emit (OpCodes.Ret);
346 void LoadArgs (ILGenerator ig)
348 int count = parameters.Count;
349 if ((modifiers & Modifiers.STATIC) == 0)
352 for (int i = 0; i < count; i++)
353 ParameterReference.EmitLdArg (ig, i);
357 // Called back from Yield
359 public void MarkYield (EmitContext ec, Expression expr)
361 ILGenerator ig = ec.ig;
363 // Store the new current
364 ig.Emit (OpCodes.Ldarg_0);
366 ig.Emit (OpCodes.Stfld, current_field);
370 ig.Emit (OpCodes.Ldarg_0);
371 IntConstant.EmitInt (ig, pc);
372 ig.Emit (OpCodes.Stfld, pc_field);
375 ig.Emit (OpCodes.Ldc_I4_1);
376 ig.Emit (OpCodes.Ret);
378 Label resume_point = ig.DefineLabel ();
379 ig.MarkLabel (resume_point);
380 resume_labels.Add (resume_point);
383 void ComputeConstructorTypes (out Type [] constructor_types, out Parameters constructor_parameters)
385 bool is_static = (modifiers & Modifiers.STATIC) != 0;
387 if (is_static && parameters.Count == 0){
388 constructor_types = TypeManager.NoTypes;
389 constructor_parameters = Parameters.EmptyReadOnlyParameters;
393 int count = (is_static ? 0 : 1) + parameters.Count;
394 constructor_types = new Type [count];
395 Parameter [] pars = new Parameter [count];
396 constructor_parameters = new Parameters (pars, null, loc);
400 constructor_types [0] = container.TypeBuilder;
402 Parameter THIS = new Parameter (
403 new TypeExpr (container.TypeBuilder, loc), "this", Parameter.Modifier.NONE, null);
408 for (int j = 0; j < parameters.Count; j++, i++){
409 Type partype = parameters.ParameterType (j);
411 pars [i] = new Parameter (new TypeExpr (partype, loc),
412 parameters.ParameterName (j),
413 Parameter.Modifier.NONE, null);
414 constructor_types [i] = partype;
419 // Creates the IEnumerator Proxy class
421 void MakeEnumeratorProxy ()
423 Type [] proxy_base_interfaces = new Type [2];
424 proxy_base_interfaces [0] = TypeManager.ienumerator_type;
425 proxy_base_interfaces [1] = TypeManager.idisposable_type;
426 TypeBuilder container_builder = container.TypeBuilder;
431 enumerator_proxy_class = container_builder.DefineNestedType (
432 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
433 TypeManager.object_type, proxy_base_interfaces);
435 TypeManager.RegisterBuilder (enumerator_proxy_class, proxy_base_interfaces);
440 pc_field = enumerator_proxy_class.DefineField ("PC", TypeManager.int32_type, FieldAttributes.Private);
441 current_field = enumerator_proxy_class.DefineField ("current", IteratorType, FieldAttributes.Private);
442 if ((modifiers & Modifiers.STATIC) == 0)
443 this_field = enumerator_proxy_class.DefineField ("THIS", container.TypeBuilder, FieldAttributes.Private);
445 parameter_fields = new FieldBuilder [parameters.Count];
446 for (int i = 0; i < parameters.Count; i++){
447 parameter_fields [i] = enumerator_proxy_class.DefineField (
448 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
449 parameters.ParameterType (i), FieldAttributes.Private);
453 // Define a constructor
455 // FIXME: currently its parameterless
456 Type [] constructor_types;
457 Parameters constructor_parameters;
459 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
461 enumerator_proxy_constructor = enumerator_proxy_class.DefineConstructor (
462 MethodAttributes.Public | MethodAttributes.HideBySig |
463 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
464 CallingConventions.HasThis, constructor_types);
465 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
466 TypeManager.RegisterMethod (enumerator_proxy_constructor, parameter_info, constructor_types);
471 ILGenerator ig = enumerator_proxy_constructor.GetILGenerator ();
472 ig.Emit (OpCodes.Ldarg_0);
473 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
476 if (this_field != null){
478 ig.Emit (OpCodes.Ldarg_0);
479 ig.Emit (OpCodes.Ldarg_1);
480 ig.Emit (OpCodes.Stfld, this_field);
484 for (int i = 0; i < parameters.Count; i++){
485 ig.Emit (OpCodes.Ldarg_0);
486 ParameterReference.EmitLdArg (ig, i + arg_start);
487 ig.Emit (OpCodes.Stfld, parameter_fields [i]);
489 ig.Emit (OpCodes.Ret);
493 // Creates the IEnumerable proxy class
495 void MakeEnumerableProxy ()
497 TypeBuilder container_builder = container.TypeBuilder;
498 Type [] proxy_base_interfaces = new Type [1];
499 proxy_base_interfaces [0] = TypeManager.ienumerable_type;
502 // Creates the Enumerable proxy class.
504 enumerable_proxy_class = container_builder.DefineNestedType (
505 MakeProxyName (), TypeAttributes.AutoLayout | TypeAttributes.Class |TypeAttributes.NestedPublic,
506 TypeManager.object_type, proxy_base_interfaces);
511 Type [] constructor_types;
512 Parameters constructor_parameters;
514 ComputeConstructorTypes (out constructor_types, out constructor_parameters);
515 if ((modifiers & Modifiers.STATIC) == 0){
516 enumerable_this_field = enumerable_proxy_class.DefineField (
517 "THIS", container.TypeBuilder, FieldAttributes.Private);
519 enumerable_parameter_fields = new FieldBuilder [parameters.Count];
520 for (int i = 0; i < parameters.Count; i++){
521 enumerable_parameter_fields [i] = enumerable_proxy_class.DefineField (
522 String.Format ("p{0}_{1}", i, parameters.ParameterName (i)),
523 parameters.ParameterType (i), FieldAttributes.Private);
526 enumerable_proxy_constructor = enumerable_proxy_class.DefineConstructor (
527 MethodAttributes.Public | MethodAttributes.HideBySig |
528 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
529 CallingConventions.HasThis, constructor_types);
530 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
531 TypeManager.RegisterMethod (enumerable_proxy_constructor, parameter_info, constructor_types);
533 ILGenerator ig = enumerable_proxy_constructor.GetILGenerator ();
534 ig.Emit (OpCodes.Ldarg_0);
535 ig.Emit (OpCodes.Call, TypeManager.object_ctor);
538 if (enumerable_this_field != null){
539 ig.Emit (OpCodes.Ldarg_0);
540 ig.Emit (OpCodes.Ldarg_1);
541 ig.Emit (OpCodes.Stfld, enumerable_this_field);
546 for (int i = 0; i < parameters.Count; i++){
547 ig.Emit (OpCodes.Ldarg_0);
548 ParameterReference.EmitLdArg (ig, i + first_arg);
549 ig.Emit (OpCodes.Stfld, enumerable_parameter_fields [i]);
551 ig.Emit (OpCodes.Ret);
555 // Populates the Enumerator Proxy class
557 void PopulateProxy ()
559 RootContext.RegisterHelperClass (enumerator_proxy_class);
566 if (return_type == TypeManager.ienumerable_type){
567 Create_GetEnumerator ();
568 RootContext.RegisterHelperClass (enumerable_proxy_class);
574 // This is invoked by the EmitCode hook
576 void SetupIterator ()
584 public IteratorHandler (string name, TypeContainer container, Type return_type, Type [] param_types,
585 InternalParameters parameters, int modifiers, Location loc)
588 this.container = container;
589 this.return_type = return_type;
590 this.param_types = param_types;
591 this.parameters = parameters;
592 this.modifiers = modifiers;
595 IteratorType = TypeManager.object_type;
597 RootContext.EmitCodeHook += new RootContext.Hook (SetupIterator);
601 // This class is just an expression that evaluates to a type, and the
602 // type is our internal proxy class. Used in the generated new body
603 // of the original method
605 class NewInnerType : Expression {
606 IteratorHandler handler;
608 public NewInnerType (IteratorHandler handler, Location l)
610 this.handler = handler;
611 eclass = ExprClass.Value;
615 public override Expression DoResolve (EmitContext ec)
617 // Create the proxy class type.
618 handler.MakeEnumeratorProxy ();
620 if (handler.return_type == TypeManager.ienumerable_type)
621 handler.MakeEnumerableProxy ();
623 type = handler.return_type;
627 public override Expression ResolveAsTypeStep (EmitContext ec)
629 return DoResolve (ec);
632 public override void Emit (EmitContext ec)
634 handler.LoadArgs (ec.ig);
636 if (handler.return_type == TypeManager.ienumerable_type)
637 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerable_proxy_constructor);
639 ec.ig.Emit (OpCodes.Newobj, (ConstructorInfo) handler.enumerator_proxy_constructor);
644 // This return statement tricks return into not flagging an error for being
645 // used in a Yields method
647 class NoCheckReturn : Return {
648 public NoCheckReturn (Expression expr, Location loc) : base (expr, loc)
652 public override bool Resolve (EmitContext ec)
654 ec.InIterator = false;
655 bool ret_val = base.Resolve (ec);
656 ec.InIterator = true;
663 // Returns the new block for the method, or null on failure
665 public Block Setup (Block block)
667 if (return_type != TypeManager.ienumerator_type &&
668 return_type != TypeManager.ienumerable_type){
670 -205, loc, String.Format (
671 "The method `{0}' contains a yield statement, but has an invalid return type for an iterator",
676 for (int i = 0; i < parameters.Count; i++){
677 Parameter.Modifier mod = parameters.ParameterModifier (i);
678 if ((mod & (Parameter.Modifier.REF | Parameter.Modifier.OUT)) != 0){
679 Report.Error (-207, loc, String.Format (
680 "Parameter {0} of `{1}' is {2} and not allowed for an iterator method",
681 i+1, name, parameters.ParameterDesc (i)));
686 original_block = block;
687 Block b = new Block (null);
689 // return new InnerClass ()
690 b.AddStatement (new NoCheckReturn (new NewInnerType (this, loc), loc));