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