2008-11-05 Francisco Figueiredo Jr. <francisco@npgsql.org>
[mono.git] / mcs / mcs / iterators.cs
1 //
2 // iterators.cs: Support for implementing iterators
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2003 Ximian, Inc.
10 // Copyright 2003-2008 Novell, Inc.
11 //
12
13 // TODO:
14 //    Flow analysis for Yield.
15 //
16
17 using System;
18 using System.Collections;
19 using System.Reflection;
20 using System.Reflection.Emit;
21
22 namespace Mono.CSharp {
23
24         public class Yield : ResumableStatement {
25                 Expression expr;
26                 bool unwind_protect;
27
28                 int resume_pc;
29
30                 public Yield (Expression expr, Location l)
31                 {
32                         this.expr = expr;
33                         loc = l;
34                 }
35
36                 public static bool CheckContext (EmitContext ec, Location loc)
37                 {
38                         for (Block block = ec.CurrentBlock; block != null; block = block.Parent) {
39                                 if (!block.Unsafe)
40                                         continue;
41
42                                 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
43                                 return false;
44                         }
45
46                         //
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.
49                         //
50                         if (!ec.CurrentAnonymousMethod.IsIterator) {
51                                 Report.Error (1621, loc,
52                                               "The yield statement cannot be used inside " +
53                                               "anonymous method blocks");
54                                 return false;
55                         }
56
57                         return true;
58                 }
59
60                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
61                 {
62                         expr.MutateHoistedGenericType (storey);
63                 }
64                 
65                 public override bool Resolve (EmitContext ec)
66                 {
67                         expr = expr.Resolve (ec);
68                         if (expr == null)
69                                 return false;
70
71                         Report.Debug (64, "RESOLVE YIELD #1", this, ec, expr, expr.GetType (),
72                                       ec.CurrentAnonymousMethod, ec.CurrentIterator);
73
74                         if (!CheckContext (ec, loc))
75                                 return false;
76
77                         Iterator iterator = ec.CurrentIterator;
78                         if (expr.Type != iterator.OriginalIteratorType) {
79                                 expr = Convert.ImplicitConversionRequired (
80                                         ec, expr, iterator.OriginalIteratorType, loc);
81                                 if (expr == null)
82                                         return false;
83                         }
84
85                         if (!ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
86                                 unwind_protect = ec.CurrentBranching.AddResumePoint (this, loc, out resume_pc);
87
88                         return true;
89                 }
90
91                 protected override void DoEmit (EmitContext ec)
92                 {
93                         ec.CurrentIterator.MarkYield (ec, expr, resume_pc, unwind_protect, resume_point);
94                 }
95
96                 protected override void CloneTo (CloneContext clonectx, Statement t)
97                 {
98                         Yield target = (Yield) t;
99
100                         target.expr = expr.Clone (clonectx);
101                 }
102         }
103
104         public class YieldBreak : ExitStatement {
105                 public YieldBreak (Location l)
106                 {
107                         loc = l;
108                 }
109
110                 public override void Error_FinallyClause ()
111                 {
112                         Report.Error (1625, loc, "Cannot yield in the body of a finally clause");
113                 }
114
115                 protected override void CloneTo (CloneContext clonectx, Statement target)
116                 {
117                         throw new NotSupportedException ();
118                 }
119
120                 protected override bool DoResolve (EmitContext ec)
121                 {
122                         return Yield.CheckContext (ec, loc);
123                 }
124
125                 protected override void DoEmit (EmitContext ec)
126                 {
127                         ec.CurrentIterator.EmitYieldBreak (ec.ig, unwind_protect);
128                 }
129
130                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
131                 {
132                         // nothing to do
133                 }
134         }
135
136         //
137         // Wraps method block into iterator wrapper block
138         //
139         class IteratorStatement : Statement
140         {
141                 Iterator iterator;
142                 ExplicitBlock original_block;
143
144                 public IteratorStatement (Iterator iterator, ExplicitBlock original_block)
145                 {
146                         this.iterator = iterator;
147                         this.original_block = original_block;
148                         this.loc = iterator.Location;
149                 }
150
151                 protected override void CloneTo (CloneContext clonectx, Statement target)
152                 {
153                         IteratorStatement t = (IteratorStatement) target;
154                         t.original_block = (ExplicitBlock) original_block.Clone (clonectx);
155                         t.iterator = (Iterator) iterator.Clone (clonectx);
156                 }
157
158                 public override bool Resolve (EmitContext ec)
159                 {
160                         ec.StartFlowBranching (iterator);
161                         bool ok = original_block.Resolve (ec);
162                         ec.EndFlowBranching ();
163                         return ok;
164                 }
165
166                 protected override void DoEmit (EmitContext ec)
167                 {
168                         iterator.EmitMoveNext (ec, original_block);
169                 }
170
171                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
172                 {
173                         original_block.MutateHoistedGenericType (storey);
174                         iterator.MutateHoistedGenericType (storey);
175                 }
176         }
177
178         public class IteratorStorey : AnonymousMethodStorey
179         {
180                 class IteratorMethod : Method
181                 {
182                         readonly IteratorStorey host;
183
184                         public IteratorMethod (IteratorStorey host, FullNamedExpression returnType, int mod, MemberName name)
185                                 : base (host, null, returnType, mod | Modifiers.DEBUGGER_HIDDEN | Modifiers.COMPILER_GENERATED,
186                                   name, Parameters.EmptyReadOnlyParameters, null)
187                         {
188                                 this.host = host;
189
190                                 Block = new ToplevelBlock (host.Iterator.Container.Toplevel, null, Location);
191                         }
192
193                         public override EmitContext CreateEmitContext (DeclSpace tc, ILGenerator ig)
194                         {
195                                 EmitContext ec = new EmitContext (
196                                         this, tc, this.ds, Location, ig, MemberType, ModFlags, false);
197
198                                 ec.CurrentAnonymousMethod = host.Iterator;
199                                 return ec;
200                         }
201                 }
202
203                 class GetEnumeratorMethod : IteratorMethod
204                 {
205                         sealed class GetEnumeratorStatement : Statement
206                         {
207                                 IteratorStorey host;
208                                 Expression type;
209
210                                 Expression new_storey;
211
212                                 public GetEnumeratorStatement (IteratorStorey host, Expression type)
213                                 {
214                                         this.host = host;
215                                         this.type = type;
216                                         loc = host.Location;
217                                 }
218
219                                 protected override void CloneTo (CloneContext clonectx, Statement target)
220                                 {
221                                         throw new NotSupportedException ();
222                                 }
223
224                                 public override bool Resolve (EmitContext ec)
225                                 {
226                                         type = type.ResolveAsTypeTerminal (ec, false);
227                                         if ((type == null) || (type.Type == null))
228                                                 return false;
229
230                                         TypeExpression storey_type_expr = new TypeExpression (host.TypeBuilder, loc);
231                                         ArrayList init = null;
232                                         if (host.hoisted_this != null) {
233                                                 init = new ArrayList (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
234                                                 HoistedThis ht = host.hoisted_this;
235                                                 FieldExpr from = new FieldExpr (ht.Field.FieldBuilder, loc);
236                                                 from.InstanceExpression = CompilerGeneratedThis.Instance;
237                                                 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
238                                         }
239
240                                         if (host.hoisted_params != null) {
241                                                 if (init == null)
242                                                         init = new ArrayList (host.HoistedParameters.Count);
243
244                                                 for (int i = 0; i < host.hoisted_params.Count; ++i) {
245                                                         HoistedParameter hp = (HoistedParameter) host.hoisted_params [i];
246                                                         HoistedParameter hp_cp = (HoistedParameter) host.hoisted_params_copy [i];
247
248                                                         FieldExpr from = new FieldExpr (hp_cp.Field.FieldBuilder, loc);
249                                                         from.InstanceExpression = CompilerGeneratedThis.Instance;
250
251                                                         init.Add (new ElementInitializer (hp.Field.Name, from, loc));
252                                                 }
253                                         }
254
255                                         if (init != null) {
256                                                 new_storey = new NewInitialize (storey_type_expr, new ArrayList (0),
257                                                         new CollectionOrObjectInitializers (init, loc), loc);
258                                         } else {
259                                                 new_storey = new New (storey_type_expr, new ArrayList (0), loc);
260                                         }
261
262                                         new_storey = new_storey.Resolve (ec);
263                                         if (new_storey != null)
264                                                 new_storey = Convert.ImplicitConversionRequired (ec, new_storey, type.Type, loc);
265
266                                         if (TypeManager.int_interlocked_compare_exchange == null) {
267                                                 Type t = TypeManager.CoreLookupType ("System.Threading", "Interlocked", Kind.Class, true);
268                                                 if (t != null) {
269                                                         TypeManager.int_interlocked_compare_exchange = TypeManager.GetPredefinedMethod (
270                                                                 t, "CompareExchange", loc, TypeManager.int32_type,
271                                                                 TypeManager.int32_type, TypeManager.int32_type);
272                                                 }
273                                         }
274
275                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
276                                         return true;
277                                 }
278
279                                 protected override void DoEmit (EmitContext ec)
280                                 {
281                                         ILGenerator ig = ec.ig;
282                                         Label label_init = ig.DefineLabel ();
283
284                                         ig.Emit (OpCodes.Ldarg_0);
285                                         ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
286                                         IntConstant.EmitInt (ig, (int) Iterator.State.Start);
287                                         IntConstant.EmitInt (ig, (int) Iterator.State.Uninitialized);
288                                         ig.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange);
289
290                                         IntConstant.EmitInt (ig, (int) Iterator.State.Uninitialized);
291                                         ig.Emit (OpCodes.Bne_Un_S, label_init);
292
293                                         ig.Emit (OpCodes.Ldarg_0);
294                                         ig.Emit (OpCodes.Ret);
295
296                                         ig.MarkLabel (label_init);
297
298                                         new_storey.Emit (ec);
299                                         ig.Emit (OpCodes.Ret);
300                                 }
301
302                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
303                                 {
304                                         throw new NotSupportedException ();
305                                 }
306                         }
307
308                         public GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
309                                 : base (host, returnType, 0, name)
310                         {
311                                 Block.AddStatement (new GetEnumeratorStatement (host, type_name));
312                         }
313                 }
314
315                 class DisposeMethod : IteratorMethod
316                 {
317                         sealed class DisposeMethodStatement : Statement
318                         {
319                                 Iterator iterator;
320
321                                 public DisposeMethodStatement (Iterator iterator)
322                                 {
323                                         this.iterator = iterator;
324                                         this.loc = iterator.Location;
325                                 }
326
327                                 protected override void CloneTo (CloneContext clonectx, Statement target)
328                                 {
329                                         throw new NotSupportedException ();
330                                 }
331
332                                 public override bool Resolve (EmitContext ec)
333                                 {
334                                         return true;
335                                 }
336
337                                 protected override void DoEmit (EmitContext ec)
338                                 {
339                                         iterator.EmitDispose (ec);
340                                 }
341
342                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
343                                 {
344                                         throw new NotSupportedException ();
345                                 }
346                         }
347
348                         public DisposeMethod (IteratorStorey host)
349                                 : base (host, TypeManager.system_void_expr, Modifiers.PUBLIC, new MemberName ("Dispose", host.Location))
350                         {
351                                 host.AddMethod (this);
352
353                                 Block = new ToplevelBlock (host.Iterator.Container, null, Location);
354                                 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
355                         }
356                 }
357
358                 //
359                 // Uses Method as method info
360                 //
361                 class DynamicMethodGroupExpr : MethodGroupExpr
362                 {
363                         readonly Method method;
364
365                         public DynamicMethodGroupExpr (Method method, Location loc)
366                                 : base (null, loc)
367                         {
368                                 this.method = method;
369                         }
370
371                         public override Expression DoResolve (EmitContext ec)
372                         {
373                                 Methods = new MethodBase [] { method.MethodBuilder };
374                                 type = method.Parent.TypeBuilder;
375                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
376                                 return base.DoResolve (ec);
377                         }
378                 }
379
380                 public readonly Iterator Iterator;
381
382                 TypeExpr iterator_type_expr;
383                 Field pc_field;
384                 Field current_field;
385
386                 TypeExpr enumerator_type;
387                 TypeExpr enumerable_type;
388 #if GMCS_SOURCE
389                 TypeArguments generic_args;
390                 TypeExpr generic_enumerator_type;
391                 TypeExpr generic_enumerable_type;
392 #else
393                 const TypeArguments generic_args = null;
394 #endif
395
396                 ArrayList hoisted_params_copy;
397                 int local_name_idx;
398
399                 public IteratorStorey (Iterator iterator)
400                         : base (iterator.Container.Toplevel, iterator.Host,
401                           iterator.OriginalMethod as MemberBase, iterator.GenericMethod, "Iterator")
402                 {
403                         this.Iterator = iterator;
404                         HasHoistedVariables = true;
405                 }
406
407                 public Field PC {
408                         get { return pc_field; }
409                 }
410
411                 public Field CurrentField {
412                         get { return current_field; }
413                 }
414
415                 public ArrayList HoistedParameters {
416                         get { return hoisted_params; }
417                 }
418
419                 protected override TypeExpr [] ResolveBaseTypes (out TypeExpr base_class)
420                 {
421                         iterator_type_expr = new TypeExpression (MutateType (Iterator.OriginalIteratorType), Location);
422
423 #if GMCS_SOURCE
424                         generic_args = new TypeArguments (Location);
425                         generic_args.Add (iterator_type_expr);
426 #endif
427
428                         ArrayList list = new ArrayList ();
429                         if (Iterator.IsEnumerable) {
430                                 enumerable_type = new TypeExpression (
431                                         TypeManager.ienumerable_type, Location);
432                                 list.Add (enumerable_type);
433
434 #if GMCS_SOURCE
435                                 generic_enumerable_type = new ConstructedType (
436                                         TypeManager.generic_ienumerable_type,
437                                         generic_args, Location);
438                                 list.Add (generic_enumerable_type);
439 #endif
440                         }
441
442                         enumerator_type = new TypeExpression (
443                                 TypeManager.ienumerator_type, Location);
444                         list.Add (enumerator_type);
445
446                         list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
447
448 #if GMCS_SOURCE
449                         generic_enumerator_type = new ConstructedType (
450                                 TypeManager.generic_ienumerator_type,
451                                 generic_args, Location);
452                         list.Add (generic_enumerator_type);
453 #endif
454
455                         type_bases = list;
456
457                         return base.ResolveBaseTypes (out base_class);
458                 }
459
460                 protected override string GetVariableMangledName (LocalInfo local_info)
461                 {
462                         return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
463                 }
464
465                 public void DefineIteratorMembers ()
466                 {
467                         pc_field = AddCompilerGeneratedField ("$PC", TypeManager.system_int32_expr);
468                         current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
469
470                         if (hoisted_params != null) {
471                                 //
472                                 // Iterators are independent, each GetEnumerator call has to
473                                 // create same enumerator therefore we have to keep original values
474                                 // around for re-initialization
475                                 //
476                                 // TODO: Do it for assigned/modified parameters only
477                                 //
478                                 hoisted_params_copy = new ArrayList (hoisted_params.Count);
479                                 foreach (HoistedParameter hp in hoisted_params) {
480                                         hoisted_params_copy.Add (new HoistedParameter (hp, "<$>" + hp.Field.Name));
481                                 }
482                         }
483
484 #if GMCS_SOURCE
485                         Define_Current (true);
486 #endif
487                         Define_Current (false);
488                         new DisposeMethod (this);
489                         Define_Reset ();
490
491                         if (Iterator.IsEnumerable) {
492                                 MemberName name = new MemberName (
493                                         new MemberName ("System.Collections.IEnumerable", Location), "GetEnumerator", Location);
494
495 #if GMCS_SOURCE
496                                 Method get_enumerator = new IteratorMethod (this, enumerator_type, 0, name);
497
498                                 name = new MemberName (
499                                         new MemberName ("System.Collections.Generic.IEnumerable", generic_args, Location), "GetEnumerator", Location);
500                                 Method gget_enumerator = new GetEnumeratorMethod (this, generic_enumerator_type, name);
501
502                                 //
503                                 // Just call generic GetEnumerator implementation
504                                 //
505                                 get_enumerator.Block.AddStatement (
506                                         new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), new ArrayList (0)), Location));
507
508                                 AddMethod (get_enumerator);
509                                 AddMethod (gget_enumerator);
510 #else
511                                 AddMethod (new GetEnumeratorMethod (this, enumerator_type, name));
512 #endif
513                         }
514                 }
515
516                 protected override void EmitHoistedParameters (EmitContext ec, ArrayList hoisted)
517                 {
518                         base.EmitHoistedParameters (ec, hoisted);
519                         base.EmitHoistedParameters (ec, hoisted_params_copy);
520                 }
521
522                 void Define_Current (bool is_generic)
523                 {
524                         MemberName left;
525                         TypeExpr type;
526
527                         if (is_generic) {
528                                 left = new MemberName (
529                                         "System.Collections.Generic.IEnumerator",
530                                         generic_args, Location);
531                                 type = iterator_type_expr;
532                         } else {
533                                 left = new MemberName ("System.Collections.IEnumerator", Location);
534                                 type = TypeManager.system_object_expr;
535                         }
536
537                         MemberName name = new MemberName (left, "Current", null, Location);
538
539                         ToplevelBlock get_block = new ToplevelBlock (Location);
540                         get_block.AddStatement (new CurrentBlock (this, is_generic));
541
542                         Accessor getter = new Accessor (get_block, 0, null, null, Location);
543
544                         Property current = new Property (
545                                 this, type, Modifiers.DEBUGGER_HIDDEN, name, null, getter, null, false);
546                         AddProperty (current);
547                 }
548
549                 void Define_Reset ()
550                 {
551                         Method reset = new Method (
552                                 this, null, TypeManager.system_void_expr,
553                                 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
554                                 new MemberName ("Reset", Location),
555                                 Parameters.EmptyReadOnlyParameters, null);
556                         AddMethod (reset);
557
558                         reset.Block = new ToplevelBlock (Location);
559                         reset.Block.AddStatement (Create_ThrowNotSupported ());
560                 }
561
562                 Statement Create_ThrowNotSupported ()
563                 {
564                         TypeExpr ex_type = new TypeLookupExpression ("System.NotSupportedException");
565
566                         return new Throw (new New (ex_type, null, Location), Location);
567                 }
568
569                 protected class CurrentBlock : Statement {
570                         IteratorStorey host;
571                         bool is_generic;
572
573                         public CurrentBlock (IteratorStorey host, bool is_generic)
574                         {
575                                 this.host = host;
576                                 this.is_generic = is_generic;
577                                 loc = host.Location;
578                         }
579
580                         protected override void CloneTo (CloneContext clonectx, Statement target)
581                         {
582                                 throw new NotSupportedException ();
583                         }
584
585                         public override bool Resolve (EmitContext ec)
586                         {
587                                 // We emit a 'ret', so prevent the enclosing TopLevelBlock from emitting one too
588                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
589                                 return true;
590                         }
591
592                         protected override void DoEmit (EmitContext ec)
593                         {
594                                 ILGenerator ig = ec.ig;
595
596                                 ig.Emit (OpCodes.Ldarg_0);
597                                 ig.Emit (OpCodes.Ldfld, host.CurrentField.FieldBuilder);
598                                 if (!is_generic)
599                                         ig.Emit (OpCodes.Box, host.CurrentField.MemberType);
600                                 ig.Emit (OpCodes.Ret);
601                         }
602
603                         public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
604                         {
605                                 throw new NotSupportedException ();
606                         }
607                 }
608         }
609
610         //
611         // Iterators are implemented as hidden anonymous block
612         //
613         public class Iterator : AnonymousExpression {
614                 public readonly IMethodData OriginalMethod;
615                 AnonymousMethodMethod method;
616                 public readonly TypeContainer Host;
617                 public readonly bool IsEnumerable;
618
619                 //
620                 // The state as we generate the iterator
621                 //
622                 Label move_next_ok, move_next_error;
623                 LocalBuilder skip_finally, current_pc;
624
625                 public LocalBuilder SkipFinally {
626                         get { return skip_finally; }
627                 }
628
629                 public LocalBuilder CurrentPC {
630                         get { return current_pc; }
631                 }
632
633                 public Block Container {
634                         get { return OriginalMethod.Block; }
635                 }
636
637                 public GenericMethod GenericMethod {
638                         get { return OriginalMethod.GenericMethod; }
639                 }
640
641                 public readonly Type OriginalIteratorType;
642
643                 readonly IteratorStorey IteratorHost;
644
645                 public enum State {
646                         Running = -3, // Used only in CurrentPC, never stored into $PC
647                         Uninitialized = -2,
648                         After = -1,
649                         Start = 0
650                 }
651
652                 public override void AddStoreyReference (AnonymousMethodStorey storey)
653                 {
654                         // do nothing
655                 }
656
657                 public void EmitYieldBreak (ILGenerator ig, bool unwind_protect)
658                 {
659                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
660                 }
661
662                 void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block)
663                 {
664                         ILGenerator ig = ec.ig;
665
666                         ig.Emit (OpCodes.Ldarg_0);
667                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
668
669                         ig.Emit (OpCodes.Ldarg_0);
670                         IntConstant.EmitInt (ig, (int) State.After);
671                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
672
673                         // We only care if the PC is zero (start executing) or non-zero (don't do anything)
674                         ig.Emit (OpCodes.Brtrue, move_next_error);
675
676                         SymbolWriter.StartIteratorBody (ec.ig);
677                         original_block.Emit (ec);
678                         SymbolWriter.EndIteratorBody (ec.ig);
679
680                         ig.MarkLabel (move_next_error);
681                         ig.Emit (OpCodes.Ldc_I4_0);
682                         ig.Emit (OpCodes.Ret);
683                 }
684
685                 internal void EmitMoveNext (EmitContext ec, Block original_block)
686                 {
687                         ILGenerator ig = ec.ig;
688
689                         move_next_ok = ig.DefineLabel ();
690                         move_next_error = ig.DefineLabel ();
691
692                         if (resume_points == null) {
693                                 EmitMoveNext_NoResumePoints (ec, original_block);
694                                 return;
695                         }
696
697                         current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
698                         ig.Emit (OpCodes.Ldarg_0);
699                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
700                         ig.Emit (OpCodes.Stloc, current_pc);
701
702                         // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
703                         ig.Emit (OpCodes.Ldarg_0);
704                         IntConstant.EmitInt (ig, (int) State.After);
705                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
706
707                         Label [] labels = new Label [1 + resume_points.Count];
708                         labels [0] = ig.DefineLabel ();
709
710                         bool need_skip_finally = false;
711                         for (int i = 0; i < resume_points.Count; ++i) {
712                                 ResumableStatement s = (ResumableStatement) resume_points [i];
713                                 need_skip_finally |= s is ExceptionStatement;
714                                 labels [i+1] = s.PrepareForEmit (ec);
715                         }
716
717                         if (need_skip_finally) {
718                                 skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type);
719                                 ig.Emit (OpCodes.Ldc_I4_0);
720                                 ig.Emit (OpCodes.Stloc, skip_finally);
721                         }
722
723                         SymbolWriter.StartIteratorDispatcher (ec.ig);
724                         ig.Emit (OpCodes.Ldloc, current_pc);
725                         ig.Emit (OpCodes.Switch, labels);
726
727                         ig.Emit (OpCodes.Br, move_next_error);
728                         SymbolWriter.EndIteratorDispatcher (ec.ig);
729
730                         ig.MarkLabel (labels [0]);
731
732                         SymbolWriter.StartIteratorBody (ec.ig);
733                         original_block.Emit (ec);
734                         SymbolWriter.EndIteratorBody (ec.ig);
735
736                         SymbolWriter.StartIteratorDispatcher (ec.ig);
737
738                         ig.Emit (OpCodes.Ldarg_0);
739                         IntConstant.EmitInt (ig, (int) State.After);
740                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
741
742                         ig.MarkLabel (move_next_error);
743                         ig.Emit (OpCodes.Ldc_I4_0);
744                         ig.Emit (OpCodes.Ret);
745
746                         ig.MarkLabel (move_next_ok);
747                         ig.Emit (OpCodes.Ldc_I4_1);
748                         ig.Emit (OpCodes.Ret);
749
750                         SymbolWriter.EndIteratorDispatcher (ec.ig);
751                 }
752
753                 public void EmitDispose (EmitContext ec)
754                 {
755                         ILGenerator ig = ec.ig;
756
757                         Label end = ig.DefineLabel ();
758
759                         Label [] labels = null;
760                         int n_resume_points = resume_points == null ? 0 : resume_points.Count;
761                         for (int i = 0; i < n_resume_points; ++i) {
762                                 ResumableStatement s = (ResumableStatement) resume_points [i];
763                                 Label ret = s.PrepareForDispose (ec, end);
764                                 if (ret.Equals (end) && labels == null)
765                                         continue;
766                                 if (labels == null) {
767                                         labels = new Label [resume_points.Count + 1];
768                                         for (int j = 0; j <= i; ++j)
769                                                 labels [j] = end;
770                                 }
771                                 labels [i+1] = ret;
772                         }
773
774                         if (labels != null) {
775                                 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
776                                 ig.Emit (OpCodes.Ldarg_0);
777                                 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
778                                 ig.Emit (OpCodes.Stloc, current_pc);
779                         }
780
781                         ig.Emit (OpCodes.Ldarg_0);
782                         IntConstant.EmitInt (ig, (int) State.After);
783                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
784
785                         if (labels != null) {
786                                 //SymbolWriter.StartIteratorDispatcher (ec.ig);
787                                 ig.Emit (OpCodes.Ldloc, current_pc);
788                                 ig.Emit (OpCodes.Switch, labels);
789                                 //SymbolWriter.EndIteratorDispatcher (ec.ig);
790
791                                 foreach (ResumableStatement s in resume_points)
792                                         s.EmitForDispose (ec, this, end, true);
793                         }
794
795                         ig.MarkLabel (end);
796                 }
797
798
799                 ArrayList resume_points;
800                 public int AddResumePoint (ResumableStatement stmt)
801                 {
802                         if (resume_points == null)
803                                 resume_points = new ArrayList ();
804                         resume_points.Add (stmt);
805                         return resume_points.Count;
806                 }
807
808                 //
809                 // Called back from Yield
810                 //
811                 public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
812                 {
813                         ILGenerator ig = ec.ig;
814
815                         // Store the new current
816                         ig.Emit (OpCodes.Ldarg_0);
817                         expr.Emit (ec);
818                         ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
819
820                         // store resume program-counter
821                         ig.Emit (OpCodes.Ldarg_0);
822                         IntConstant.EmitInt (ig, resume_pc);
823                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
824
825                         // mark finally blocks as disabled
826                         if (unwind_protect && skip_finally != null) {
827                                 ig.Emit (OpCodes.Ldc_I4_1);
828                                 ig.Emit (OpCodes.Stloc, skip_finally);
829                         }
830
831                         // Return ok
832                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
833
834                         ig.MarkLabel (resume_point);
835                 }
836
837                 public override string ContainerType {
838                         get { return "iterator"; }
839                 }
840
841                 public override bool IsIterator {
842                         get { return true; }
843                 }
844
845                 public override AnonymousMethodStorey Storey {
846                         get { return IteratorHost; }
847                 }
848
849                 //
850                 // Our constructor
851                 //
852                 private Iterator (IMethodData method, TypeContainer host, Type iterator_type, bool is_enumerable)
853                         : base (
854                                 new ToplevelBlock (method.Block, Parameters.EmptyReadOnlyParameters, method.Block.StartLocation),
855                                 TypeManager.bool_type,
856                                 method.Location)
857                 {
858                         this.OriginalMethod = method;
859                         this.OriginalIteratorType = iterator_type;
860                         this.IsEnumerable = is_enumerable;
861                         this.Host = host;
862
863                         IteratorHost = Block.ChangeToIterator (this, method.Block);
864                 }
865
866                 public override string GetSignatureForError ()
867                 {
868                         return OriginalMethod.GetSignatureForError ();
869                 }
870
871                 public override Expression DoResolve (EmitContext ec)
872                 {
873                         method = new AnonymousMethodMethod (Storey,
874                                 this, Storey, null, TypeManager.system_boolean_expr,
875                                 Modifiers.PUBLIC, OriginalMethod.GetSignatureForError (),
876                                 new MemberName ("MoveNext", Location),
877                                 Parameters.EmptyReadOnlyParameters);
878
879                         if (!Compatible (ec))
880                                 return null;
881
882                         IteratorHost.DefineIteratorMembers ();
883
884                         eclass = ExprClass.Value;
885                         type = ec.ReturnType;
886                         return this;
887                 }
888
889                 public override void Emit (EmitContext ec)
890                 {
891                         //
892                         // Load Iterator storey instance
893                         //
894                         method.Storey.Instance.Emit (ec);
895
896                         //
897                         // Initialize iterator PC when it's unitialized
898                         //
899                         if (IsEnumerable) {
900                                 ILGenerator ig = ec.ig;
901                                 ig.Emit (OpCodes.Dup);
902                                 IntConstant.EmitInt (ig, (int)State.Uninitialized);
903
904                                 FieldInfo field = IteratorHost.PC.FieldBuilder;
905 #if GMCS_SOURCE
906                                 if (Storey.MemberName.IsGeneric)
907                                         field = TypeBuilder.GetField (Storey.Instance.Type, field);
908 #endif
909                                 ig.Emit (OpCodes.Stfld, field);
910                         }
911                 }
912
913                 public override Expression CreateExpressionTree (EmitContext ec)
914                 {
915                         throw new NotSupportedException ("ET");
916                 }
917
918                 public static void CreateIterator (IMethodData method, TypeContainer parent, int modifiers)
919                 {
920                         bool is_enumerable;
921                         Type iterator_type;
922
923                         Type ret = method.ReturnType;
924                         if (ret == null)
925                                 return;
926
927                         if (!CheckType (ret, out iterator_type, out is_enumerable)) {
928                                 Report.Error (1624, method.Location,
929                                               "The body of `{0}' cannot be an iterator block " +
930                                               "because `{1}' is not an iterator interface type",
931                                               method.GetSignatureForError (),
932                                               TypeManager.CSharpName (ret));
933                                 return;
934                         }
935
936                         Parameters parameters = method.ParameterInfo;
937                         for (int i = 0; i < parameters.Count; i++) {
938                                 Parameter p = parameters [i];
939                                 Parameter.Modifier mod = p.ModFlags;
940                                 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
941                                         Report.Error (1623, p.Location,
942                                                 "Iterators cannot have ref or out parameters");
943                                         return;
944                                 }
945
946                                 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
947                                         Report.Error (1636, method.Location,
948                                                 "__arglist is not allowed in parameter list of iterators");
949                                         return;
950                                 }
951
952                                 if (parameters.Types [i].IsPointer) {
953                                         Report.Error (1637, p.Location,
954                                                           "Iterators cannot have unsafe parameters or " +
955                                                           "yield types");
956                                         return;
957                                 }
958                         }
959
960                         if ((modifiers & Modifiers.UNSAFE) != 0) {
961                                 Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
962                                 return;
963                         }
964
965                         Iterator iter = new Iterator (method, parent, iterator_type, is_enumerable);
966                         iter.Storey.DefineType ();
967                 }
968
969                 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
970                 {
971                         original_iterator_type = null;
972                         is_enumerable = false;
973
974                         if (ret == TypeManager.ienumerable_type) {
975                                 original_iterator_type = TypeManager.object_type;
976                                 is_enumerable = true;
977                                 return true;
978                         }
979                         if (ret == TypeManager.ienumerator_type) {
980                                 original_iterator_type = TypeManager.object_type;
981                                 is_enumerable = false;
982                                 return true;
983                         }
984
985                         if (!TypeManager.IsGenericType (ret))
986                                 return false;
987
988                         Type[] args = TypeManager.GetTypeArguments (ret);
989                         if (args.Length != 1)
990                                 return false;
991
992                         Type gt = TypeManager.DropGenericTypeArguments (ret);
993                         if (gt == TypeManager.generic_ienumerable_type) {
994                                 original_iterator_type = args [0];
995                                 is_enumerable = true;
996                                 return true;
997                         }
998                         
999                         if (gt == TypeManager.generic_ienumerator_type) {
1000                                 original_iterator_type = args [0];
1001                                 is_enumerable = false;
1002                                 return true;
1003                         }
1004
1005                         return false;
1006                 }
1007         }
1008 }
1009