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