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 void DoEmit (EmitContext ec)
81 IteratorHandler.Current.MarkYield (ec, expr);
85 public class YieldBreak : Statement {
87 public YieldBreak (Location l)
92 public override bool Resolve (EmitContext ec)
94 if (!Yield.CheckContext (ec, loc))
97 ec.CurrentBranching.CurrentUsageVector.Goto ();
101 protected override void DoEmit (EmitContext ec)
103 IteratorHandler.Current.EmitYieldBreak (ec.ig, true);
107 public class IteratorHandler {
109 // Points to the current iterator handler, will be probed by
110 // Yield and YieldBreak to get their context information
112 public static IteratorHandler Current;
115 // The typebuilder to the proxy class we create
117 TypeBuilder enumerator_proxy_class;
118 TypeBuilder enumerable_proxy_class;
121 // The type of this iterator, object by default.
123 public Type IteratorType;
126 // The members we create on the proxy class
128 MethodBuilder move_next_method;
129 MethodBuilder reset_method;
130 MethodBuilder get_current_method;
131 MethodBuilder dispose_method;
132 MethodBuilder getenumerator_method;
133 PropertyBuilder current_property;
134 ConstructorBuilder enumerator_proxy_constructor;
135 ConstructorBuilder enumerable_proxy_constructor;
138 // The PC for the state machine.
140 FieldBuilder pc_field;
143 // The value computed for Current
145 FieldBuilder current_field;
148 // Used to reference fields on the container class (instance methods)
150 public FieldBuilder this_field;
151 public FieldBuilder enumerable_this_field;
154 // References the parameters
157 public FieldBuilder [] parameter_fields;
158 FieldBuilder [] enumerable_parameter_fields;
161 // The state as we generate the iterator
163 ArrayList resume_labels = new ArrayList ();
167 // Context from the original method
170 TypeContainer container;
173 InternalParameters parameters;
174 Block original_block;
178 static int proxy_count;
179 string MakeProxyName ()
181 return String.Format ("__Proxy_{0}", proxy_count++);
184 public void EmitYieldBreak (ILGenerator ig, bool add_return)
186 ig.Emit (OpCodes.Ldarg_0);
187 IntConstant.EmitInt (ig, -1);
188 ig.Emit (OpCodes.Stfld, pc_field);
190 ig.Emit (OpCodes.Ldc_I4_0);
191 ig.Emit (OpCodes.Ret);
195 void EmitThrowInvalidOp (ILGenerator ig)
197 ig.Emit (OpCodes.Newobj, TypeManager.invalid_operation_ctor);
198 ig.Emit (OpCodes.Throw);
201 void Create_MoveNext ()
203 move_next_method = enumerator_proxy_class.DefineMethod (
204 "System.IEnumerator.MoveNext",
205 MethodAttributes.HideBySig | MethodAttributes.NewSlot |
206 MethodAttributes.Virtual,
207 CallingConventions.HasThis, TypeManager.bool_type, TypeManager.NoTypes);
208 enumerator_proxy_class.DefineMethodOverride (move_next_method, TypeManager.bool_movenext_void);
210 ILGenerator ig = move_next_method.GetILGenerator ();
211 EmitContext ec = new EmitContext (
213 TypeManager.void_type, modifiers);
215 Label dispatcher = ig.DefineLabel ();
216 ig.Emit (OpCodes.Br, dispatcher);
217 Label entry_point = ig.DefineLabel ();
218 ig.MarkLabel (entry_point);
219 resume_labels.Add (entry_point);
222 SymbolWriter sw = CodeGen.SymbolWriter;
223 if ((sw != null) && !Location.IsNull (loc) && !Location.IsNull (original_block.EndLocation)) {
224 sw.OpenMethod (container, move_next_method, loc, original_block.EndLocation);
226 ec.EmitTopBlock (original_block, parameters, loc);
230 ec.EmitTopBlock (original_block, parameters, loc);
234 EmitYieldBreak (ig, true);
237 // FIXME: Split the switch in blocks that can be consumed by switch.
239 ig.MarkLabel (dispatcher);
241 Label [] labels = new Label [resume_labels.Count];
242 resume_labels.CopyTo (labels);
243 ig.Emit (OpCodes.Ldarg_0);
244 ig.Emit (OpCodes.Ldfld, pc_field);
245 ig.Emit (OpCodes.Switch, labels);
246 ig.Emit (OpCodes.Ldc_I4_0);
247 ig.Emit (OpCodes.Ret);
251 // Invoked when a local variable declaration needs to be mapped to
252 // a field in our proxy class
254 // Prefixes registered:
255 // v_ for EmitContext.MapVariable
258 public FieldBuilder MapVariable (string pfx, string name, Type t)
260 return enumerator_proxy_class.DefineField (pfx + 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 TypeExpression (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 TypeExpression (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 TypeExpr [] proxy_base_interfaces = new TypeExpr [2];
421 proxy_base_interfaces [0] = new TypeExpression (TypeManager.ienumerator_type, loc);
422 proxy_base_interfaces [1] = new TypeExpression (TypeManager.idisposable_type, loc);
423 Type [] proxy_base_itypes = new Type [2];
424 proxy_base_itypes [0] = TypeManager.ienumerator_type;
425 proxy_base_itypes [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_itypes);
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));