2008-11-27 Marek Safar <marek.safar@gmail.com>
[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                                 IteratorMethod host_method;
209
210                                 Expression new_storey;
211
212                                 public GetEnumeratorStatement (IteratorStorey host, IteratorMethod host_method)
213                                 {
214                                         this.host = host;
215                                         this.host_method = host_method;
216                                         loc = host_method.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                                         TypeExpression storey_type_expr = new TypeExpression (host.TypeBuilder, loc);
227                                         ArrayList init = null;
228                                         if (host.hoisted_this != null) {
229                                                 init = new ArrayList (host.hoisted_params == null ? 1 : host.HoistedParameters.Count + 1);
230                                                 HoistedThis ht = host.hoisted_this;
231                                                 FieldExpr from = new FieldExpr (ht.Field.FieldBuilder, loc);
232                                                 from.InstanceExpression = CompilerGeneratedThis.Instance;
233                                                 init.Add (new ElementInitializer (ht.Field.Name, from, loc));
234                                         }
235
236                                         if (host.hoisted_params != null) {
237                                                 if (init == null)
238                                                         init = new ArrayList (host.HoistedParameters.Count);
239
240                                                 for (int i = 0; i < host.hoisted_params.Count; ++i) {
241                                                         HoistedParameter hp = (HoistedParameter) host.hoisted_params [i];
242                                                         HoistedParameter hp_cp = (HoistedParameter) host.hoisted_params_copy [i];
243
244                                                         FieldExpr from = new FieldExpr (hp_cp.Field.FieldBuilder, loc);
245                                                         from.InstanceExpression = CompilerGeneratedThis.Instance;
246
247                                                         init.Add (new ElementInitializer (hp.Field.Name, from, loc));
248                                                 }
249                                         }
250
251                                         if (init != null) {
252                                                 new_storey = new NewInitialize (storey_type_expr, new ArrayList (0),
253                                                         new CollectionOrObjectInitializers (init, loc), loc);
254                                         } else {
255                                                 new_storey = new New (storey_type_expr, new ArrayList (0), loc);
256                                         }
257
258                                         new_storey = new_storey.Resolve (ec);
259                                         if (new_storey != null)
260                                                 new_storey = Convert.ImplicitConversionRequired (ec, new_storey, host_method.MemberType, loc);
261
262                                         if (TypeManager.int_interlocked_compare_exchange == null) {
263                                                 Type t = TypeManager.CoreLookupType ("System.Threading", "Interlocked", Kind.Class, true);
264                                                 if (t != null) {
265                                                         TypeManager.int_interlocked_compare_exchange = TypeManager.GetPredefinedMethod (
266                                                                 t, "CompareExchange", loc, TypeManager.int32_type,
267                                                                 TypeManager.int32_type, TypeManager.int32_type);
268                                                 }
269                                         }
270
271                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
272                                         return true;
273                                 }
274
275                                 protected override void DoEmit (EmitContext ec)
276                                 {
277                                         ILGenerator ig = ec.ig;
278                                         Label label_init = ig.DefineLabel ();
279
280                                         ig.Emit (OpCodes.Ldarg_0);
281                                         ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder);
282                                         IntConstant.EmitInt (ig, (int) Iterator.State.Start);
283                                         IntConstant.EmitInt (ig, (int) Iterator.State.Uninitialized);
284                                         ig.Emit (OpCodes.Call, TypeManager.int_interlocked_compare_exchange);
285
286                                         IntConstant.EmitInt (ig, (int) Iterator.State.Uninitialized);
287                                         ig.Emit (OpCodes.Bne_Un_S, label_init);
288
289                                         ig.Emit (OpCodes.Ldarg_0);
290                                         ig.Emit (OpCodes.Ret);
291
292                                         ig.MarkLabel (label_init);
293
294                                         new_storey.Emit (ec);
295                                         ig.Emit (OpCodes.Ret);
296                                 }
297
298                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
299                                 {
300                                         throw new NotSupportedException ();
301                                 }
302                         }
303
304                         public GetEnumeratorMethod (IteratorStorey host, FullNamedExpression returnType, MemberName name)
305                                 : base (host, returnType, 0, name)
306                         {
307                                 Block.AddStatement (new GetEnumeratorStatement (host, this));
308                         }
309                 }
310
311                 class DisposeMethod : IteratorMethod
312                 {
313                         sealed class DisposeMethodStatement : Statement
314                         {
315                                 Iterator iterator;
316
317                                 public DisposeMethodStatement (Iterator iterator)
318                                 {
319                                         this.iterator = iterator;
320                                         this.loc = iterator.Location;
321                                 }
322
323                                 protected override void CloneTo (CloneContext clonectx, Statement target)
324                                 {
325                                         throw new NotSupportedException ();
326                                 }
327
328                                 public override bool Resolve (EmitContext ec)
329                                 {
330                                         return true;
331                                 }
332
333                                 protected override void DoEmit (EmitContext ec)
334                                 {
335                                         iterator.EmitDispose (ec);
336                                 }
337
338                                 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
339                                 {
340                                         throw new NotSupportedException ();
341                                 }
342                         }
343
344                         public DisposeMethod (IteratorStorey host)
345                                 : base (host, TypeManager.system_void_expr, Modifiers.PUBLIC, new MemberName ("Dispose", host.Location))
346                         {
347                                 host.AddMethod (this);
348
349                                 Block = new ToplevelBlock (host.Iterator.Container, null, Location);
350                                 Block.AddStatement (new DisposeMethodStatement (host.Iterator));
351                         }
352                 }
353
354                 //
355                 // Uses Method as method info
356                 //
357                 class DynamicMethodGroupExpr : MethodGroupExpr
358                 {
359                         readonly Method method;
360
361                         public DynamicMethodGroupExpr (Method method, Location loc)
362                                 : base (null, loc)
363                         {
364                                 this.method = method;
365                         }
366
367                         public override Expression DoResolve (EmitContext ec)
368                         {
369                                 Methods = new MethodBase [] { method.MethodBuilder };
370                                 type = method.Parent.TypeBuilder;
371                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
372                                 return base.DoResolve (ec);
373                         }
374                 }
375
376                 class DynamicFieldExpr : FieldExpr
377                 {
378                         readonly Field field;
379
380                         public DynamicFieldExpr (Field field, Location loc)
381                                 : base (loc)
382                         {
383                                 this.field = field;
384                         }
385
386                         public override Expression DoResolve (EmitContext ec)
387                         {
388                                 FieldInfo = field.FieldBuilder;
389                                 type = TypeManager.TypeToCoreType (FieldInfo.FieldType);
390                                 InstanceExpression = new CompilerGeneratedThis (type, Location);
391                                 return base.DoResolve (ec);
392                         }
393                 }
394
395                 public readonly Iterator Iterator;
396
397                 TypeExpr iterator_type_expr;
398                 Field pc_field;
399                 Field current_field;
400
401                 TypeExpr enumerator_type;
402                 TypeExpr enumerable_type;
403 #if GMCS_SOURCE
404                 TypeArguments generic_args;
405                 TypeExpr generic_enumerator_type;
406                 TypeExpr generic_enumerable_type;
407 #else
408                 const TypeArguments generic_args = null;
409 #endif
410
411                 ArrayList hoisted_params_copy;
412                 int local_name_idx;
413
414                 public IteratorStorey (Iterator iterator)
415                         : base (iterator.Container.Toplevel, iterator.Host,
416                           iterator.OriginalMethod as MemberBase, iterator.GenericMethod, "Iterator")
417                 {
418                         this.Iterator = iterator;
419                         HasHoistedVariables = true;
420                 }
421
422                 public Field PC {
423                         get { return pc_field; }
424                 }
425
426                 public Field CurrentField {
427                         get { return current_field; }
428                 }
429
430                 public ArrayList HoistedParameters {
431                         get { return hoisted_params; }
432                 }
433
434                 protected override TypeExpr [] ResolveBaseTypes (out TypeExpr base_class)
435                 {
436                         iterator_type_expr = new TypeExpression (MutateType (Iterator.OriginalIteratorType), Location);
437
438 #if GMCS_SOURCE
439                         generic_args = new TypeArguments (iterator_type_expr);
440 #endif
441
442                         ArrayList list = new ArrayList ();
443                         if (Iterator.IsEnumerable) {
444                                 enumerable_type = new TypeExpression (
445                                         TypeManager.ienumerable_type, Location);
446                                 list.Add (enumerable_type);
447
448 #if GMCS_SOURCE
449                                 generic_enumerable_type = new GenericTypeExpr (
450                                         TypeManager.generic_ienumerable_type,
451                                         generic_args, Location);
452                                 list.Add (generic_enumerable_type);
453 #endif
454                         }
455
456                         enumerator_type = new TypeExpression (
457                                 TypeManager.ienumerator_type, Location);
458                         list.Add (enumerator_type);
459
460                         list.Add (new TypeExpression (TypeManager.idisposable_type, Location));
461
462 #if GMCS_SOURCE
463                         generic_enumerator_type = new GenericTypeExpr (
464                                 TypeManager.generic_ienumerator_type,
465                                 generic_args, Location);
466                         list.Add (generic_enumerator_type);
467 #endif
468
469                         type_bases = list;
470
471                         return base.ResolveBaseTypes (out base_class);
472                 }
473
474                 protected override string GetVariableMangledName (LocalInfo local_info)
475                 {
476                         return "<" + local_info.Name + ">__" + local_name_idx++.ToString ();
477                 }
478
479                 public void DefineIteratorMembers ()
480                 {
481                         pc_field = AddCompilerGeneratedField ("$PC", TypeManager.system_int32_expr);
482                         current_field = AddCompilerGeneratedField ("$current", iterator_type_expr);
483
484                         if (hoisted_params != null) {
485                                 //
486                                 // Iterators are independent, each GetEnumerator call has to
487                                 // create same enumerator therefore we have to keep original values
488                                 // around for re-initialization
489                                 //
490                                 // TODO: Do it for assigned/modified parameters only
491                                 //
492                                 hoisted_params_copy = new ArrayList (hoisted_params.Count);
493                                 foreach (HoistedParameter hp in hoisted_params) {
494                                         hoisted_params_copy.Add (new HoistedParameter (hp, "<$>" + hp.Field.Name));
495                                 }
496                         }
497
498 #if GMCS_SOURCE
499                         Define_Current (true);
500 #endif
501                         Define_Current (false);
502                         new DisposeMethod (this);
503                         Define_Reset ();
504
505                         if (Iterator.IsEnumerable) {
506                                 MemberName name = new MemberName (
507                                         new MemberName ("System.Collections.IEnumerable", Location), "GetEnumerator", Location);
508
509 #if GMCS_SOURCE
510                                 Method get_enumerator = new IteratorMethod (this, enumerator_type, 0, name);
511
512                                 name = new MemberName (
513                                         new MemberName ("System.Collections.Generic.IEnumerable", generic_args, Location), "GetEnumerator", Location);
514                                 Method gget_enumerator = new GetEnumeratorMethod (this, generic_enumerator_type, name);
515
516                                 //
517                                 // Just call generic GetEnumerator implementation
518                                 //
519                                 get_enumerator.Block.AddStatement (
520                                         new Return (new Invocation (new DynamicMethodGroupExpr (gget_enumerator, Location), new ArrayList (0)), Location));
521
522                                 AddMethod (get_enumerator);
523                                 AddMethod (gget_enumerator);
524 #else
525                                 AddMethod (new GetEnumeratorMethod (this, enumerator_type, name));
526 #endif
527                         }
528                 }
529
530                 protected override void EmitHoistedParameters (EmitContext ec, ArrayList hoisted)
531                 {
532                         base.EmitHoistedParameters (ec, hoisted);
533                         base.EmitHoistedParameters (ec, hoisted_params_copy);
534                 }
535
536                 void Define_Current (bool is_generic)
537                 {
538                         MemberName left;
539                         TypeExpr type;
540
541                         if (is_generic) {
542                                 left = new MemberName (
543                                         "System.Collections.Generic.IEnumerator",
544                                         generic_args, Location);
545                                 type = iterator_type_expr;
546                         } else {
547                                 left = new MemberName ("System.Collections.IEnumerator", Location);
548                                 type = TypeManager.system_object_expr;
549                         }
550
551                         MemberName name = new MemberName (left, "Current", Location);
552
553                         ToplevelBlock get_block = new ToplevelBlock (Location);
554                         get_block.AddStatement (new Return (new DynamicFieldExpr (CurrentField, Location), Location));
555                                 
556                         Accessor getter = new Accessor (get_block, 0, null, null, Location);
557
558                         Property current = new Property (
559                                 this, type, Modifiers.DEBUGGER_HIDDEN, name, null, getter, null, false);
560                         AddProperty (current);
561                 }
562
563                 void Define_Reset ()
564                 {
565                         Method reset = new Method (
566                                 this, null, TypeManager.system_void_expr,
567                                 Modifiers.PUBLIC | Modifiers.DEBUGGER_HIDDEN,
568                                 new MemberName ("Reset", Location),
569                                 Parameters.EmptyReadOnlyParameters, null);
570                         AddMethod (reset);
571
572                         reset.Block = new ToplevelBlock (Location);
573
574                         TypeExpr ex_type = new TypeLookupExpression ("System.NotSupportedException");
575                         reset.Block.AddStatement (new Throw (new New (ex_type, null, Location), Location));
576                 }
577         }
578
579         //
580         // Iterators are implemented as hidden anonymous block
581         //
582         public class Iterator : AnonymousExpression {
583                 public readonly IMethodData OriginalMethod;
584                 AnonymousMethodMethod method;
585                 public readonly TypeContainer Host;
586                 public readonly bool IsEnumerable;
587
588                 //
589                 // The state as we generate the iterator
590                 //
591                 Label move_next_ok, move_next_error;
592                 LocalBuilder skip_finally, current_pc;
593
594                 public LocalBuilder SkipFinally {
595                         get { return skip_finally; }
596                 }
597
598                 public LocalBuilder CurrentPC {
599                         get { return current_pc; }
600                 }
601
602                 public Block Container {
603                         get { return OriginalMethod.Block; }
604                 }
605
606                 public GenericMethod GenericMethod {
607                         get { return OriginalMethod.GenericMethod; }
608                 }
609
610                 public readonly Type OriginalIteratorType;
611
612                 readonly IteratorStorey IteratorHost;
613
614                 public enum State {
615                         Running = -3, // Used only in CurrentPC, never stored into $PC
616                         Uninitialized = -2,
617                         After = -1,
618                         Start = 0
619                 }
620
621                 public override void AddStoreyReference (AnonymousMethodStorey storey)
622                 {
623                         // do nothing
624                 }
625
626                 public void EmitYieldBreak (ILGenerator ig, bool unwind_protect)
627                 {
628                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error);
629                 }
630
631                 void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block)
632                 {
633                         ILGenerator ig = ec.ig;
634
635                         ig.Emit (OpCodes.Ldarg_0);
636                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
637
638                         ig.Emit (OpCodes.Ldarg_0);
639                         IntConstant.EmitInt (ig, (int) State.After);
640                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
641
642                         // We only care if the PC is zero (start executing) or non-zero (don't do anything)
643                         ig.Emit (OpCodes.Brtrue, move_next_error);
644
645                         SymbolWriter.StartIteratorBody (ec.ig);
646                         original_block.Emit (ec);
647                         SymbolWriter.EndIteratorBody (ec.ig);
648
649                         ig.MarkLabel (move_next_error);
650                         ig.Emit (OpCodes.Ldc_I4_0);
651                         ig.Emit (OpCodes.Ret);
652                 }
653
654                 internal void EmitMoveNext (EmitContext ec, Block original_block)
655                 {
656                         ILGenerator ig = ec.ig;
657
658                         move_next_ok = ig.DefineLabel ();
659                         move_next_error = ig.DefineLabel ();
660
661                         if (resume_points == null) {
662                                 EmitMoveNext_NoResumePoints (ec, original_block);
663                                 return;
664                         }
665
666                         current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
667                         ig.Emit (OpCodes.Ldarg_0);
668                         ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
669                         ig.Emit (OpCodes.Stloc, current_pc);
670
671                         // We're actually in state 'running', but this is as good a PC value as any if there's an abnormal exit
672                         ig.Emit (OpCodes.Ldarg_0);
673                         IntConstant.EmitInt (ig, (int) State.After);
674                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
675
676                         Label [] labels = new Label [1 + resume_points.Count];
677                         labels [0] = ig.DefineLabel ();
678
679                         bool need_skip_finally = false;
680                         for (int i = 0; i < resume_points.Count; ++i) {
681                                 ResumableStatement s = (ResumableStatement) resume_points [i];
682                                 need_skip_finally |= s is ExceptionStatement;
683                                 labels [i+1] = s.PrepareForEmit (ec);
684                         }
685
686                         if (need_skip_finally) {
687                                 skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type);
688                                 ig.Emit (OpCodes.Ldc_I4_0);
689                                 ig.Emit (OpCodes.Stloc, skip_finally);
690                         }
691
692                         SymbolWriter.StartIteratorDispatcher (ec.ig);
693                         ig.Emit (OpCodes.Ldloc, current_pc);
694                         ig.Emit (OpCodes.Switch, labels);
695
696                         ig.Emit (OpCodes.Br, move_next_error);
697                         SymbolWriter.EndIteratorDispatcher (ec.ig);
698
699                         ig.MarkLabel (labels [0]);
700
701                         SymbolWriter.StartIteratorBody (ec.ig);
702                         original_block.Emit (ec);
703                         SymbolWriter.EndIteratorBody (ec.ig);
704
705                         SymbolWriter.StartIteratorDispatcher (ec.ig);
706
707                         ig.Emit (OpCodes.Ldarg_0);
708                         IntConstant.EmitInt (ig, (int) State.After);
709                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
710
711                         ig.MarkLabel (move_next_error);
712                         ig.Emit (OpCodes.Ldc_I4_0);
713                         ig.Emit (OpCodes.Ret);
714
715                         ig.MarkLabel (move_next_ok);
716                         ig.Emit (OpCodes.Ldc_I4_1);
717                         ig.Emit (OpCodes.Ret);
718
719                         SymbolWriter.EndIteratorDispatcher (ec.ig);
720                 }
721
722                 public void EmitDispose (EmitContext ec)
723                 {
724                         ILGenerator ig = ec.ig;
725
726                         Label end = ig.DefineLabel ();
727
728                         Label [] labels = null;
729                         int n_resume_points = resume_points == null ? 0 : resume_points.Count;
730                         for (int i = 0; i < n_resume_points; ++i) {
731                                 ResumableStatement s = (ResumableStatement) resume_points [i];
732                                 Label ret = s.PrepareForDispose (ec, end);
733                                 if (ret.Equals (end) && labels == null)
734                                         continue;
735                                 if (labels == null) {
736                                         labels = new Label [resume_points.Count + 1];
737                                         for (int j = 0; j <= i; ++j)
738                                                 labels [j] = end;
739                                 }
740                                 labels [i+1] = ret;
741                         }
742
743                         if (labels != null) {
744                                 current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type);
745                                 ig.Emit (OpCodes.Ldarg_0);
746                                 ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder);
747                                 ig.Emit (OpCodes.Stloc, current_pc);
748                         }
749
750                         ig.Emit (OpCodes.Ldarg_0);
751                         IntConstant.EmitInt (ig, (int) State.After);
752                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
753
754                         if (labels != null) {
755                                 //SymbolWriter.StartIteratorDispatcher (ec.ig);
756                                 ig.Emit (OpCodes.Ldloc, current_pc);
757                                 ig.Emit (OpCodes.Switch, labels);
758                                 //SymbolWriter.EndIteratorDispatcher (ec.ig);
759
760                                 foreach (ResumableStatement s in resume_points)
761                                         s.EmitForDispose (ec, this, end, true);
762                         }
763
764                         ig.MarkLabel (end);
765                 }
766
767
768                 ArrayList resume_points;
769                 public int AddResumePoint (ResumableStatement stmt)
770                 {
771                         if (resume_points == null)
772                                 resume_points = new ArrayList ();
773                         resume_points.Add (stmt);
774                         return resume_points.Count;
775                 }
776
777                 //
778                 // Called back from Yield
779                 //
780                 public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point)
781                 {
782                         ILGenerator ig = ec.ig;
783
784                         // Store the new current
785                         ig.Emit (OpCodes.Ldarg_0);
786                         expr.Emit (ec);
787                         ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder);
788
789                         // store resume program-counter
790                         ig.Emit (OpCodes.Ldarg_0);
791                         IntConstant.EmitInt (ig, resume_pc);
792                         ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder);
793
794                         // mark finally blocks as disabled
795                         if (unwind_protect && skip_finally != null) {
796                                 ig.Emit (OpCodes.Ldc_I4_1);
797                                 ig.Emit (OpCodes.Stloc, skip_finally);
798                         }
799
800                         // Return ok
801                         ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok);
802
803                         ig.MarkLabel (resume_point);
804                 }
805
806                 public override string ContainerType {
807                         get { return "iterator"; }
808                 }
809
810                 public override bool IsIterator {
811                         get { return true; }
812                 }
813
814                 public override AnonymousMethodStorey Storey {
815                         get { return IteratorHost; }
816                 }
817
818                 //
819                 // Our constructor
820                 //
821                 private Iterator (IMethodData method, TypeContainer host, Type iterator_type, bool is_enumerable)
822                         : base (
823                                 new ToplevelBlock (method.Block, Parameters.EmptyReadOnlyParameters, method.Block.StartLocation),
824                                 TypeManager.bool_type,
825                                 method.Location)
826                 {
827                         this.OriginalMethod = method;
828                         this.OriginalIteratorType = iterator_type;
829                         this.IsEnumerable = is_enumerable;
830                         this.Host = host;
831
832                         IteratorHost = Block.ChangeToIterator (this, method.Block);
833                 }
834
835                 public override string GetSignatureForError ()
836                 {
837                         return OriginalMethod.GetSignatureForError ();
838                 }
839
840                 public override Expression DoResolve (EmitContext ec)
841                 {
842                         method = new AnonymousMethodMethod (Storey,
843                                 this, Storey, null, TypeManager.system_boolean_expr,
844                                 Modifiers.PUBLIC, OriginalMethod.GetSignatureForError (),
845                                 new MemberName ("MoveNext", Location),
846                                 Parameters.EmptyReadOnlyParameters);
847
848                         if (!Compatible (ec))
849                                 return null;
850
851                         IteratorHost.DefineIteratorMembers ();
852
853                         eclass = ExprClass.Value;
854                         type = ec.ReturnType;
855                         return this;
856                 }
857
858                 public override void Emit (EmitContext ec)
859                 {
860                         //
861                         // Load Iterator storey instance
862                         //
863                         method.Storey.Instance.Emit (ec);
864
865                         //
866                         // Initialize iterator PC when it's unitialized
867                         //
868                         if (IsEnumerable) {
869                                 ILGenerator ig = ec.ig;
870                                 ig.Emit (OpCodes.Dup);
871                                 IntConstant.EmitInt (ig, (int)State.Uninitialized);
872
873                                 FieldInfo field = IteratorHost.PC.FieldBuilder;
874 #if GMCS_SOURCE
875                                 if (Storey.MemberName.IsGeneric)
876                                         field = TypeBuilder.GetField (Storey.Instance.Type, field);
877 #endif
878                                 ig.Emit (OpCodes.Stfld, field);
879                         }
880                 }
881
882                 public override Expression CreateExpressionTree (EmitContext ec)
883                 {
884                         throw new NotSupportedException ("ET");
885                 }
886
887                 public static void CreateIterator (IMethodData method, TypeContainer parent, int modifiers)
888                 {
889                         bool is_enumerable;
890                         Type iterator_type;
891
892                         Type ret = method.ReturnType;
893                         if (ret == null)
894                                 return;
895
896                         if (!CheckType (ret, out iterator_type, out is_enumerable)) {
897                                 Report.Error (1624, method.Location,
898                                               "The body of `{0}' cannot be an iterator block " +
899                                               "because `{1}' is not an iterator interface type",
900                                               method.GetSignatureForError (),
901                                               TypeManager.CSharpName (ret));
902                                 return;
903                         }
904
905                         Parameters parameters = method.ParameterInfo;
906                         for (int i = 0; i < parameters.Count; i++) {
907                                 Parameter p = parameters [i];
908                                 Parameter.Modifier mod = p.ModFlags;
909                                 if ((mod & Parameter.Modifier.ISBYREF) != 0) {
910                                         Report.Error (1623, p.Location,
911                                                 "Iterators cannot have ref or out parameters");
912                                         return;
913                                 }
914
915                                 if ((mod & Parameter.Modifier.ARGLIST) != 0) {
916                                         Report.Error (1636, method.Location,
917                                                 "__arglist is not allowed in parameter list of iterators");
918                                         return;
919                                 }
920
921                                 if (parameters.Types [i].IsPointer) {
922                                         Report.Error (1637, p.Location,
923                                                           "Iterators cannot have unsafe parameters or " +
924                                                           "yield types");
925                                         return;
926                                 }
927                         }
928
929                         if ((modifiers & Modifiers.UNSAFE) != 0) {
930                                 Report.Error (1629, method.Location, "Unsafe code may not appear in iterators");
931                                 return;
932                         }
933
934                         Iterator iter = new Iterator (method, parent, iterator_type, is_enumerable);
935                         iter.Storey.DefineType ();
936                 }
937
938                 static bool CheckType (Type ret, out Type original_iterator_type, out bool is_enumerable)
939                 {
940                         original_iterator_type = null;
941                         is_enumerable = false;
942
943                         if (ret == TypeManager.ienumerable_type) {
944                                 original_iterator_type = TypeManager.object_type;
945                                 is_enumerable = true;
946                                 return true;
947                         }
948                         if (ret == TypeManager.ienumerator_type) {
949                                 original_iterator_type = TypeManager.object_type;
950                                 is_enumerable = false;
951                                 return true;
952                         }
953
954                         if (!TypeManager.IsGenericType (ret))
955                                 return false;
956
957                         Type[] args = TypeManager.GetTypeArguments (ret);
958                         if (args.Length != 1)
959                                 return false;
960
961                         Type gt = TypeManager.DropGenericTypeArguments (ret);
962                         if (gt == TypeManager.generic_ienumerable_type) {
963                                 original_iterator_type = args [0];
964                                 is_enumerable = true;
965                                 return true;
966                         }
967                         
968                         if (gt == TypeManager.generic_ienumerator_type) {
969                                 original_iterator_type = args [0];
970                                 is_enumerable = false;
971                                 return true;
972                         }
973
974                         return false;
975                 }
976         }
977 }
978