* support-test-*.cs: Rename from test-*-p2.cs.
[mono.git] / mcs / mcs / anonymous.cs
1 //
2 // anonymous.cs: Support for anonymous methods
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximain.com)
6 //
7 // (C) 2003, 2004 Novell, Inc.
8 //
9 // TODO: Ideally, we should have the helper classes emited as a hierarchy to map
10 // their nesting, and have the visibility set to private, instead of NestedAssembly
11 //
12 //
13 //
14
15 using System;
16 using System.Text;
17 using System.Collections;
18 using System.Reflection;
19 using System.Reflection.Emit;
20
21 namespace Mono.CSharp {
22
23         public abstract class AnonymousContainer : Expression
24         {
25                 // Used to generate unique method names.
26                 protected static int anonymous_method_count;
27                     
28                 // An array list of AnonymousMethodParameter or null
29                 public Parameters Parameters;
30
31                 //
32                 // The block that makes up the body for the anonymous mehtod
33                 //
34                 public ToplevelBlock Block;
35
36                 //
37                 // The container block for this anonymous method.
38                 //
39                 public Block ContainingBlock;
40
41                 //
42                 // The implicit method we create
43                 //
44                 public Method method;
45
46                 protected MethodInfo invoke_mb;
47                 
48                 // The emit context for the anonymous method
49                 public EmitContext aec;
50                 public Parameters amp;
51                 protected bool unreachable;
52
53                 // The method scope
54                 ScopeInfo method_scope;
55                 bool computed_method_scope = false;
56                 
57                 //
58                 // The modifiers applied to the method, we aggregate them
59                 //
60                 protected int method_modifiers = Modifiers.PRIVATE;
61                 
62                 //
63                 // Track the scopes that this method has used.  At the
64                 // end this is used to determine the ScopeInfo that will
65                 // host the method
66                 //
67                 ArrayList scopes_used = new ArrayList ();
68                 
69                 //
70                 // Points to our container anonymous method if its present
71                 //
72                 public AnonymousContainer ContainerAnonymousMethod;     
73
74                 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
75                                               ToplevelBlock block, Location l)
76                 {
77                         Parameters = parameters;
78                         Block = block;
79                         loc = l;
80
81                         //
82                         // The order is important: this setups the CaptureContext tree hierarchy.
83                         //
84                         if (container == null) {
85                                 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
86                                 return;
87                         }
88                         container.SetHaveAnonymousMethods (l, this);
89                         block.SetHaveAnonymousMethods (l, this);
90                 }
91
92                 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
93                                               Location l):
94                         this (parameters, container, new ToplevelBlock (container, parameters, l), l)
95                 {
96                 }
97
98                 public override Expression DoResolve (EmitContext ec)
99                 {
100                         //
101                         // Set class type, set type
102                         //
103
104                         eclass = ExprClass.Value;
105                         
106                         //
107                         // This hack means `The type is not accessible
108                         // anywhere', we depend on special conversion
109                         // rules.
110                         // 
111                         type = TypeManager.anonymous_method_type;
112
113                         return this;
114                 }
115
116                 public void RegisterScope (ScopeInfo scope)
117                 {
118                         if (scopes_used.Contains (scope))
119                                 return;
120                         scopes_used.Add (scope);
121                 }
122
123                 // Returns the deepest of two scopes
124                 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
125                 {
126                         ScopeInfo p;
127
128                         if (a == null)
129                                 return b;
130                         if (b == null)
131                                 return a;
132                         if (a == b)
133                                 return a;
134
135                         //
136                         // If they Scopes are on the same CaptureContext, we do the double
137                         // checks just so if there is an invariant change in the future,
138                         // we get the exception at the end
139                         //
140                         for (p = a; p != null; p = p.ParentScope)
141                                 if (p == b)
142                                         return a;
143                         
144                         for (p = b; p != null; p = p.ParentScope)
145                                 if (p == a)
146                                         return b;
147
148                         CaptureContext ca = a.CaptureContext;
149                         CaptureContext cb = b.CaptureContext;
150
151                         for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
152                                 if (c == cb)
153                                         return a;
154
155                         for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
156                                 if (c == ca)
157                                         return b;
158                         throw new Exception ("Should never be reached");
159                 }
160
161                 //
162                 // Determines the proper host for a method considering the
163                 // scopes it references
164                 //
165                 public void ComputeMethodHost ()
166                 {
167                         if (computed_method_scope)
168                                 return;
169                         
170                         method_scope = null;
171                         int top = scopes_used.Count;
172                         computed_method_scope = true;
173
174                         if (top == 0)
175                                 return;
176                         
177                         method_scope = (ScopeInfo) scopes_used [0];
178                         if (top == 1)
179                                 return;
180                         
181                         for (int i = 1; i < top; i++)
182                                 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
183                 }
184
185                 public ScopeInfo Scope {
186                         get {
187                                 if (computed_method_scope)
188                                         return method_scope;
189
190                                 //
191                                 // This means that ComputeMethodHost is not being called, most
192                                 // likely by someone who overwrote the CreateMethodHost method
193                                 //
194                                 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
195                         }
196                 }
197                 
198                 
199                 protected abstract bool CreateMethodHost (EmitContext ec);
200
201                 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
202
203                 public abstract bool IsIterator {
204                         get;
205                 }
206         }
207
208         public class AnonymousMethod : AnonymousContainer
209         {
210                 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
211                                         ToplevelBlock block, Location l)
212                         : base (parameters, container, block, l)
213                 {
214                 }
215
216                 public override bool IsIterator {
217                         get { return false; }
218                 }
219
220                 public override void Emit (EmitContext ec)
221                 {
222                         // nothing, as we only exist to not do anything.
223                 }
224
225                 //
226                 // Creates the host for the anonymous method
227                 //
228                 protected override bool CreateMethodHost (EmitContext ec)
229                 {
230                         ComputeMethodHost ();
231
232                         //
233                         // Crude hack follows: we replace the TypeBuilder during the
234                         // definition to get the method hosted in the right class
235                         //
236                         TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
237                         TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
238
239                         if (current_type == null)
240                                 throw new Exception ("The current_type is null");
241                         
242                         if (type_host == null)
243                                 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
244
245                         if (current_type != type_host)
246                                 method_modifiers = Modifiers.INTERNAL;
247
248                         if (current_type == type_host && ec.IsStatic){
249                                 method_modifiers |= Modifiers.STATIC;
250                                 current_type = null;
251                         } 
252
253                         method = new Method (
254                                 (TypeContainer) ec.TypeContainer,
255                                 new TypeExpression (invoke_mb.ReturnType, loc),
256                                 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
257                                 Parameters, null);
258                         method.Block = Block;
259                         
260                         //
261                         // Swap the TypeBuilder while we define the method, then restore
262                         //
263                         if (current_type != null)
264                                 ec.TypeContainer.TypeBuilder = type_host;
265                         bool res = method.Define ();
266                         if (current_type != null)
267                                 ec.TypeContainer.TypeBuilder = current_type;
268                         return res;
269                 }
270                 
271                 void Error_ParameterMismatch (Type t)
272                 {
273                         Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
274                                       "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
275                 }
276
277                 //
278                 // Returns true if this anonymous method can be implicitly
279                 // converted to the delegate type `delegate_type'
280                 //
281                 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
282                 {
283                         //
284                         // At this point its the first time we know the return type that is 
285                         // needed for the anonymous method.  We create the method here.
286                         //
287
288                         invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
289                         ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
290
291                         if (Parameters == null){                                
292                                 //
293                                 // We provide a set of inaccessible parameters
294                                 //
295                                 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
296                                 
297                                 for (int i = 0; i < invoke_pd.Count; i++){
298                                         fixedpars [i] = new Parameter (
299                                                 invoke_pd.ParameterType (i),
300                                                 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
301                                 }
302                                 
303                                 Parameters = new Parameters (fixedpars);
304                         }
305                         
306                         //
307                         // First, parameter types of `delegate_type' must be compatible
308                         // with the anonymous method.
309                         //
310                         Parameters.Resolve (ec);
311                         amp = Parameters;
312                         
313                         if (amp.Count != invoke_pd.Count){
314                                 if (!probe){
315                                         Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
316                                                 TypeManager.CSharpName (delegate_type), amp.Count.ToString ());
317                                         Error_ParameterMismatch (delegate_type);
318                                 }
319                                 return null;
320                         }
321                         
322                         for (int i = 0; i < amp.Count; i++){
323                                 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
324
325                                 if (!probe) {
326                                         if (amp_mod != invoke_pd.ParameterModifier (i)){
327                                                 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
328                                                         (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
329                                                 Error_ParameterMismatch (delegate_type);
330                                                 return null;
331                                         }
332                                 
333                                         if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
334                                                 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
335                                                         (i+1).ToString (),
336                                                         TypeManager.CSharpName (amp.ParameterType (i)),
337                                                         TypeManager.CSharpName (invoke_pd.ParameterType (i)));
338                                                 Error_ParameterMismatch (delegate_type);
339                                                 return null;
340                                         }
341                                 }
342                         }
343
344                         //
345                         // If we are only probing, return ourselves
346                         //
347                         if (probe)
348                                 return this;
349                         
350                         //
351                         // Second: the return type of the delegate must be compatible with 
352                         // the anonymous type.   Instead of doing a pass to examine the block
353                         // we satisfy the rule by setting the return type on the EmitContext
354                         // to be the delegate type return type.
355                         //
356
357                         //MethodBuilder builder = method_data.MethodBuilder;
358                         //ILGenerator ig = builder.GetILGenerator ();
359
360                         
361                         aec = new EmitContext (
362                                 ec.TypeContainer, ec.DeclSpace, loc, null,
363                                 invoke_mb.ReturnType,
364                                 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
365                                 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
366                                 (ec.IsStatic ? Modifiers.STATIC : 0),
367                                 /* No constructor */ false);
368
369                         aec.CurrentAnonymousMethod = this;
370                         ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
371                         ContainingBlock = ec.CurrentBlock;
372
373                         if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
374                                 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
375
376                         return null;
377                 }
378
379                 public override string ExprClassName {
380                         get {
381                                 return "anonymous method";
382                         }
383                 }
384
385                 public MethodBuilder GetMethodBuilder ()
386                 {
387                         return method.MethodData.MethodBuilder;
388                 }
389
390                 public override string GetSignatureForError ()
391                 {
392                         string s = TypeManager.CSharpSignature (invoke_mb);
393                         return s.Substring (0, s.IndexOf (".Invoke("));
394                 }
395                 
396                 public bool EmitMethod (EmitContext ec)
397                 {
398                         if (!CreateMethodHost (ec))
399                                 return false;
400
401                         MethodBuilder builder = GetMethodBuilder ();
402                         ILGenerator ig = builder.GetILGenerator ();
403                         aec.ig = ig;
404                         
405                         Parameters.ApplyAttributes (aec, builder);
406
407                         //
408                         // Adjust based on the computed state of the
409                         // method from CreateMethodHost
410                         
411                         aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
412                         
413                         aec.EmitMeta (Block);
414                         aec.EmitResolvedTopBlock (Block, unreachable);
415                         return true;
416                 }
417
418                 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
419                 {
420                         TypeBuilder container = ec.TypeContainer.TypeBuilder;
421                         string name = String.Format ("<>AnonHelp<{0}>", scope.id);
422
423                         scope.ScopeTypeBuilder = container.DefineNestedType (
424                                 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
425                                 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
426
427                         Type [] constructor_types = TypeManager.NoTypes;
428                         scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
429                                 MethodAttributes.Public | MethodAttributes.HideBySig |
430                                 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
431                                 CallingConventions.HasThis, constructor_types);
432
433                         TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
434
435                         ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
436                         cig.Emit (OpCodes.Ldarg_0);
437                         cig.Emit (OpCodes.Call, TypeManager.object_ctor);
438                         cig.Emit (OpCodes.Ret);
439                 }
440
441                 public static void Error_AddressOfCapturedVar (string name, Location loc)
442                 {
443                         Report.Error (1686, loc,
444                                 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
445                                 name);
446                 }
447         }
448
449         //
450         // This will emit the code for the delegate, as well delegate creation on the host
451         //
452         public class AnonymousDelegate : DelegateCreation {
453                 AnonymousMethod am;
454
455                 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
456                 {
457                         type = target_type;
458                         loc = l;
459                         this.am = am;
460                 }
461
462                 public override Expression DoResolve (EmitContext ec)
463                 {
464                         eclass = ExprClass.Value;
465
466                         return this;
467                 }
468                 
469                 public override void Emit (EmitContext ec)
470                 {
471                         if (!am.EmitMethod (ec))
472                                 return;
473
474                         //
475                         // Now emit the delegate creation.
476                         //
477                         if ((am.method.ModFlags & Modifiers.STATIC) == 0)
478                                 delegate_instance_expression = new AnonymousInstance (am);
479                         
480                         Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
481                         constructor_method = ((MethodGroupExpr) ml).Methods [0];
482                         delegate_method = am.GetMethodBuilder ();
483                         base.Emit (ec);
484                 }
485
486                 class AnonymousInstance : Expression {
487                         AnonymousMethod am;
488                         
489                         public AnonymousInstance (AnonymousMethod am)
490                         {
491                                 this.am = am;
492                                 eclass = ExprClass.Value;
493                         }
494
495                         public override Expression DoResolve (EmitContext ec)
496                         {
497                                 return this;
498                         }
499                         
500                         public override void Emit (EmitContext ec)
501                         {
502                                 am.aec.EmitMethodHostInstance (ec, am);
503                         }
504                 }
505         }
506
507         class CapturedParameter {
508                 public Type Type;
509                 public FieldBuilder FieldBuilder;
510                 public int Idx;
511
512                 public CapturedParameter (Type type, int idx)
513                 {
514                         Type = type;
515                         Idx = idx;
516                 }
517         }
518
519         //
520         // Here we cluster all the variables captured on a given scope, we also
521         // keep some extra information that might be required on each scope.
522         //
523         public class ScopeInfo {
524                 public CaptureContext CaptureContext;
525                 public ScopeInfo ParentScope;
526                 public Block ScopeBlock;
527                 public bool NeedThis = false;
528                 public bool HostsParameters = false;
529                 
530                 // For tracking the number of scopes created.
531                 public int id;
532                 static int count;
533                 bool inited = false;
534                 
535                 ArrayList locals = new ArrayList ();
536                 ArrayList children = new ArrayList ();
537
538                 //
539                 // The types and fields generated
540                 //
541                 public TypeBuilder ScopeTypeBuilder;
542                 public ConstructorBuilder ScopeConstructor;
543                 public FieldBuilder THIS;
544                 public FieldBuilder ParentLink;
545
546                 //
547                 // Points to the object of type `ScopeTypeBuilder' that
548                 // holds the data for the scope
549                 //
550                 LocalBuilder scope_instance;
551                 
552                 public ScopeInfo (CaptureContext cc, Block b)
553                 {
554                         CaptureContext = cc;
555                         ScopeBlock = b;
556                         id = count++;
557
558                         cc.RegisterCaptureContext ();
559                 }
560
561                 public void AddLocal (LocalInfo li)
562                 {
563                         if (locals.Contains (li))
564                                 return;
565
566                         locals.Add (li);
567                 }
568
569                 public bool IsCaptured (LocalInfo li)
570                 {
571                         return locals.Contains (li);
572                 }
573                 
574                 internal void AddChild (ScopeInfo si)
575                 {
576                         if (children.Contains (si))
577                                 return;
578
579                         //
580                         // If any of the current children should be a children of `si', move them there
581                         //
582                         ArrayList move_queue = null;
583                         foreach (ScopeInfo child in children){
584                                 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
585                                         if (move_queue == null)
586                                                 move_queue = new ArrayList ();
587                                         move_queue.Add (child);
588                                         child.ParentScope = si;
589                                         si.AddChild (child);
590                                 }
591                         }
592                         
593                         children.Add (si);
594
595                         if (move_queue != null){
596                                 foreach (ScopeInfo child in move_queue){
597                                         children.Remove (child);
598                                 }
599                         }
600                 } 
601
602                 static int indent = 0;
603
604                 void Pad ()
605                 {
606                         for (int i = 0; i < indent; i++)
607                                 Console.Write ("    ");
608                 }
609
610                 void EmitDebug ()
611                 {
612                         //Console.WriteLine (Environment.StackTrace);
613                         Pad ();
614                         Console.WriteLine ("START");
615                         indent++;
616                         Pad ();
617                         Console.WriteLine ("NeedThis=" + NeedThis);
618                         foreach (LocalInfo li in locals){
619                                 Pad ();
620                                 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
621                         }
622                         
623                         foreach (ScopeInfo si in children)
624                                 si.EmitDebug ();
625                         indent--;
626                         Pad ();
627                         Console.WriteLine ("END");
628                 }
629                 
630                 public string MakeHelperName ()
631                 {
632                         return String.Format ("<>AnonHelp<{0}>", id);
633                 }
634
635                 private string MakeFieldName (string local_name)
636                 {
637                         return "<" + id + ":" + local_name + ">";
638                 }
639
640                 public void EmitScopeType (EmitContext ec)
641                 {
642                         // EmitDebug ();
643
644                         if (ScopeTypeBuilder != null)
645                                 return;
646                         
647                         TypeBuilder container = ec.TypeContainer.TypeBuilder;
648
649                         CaptureContext.Host.CreateScopeType (ec, this);
650                         
651                         if (NeedThis)
652                                 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
653
654                         if (ParentScope != null){
655                                 if (ParentScope.ScopeTypeBuilder == null){
656                                         throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
657                                 }
658
659                                 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
660                                         ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
661                                                                                    FieldAttributes.Assembly);
662                         }
663                         
664                         if (NeedThis && ParentScope != null)
665                                 throw new Exception ("I was not expecting THIS && having a parent");
666
667                         foreach (LocalInfo info in locals)
668                                 info.FieldBuilder = ScopeTypeBuilder.DefineField (
669                                         MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
670
671                         if (HostsParameters){
672                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
673                                 
674                                 foreach (DictionaryEntry de in captured_parameters){
675                                         string name = (string) de.Key;
676                                         CapturedParameter cp = (CapturedParameter) de.Value;
677                                         FieldBuilder fb;
678                                         
679                                         fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
680                                         cp.FieldBuilder = fb;
681                                 }
682                         }
683
684                         foreach (ScopeInfo si in children){
685                                 si.EmitScopeType (ec);
686                         }
687                 }
688
689                 public void CloseTypes ()
690                 {
691                         RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
692                         foreach (ScopeInfo si in children)
693                                 si.CloseTypes ();
694                 }
695
696                 //
697                 // Emits the initialization code for the scope
698                 //
699                 public void EmitInitScope (EmitContext ec)
700                 {
701                         ILGenerator ig = ec.ig;
702
703                         if (inited)
704                                 return;
705
706                         if (ScopeConstructor == null)
707                                 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
708                         
709                         if (!CaptureContext.Host.IsIterator) {
710                                 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
711                                 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
712                                 ig.Emit (OpCodes.Stloc, scope_instance);
713                         }
714
715                         if (THIS != null){
716                                 if (CaptureContext.Host.IsIterator) {
717                                         ig.Emit (OpCodes.Ldarg_0);
718                                         ig.Emit (OpCodes.Ldarg_1);
719                                 } else {
720                                         ig.Emit (OpCodes.Ldloc, scope_instance);
721                                         ig.Emit (OpCodes.Ldarg_0);
722                                 }
723                                 ig.Emit (OpCodes.Stfld, THIS);
724                         }
725
726                         //
727                         // Copy the parameter values, if any
728                         //
729                         int extra = ec.IsStatic ? 0 : 1;
730                         if (CaptureContext.Host.IsIterator)
731                                 extra++;
732                         if (HostsParameters){
733                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
734                                 
735                                 foreach (DictionaryEntry de in captured_parameters){
736                                         CapturedParameter cp = (CapturedParameter) de.Value;
737
738                                         EmitScopeInstance (ig);
739                                         ParameterReference.EmitLdArg (ig, cp.Idx + extra);
740                                         ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
741                                 }
742                         }
743
744                         if (ParentScope != null){
745                                 if (!ParentScope.inited)
746                                         ParentScope.EmitInitScope (ec);
747
748                                 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
749                                         //
750                                         // Only emit initialization in our capturecontext world
751                                         //
752                                         if (ParentScope.CaptureContext == CaptureContext){
753                                                 EmitScopeInstance (ig);
754                                                 ParentScope.EmitScopeInstance (ig);
755                                                 ig.Emit (OpCodes.Stfld, ParentLink);
756                                         } else {
757                                                 EmitScopeInstance (ig);
758                                                 ig.Emit (OpCodes.Ldarg_0);
759                                                 ig.Emit (OpCodes.Stfld, ParentLink);
760                                         }
761                                 }
762                         }
763                         inited = true;
764                 }
765
766                 public void EmitScopeInstance (ILGenerator ig)
767                 {
768                         if (CaptureContext.Host.IsIterator)
769                                 ig.Emit (OpCodes.Ldarg_0);
770                         else
771                                 ig.Emit (OpCodes.Ldloc, scope_instance);
772                 }
773
774                 public static void CheckCycles (string msg, ScopeInfo s)
775                 {
776                         ArrayList l = new ArrayList ();
777                         int n = 0;
778                         
779                         for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
780                                 if (l.Contains (p)){
781                                         Console.WriteLine ("Loop detected {0} in {1}", n, msg);
782                                         throw new Exception ();
783                                 }
784                                 l.Add (p);
785                         }
786                 }
787                 
788                 static void DoPath (StringBuilder sb, ScopeInfo start)
789                 {
790                         CheckCycles ("print", start);
791                         
792                         if (start.ParentScope != null){
793                                 DoPath (sb, start.ParentScope);
794                                 sb.Append (", ");
795                         }
796                         sb.Append ((start.id).ToString ());
797                 }
798                 
799                 public override string ToString ()
800                 {
801                         StringBuilder sb = new StringBuilder ();
802                         
803                         sb.Append ("{");
804                         if (CaptureContext != null){
805                                 sb.Append (CaptureContext.ToString ());
806                                 sb.Append (":");
807                         }
808
809                         DoPath (sb, this);
810                         sb.Append ("}");
811
812                         return sb.ToString ();
813                 }
814         }
815
816         //
817         // CaptureContext objects are created on demand if a method has
818         // anonymous methods and kept on the ToplevelBlock.
819         //
820         // If they exist, all ToplevelBlocks in the containing block are
821         // linked together (children pointing to their parents).
822         //
823         public class CaptureContext {
824                 public static int count;
825                 public int cc_id;
826                 public Location loc;
827                 
828                 //
829                 // Points to the toplevel block that owns this CaptureContext
830                 //
831                 ToplevelBlock toplevel_owner;
832
833                 //
834                 // All the scopes we capture
835                 //
836                 Hashtable scopes = new Hashtable ();
837
838                 //
839                 // All the root scopes
840                 //
841                 ArrayList roots = new ArrayList ();
842                 
843                 bool have_captured_vars = false;
844                 bool referenced_this = false;
845
846                 //
847                 // Captured fields
848                 //
849                 Hashtable captured_fields = new Hashtable ();
850                 Hashtable captured_variables = new Hashtable ();
851                 public Hashtable captured_parameters = new Hashtable ();
852                 public AnonymousContainer Host;
853                 
854                 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
855                                        AnonymousContainer host)
856                 {
857                         cc_id = count++;
858                         this.toplevel_owner = toplevel_owner;
859                         this.loc = loc;
860
861                         if (host != null)
862                                 Host = host;
863                 }
864
865                 void DoPath (StringBuilder sb, CaptureContext cc)
866                 {
867                         if (cc.ParentCaptureContext != null){
868                                 DoPath (sb, cc.ParentCaptureContext);
869                                 sb.Append (".");
870                         }
871                         sb.Append (cc.cc_id.ToString ());
872                 }
873
874                 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
875                 {
876                         toplevel_owner = new_toplevel;
877                         Host = new_host;
878
879                         for (CaptureContext cc = ParentCaptureContext; cc != null;
880                              cc = cc.ParentCaptureContext) {
881                                 cc.Host = new_host;
882                         }
883                 }
884                 
885                 public override string ToString ()
886                 {
887                         StringBuilder sb = new StringBuilder ();
888                         sb.Append ("[");
889                         DoPath (sb, this);
890                         sb.Append ("]");
891                         return sb.ToString ();
892                 }
893
894                 public ToplevelBlock ParentToplevel {
895                         get {
896                                 return toplevel_owner.Container;
897                         }
898                 }
899
900                 public CaptureContext ParentCaptureContext {
901                         get {
902                                 ToplevelBlock parent = ParentToplevel;
903                                 
904                                 return (parent == null) ? null : parent.CaptureContext;
905                         }
906                 }
907
908                 ScopeInfo GetScopeForBlock (Block block)
909                 {
910                         ScopeInfo si = (ScopeInfo) scopes [block.ID];
911                         if (si != null)
912                                 return si;
913                         si = new ScopeInfo (this, block);
914                         scopes [block.ID] = si;
915                         return si;
916                 }
917                 
918                 public void AddLocal (AnonymousContainer am, LocalInfo li)
919                 {
920                         if (li.Block.Toplevel != toplevel_owner){
921                                 ParentCaptureContext.AddLocal (am, li);
922                                 return;
923                         }
924                         ScopeInfo scope = GetScopeForBlock (li.Block);
925
926                         //
927                         // Adjust the owner
928                         //
929                         if (Host != null)
930                                 Host.RegisterScope (scope);
931
932                         //
933                         // Adjust the user
934                         //
935                         am.RegisterScope (scope);
936                         
937                         if (captured_variables [li] != null)
938                                 return;
939                         
940                         have_captured_vars = true;
941                         captured_variables [li] = li;
942                         scope.AddLocal (li);
943                 }
944
945                 //
946                 // Retursn the CaptureContext for the block that defines the parameter `name'
947                 //
948                 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
949                 {
950                         ToplevelBlock container = current.Container;
951                         if (container != null){
952                                 CaptureContext cc = _ContextForParameter (container, name);
953                                 if (cc != null)
954                                         return cc;
955                         }
956                         if (current.IsParameterReference (name))
957                                 return current.ToplevelBlockCaptureContext;
958                         return null;
959                 }
960
961                 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
962                 {
963                         CaptureContext cc = _ContextForParameter (current, name);
964                         if (cc == null)
965                                 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
966                         return cc;
967                 }
968                 
969                 //
970                 // Records the captured parameter at the appropriate CaptureContext
971                 //
972                 public void AddParameter (EmitContext ec, AnonymousContainer am,
973                                           string name, Type t, int idx)
974                 {
975                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
976
977                         cc.AddParameterToContext (am, name, t, idx);
978                 }
979
980                 //
981                 // Records the parameters in the context
982                 //
983                 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
984                 {
985                         if (captured_parameters == null)
986                                 captured_parameters = new Hashtable ();
987                         if (captured_parameters [name] == null)
988                                 captured_parameters [name] = new CapturedParameter (t, idx);
989
990                         ScopeInfo scope = GetScopeForBlock (toplevel_owner);
991                         scope.HostsParameters = true;
992                         am.RegisterScope (scope);
993                 }
994
995                 //
996                 // Captured fields are only recorded on the topmost CaptureContext, because that
997                 // one is the one linked to the owner of instance fields
998                 //
999                 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1000                 {
1001                         if (fe.FieldInfo.IsStatic)
1002                                 throw new Exception ("Attempt to register a static field as a captured field");
1003                         CaptureContext parent = ParentCaptureContext;
1004                         if (parent != null) {
1005                                 parent.AddField (ec, am, fe);
1006                                 return;
1007                         }
1008
1009                         ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1010                         am.RegisterScope (scope);
1011                 }
1012
1013                 public void CaptureThis (AnonymousContainer am)
1014                 {
1015                         if (am == null)
1016                                 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1017                         CaptureContext parent = ParentCaptureContext;
1018                         if (parent != null) {
1019                                 parent.CaptureThis (am);
1020                                 return;
1021                         }
1022                         referenced_this = true;
1023
1024                         ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1025                         am.RegisterScope (scope);
1026                 }
1027
1028                 public bool HaveCapturedVariables {
1029                         get {
1030                                 return have_captured_vars;
1031                         }
1032                 }
1033                 
1034                 public bool HaveCapturedFields {
1035                         get {
1036                                 CaptureContext parent = ParentCaptureContext;
1037                                 if (parent != null)
1038                                         return parent.HaveCapturedFields;
1039                                 return captured_fields.Count > 0;
1040                         }
1041                 }
1042
1043                 public bool IsCaptured (LocalInfo local)
1044                 {
1045                         foreach (ScopeInfo si in scopes.Values){
1046                                 if (si.IsCaptured (local))
1047                                         return true;
1048                         }
1049                         return false;
1050                 }
1051
1052                 //
1053                 // Returns whether the parameter is captured
1054                 //
1055                 public bool IsParameterCaptured (string name)
1056                 {
1057                         if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1058                                 return true;
1059                         
1060                         if (captured_parameters != null)
1061                                 return captured_parameters [name] != null;
1062                         return false;
1063                 }
1064
1065                 public void EmitAnonymousHelperClasses (EmitContext ec)
1066                 {
1067                         if (roots.Count != 0){
1068                                 foreach (ScopeInfo root in roots){
1069                                         //
1070                                         // FIXME: We really should do this in a per-ScopeInfo
1071                                         // basis, instead of having the NeedThis applied to
1072                                         // all of the roots.
1073                                         //
1074                                         root.NeedThis = HaveCapturedFields || referenced_this;
1075                                         
1076                                         root.EmitScopeType (ec);
1077                                 }
1078                         } 
1079                 }
1080
1081                 public void CloseAnonymousHelperClasses ()
1082                 {
1083                         if (roots.Count != 0)
1084                                 foreach (ScopeInfo root in roots)
1085                                         root.CloseTypes ();
1086                 }
1087
1088                 public void EmitInitScope (EmitContext ec)
1089                 {
1090                         EmitAnonymousHelperClasses (ec);
1091                         if (roots.Count != 0)
1092                                 foreach (ScopeInfo root in roots)
1093                                         root.EmitInitScope (ec);
1094                 }
1095
1096                 ScopeInfo EmitGetScopeFromBlock (EmitContext ec, Block b)
1097                 {
1098                         ScopeInfo si;
1099                         
1100                         si = (ScopeInfo) scopes [b.ID];
1101                         if (si == null)
1102                                 throw new Exception ("Si is null for block " + b.ID);
1103                         si.EmitInitScope (ec);
1104
1105                         return si;
1106                 }
1107
1108                 //
1109                 // Emits the opcodes necessary to load the instance of the captured
1110                 // variable in `li'
1111                 //
1112                 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1113                                                           AnonymousContainer am)
1114                 {
1115                         ILGenerator ig = ec.ig;
1116                         ScopeInfo si;
1117
1118                         if (li.Block.Toplevel == toplevel_owner){
1119                                 si = EmitGetScopeFromBlock (ec, li.Block);
1120                                 si.EmitScopeInstance (ig);
1121                                 return;
1122                         }
1123
1124                         si = am.Scope;
1125                         ig.Emit (OpCodes.Ldarg_0);
1126                         if (si != null){
1127                                 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1128                                         return;
1129                                 }
1130
1131                                 while (si.ScopeBlock.ID != li.Block.ID){
1132                                         if (si.ParentLink != null)
1133                                                 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1134                                         si = si.ParentScope;
1135                                         if (si == null) {
1136                                                 si = am.Scope;
1137                                                 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1138                                                 while (si.ScopeBlock.ID != li.Block.ID){
1139                                                         Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1140                                                         si = si.ParentScope;
1141                                                 }
1142
1143                                                 throw new Exception (
1144                                                              String.Format ("Never found block {0} starting at {1} while looking up {2}",
1145                                                                             li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1146                                         }
1147                                 }
1148                         }
1149                 }
1150
1151                 //
1152                 // Internal routine that loads the instance to reach parameter `name'
1153                 //
1154                 void EmitParameterInstance (EmitContext ec, string name)
1155                 {
1156                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1157                         if (cc != this){
1158                                 cc.EmitParameterInstance (ec, name);
1159                                 return;
1160                         }
1161
1162                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1163                         if (par_info != null){
1164                                 // 
1165                                 // FIXME: implementing this.
1166                                 //
1167                         }
1168                         ILGenerator ig = ec.ig;
1169
1170                         ScopeInfo si;
1171
1172                         if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1173                                 si = EmitGetScopeFromBlock (ec, toplevel_owner);
1174                                 si.EmitScopeInstance (ig);
1175                         } else {
1176                                 si = ec.CurrentAnonymousMethod.Scope;
1177                                 ig.Emit (OpCodes.Ldarg_0);
1178                         }
1179
1180                         if (si != null){
1181                                 while (si.ParentLink != null) {
1182                                         ig.Emit (OpCodes.Ldfld, si.ParentLink);
1183                                         si = si.ParentScope;
1184                                 } 
1185                         }
1186                 }
1187
1188                 //
1189                 // Emits the code necessary to load the parameter named `name' within
1190                 // an anonymous method.
1191                 //
1192                 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1193                 {
1194                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1195                         if (cc != this){
1196                                 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1197                                 return;
1198                         }
1199                         if (!prepared)
1200                                 EmitParameterInstance (ec, name);
1201                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1202                         if (par_info != null){
1203                                 // 
1204                                 // FIXME: implementing this.
1205                                 //
1206                         }
1207                         ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1208
1209                         if (leave_copy){
1210                                 ec.ig.Emit (OpCodes.Dup);
1211                                 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1212                                 temp.Store (ec);
1213                         }
1214                 }
1215
1216                 //
1217                 // Implements the assignment of `source' to the paramenter named `name' within
1218                 // an anonymous method.
1219                 //
1220                 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1221                 {
1222                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1223                         if (cc != this){
1224                                 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1225                                 return;
1226                         }
1227                         ILGenerator ig = ec.ig;
1228                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1229
1230                         EmitParameterInstance (ec, name);
1231                         if (prepare_for_load)
1232                                 ig.Emit (OpCodes.Dup);
1233                         source.Emit (ec);
1234                         if (leave_copy){
1235                                 ig.Emit (OpCodes.Dup);
1236                                 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1237                                 temp.Store (ec);
1238                         }
1239                         ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1240                         if (temp != null)
1241                                 temp.Emit (ec);
1242                 }
1243
1244                 //
1245                 // Emits the address for the parameter named `name' within
1246                 // an anonymous method.
1247                 //
1248                 public void EmitAddressOfParameter (EmitContext ec, string name)
1249                 {
1250                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1251                         if (cc != this){
1252                                 cc.EmitAddressOfParameter (ec, name);
1253                                 return;
1254                         }
1255                         EmitParameterInstance (ec, name);
1256                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1257                         ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1258                 }
1259
1260                 //
1261                 // The following methods are only invoked on the host for the
1262                 // anonymous method.
1263                 //
1264                 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1265                 {
1266                         ILGenerator ig = target.ig;
1267                         ScopeInfo si = am.Scope;
1268
1269                         AnonymousContainer container = am.ContainerAnonymousMethod;
1270
1271                         if ((si == null) || ((container != null) && (si == container.Scope))) {
1272                                 ig.Emit (OpCodes.Ldarg_0);
1273                                 return;
1274                         }
1275
1276                         si.EmitInitScope (target);
1277                         si.EmitScopeInstance (ig);
1278                 }
1279
1280                 public void RegisterCaptureContext ()
1281                 {
1282                         toplevel_owner.RegisterCaptureContext (this);
1283                 }
1284
1285                 //
1286                 // Returs true if `probe' is an ancestor of `scope' in the 
1287                 // scope chain
1288                 //
1289                 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1290                 {
1291                         for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1292                                 if (probe.ScopeBlock == b)
1293                                         return true;
1294                         }
1295                         return false;
1296                 }
1297
1298                 //
1299                 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1300                 // of `scope' found in `scope_list'.
1301                 //
1302                 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1303                 //
1304                 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1305                 {
1306                         object ancestors = null;
1307                         
1308                         for (int i = 0; i < scope_list.Length; i++){
1309                                 // Ignore the same scope
1310                                 if (scope_list [i] == scope)
1311                                         continue;
1312                                 
1313                                 if (IsAncestor (scope_list [i], scope)){
1314                                         if (ancestors == null){
1315                                                 ancestors = scope_list [i];
1316                                                 continue;
1317                                         }
1318                                         
1319                                         if (ancestors is ScopeInfo){
1320                                                 object old = ancestors;
1321                                                 ancestors = new ArrayList (4);
1322                                                 ((ArrayList)ancestors).Add (old);
1323                                         } 
1324                                         
1325                                         ((ArrayList)ancestors).Add (scope_list [i]);
1326                                 }
1327                         }
1328                         return ancestors;
1329                 }
1330
1331                 //
1332                 // Returns the immediate parent of `scope' from all the captured
1333                 // scopes found in `scope_list', or null if this is a toplevel scope.
1334                 //
1335                 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1336                 {
1337                         object ancestors = GetAncestorScopes (scope, scope_list);
1338                         if (ancestors == null)
1339                                 return null;
1340
1341                         // Single match, thats the parent.
1342                         if (ancestors is ScopeInfo)
1343                                 return (ScopeInfo) ancestors;
1344
1345                         ArrayList candidates = (ArrayList) ancestors;
1346                         ScopeInfo parent = (ScopeInfo) candidates [0];
1347                         for (int i = 1; i < candidates.Count; i++){
1348                                 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1349                                         parent = (ScopeInfo) candidates [i];
1350                         }
1351                         return parent;
1352                 }
1353                 
1354                 //
1355                 // Links all the scopes
1356                 //
1357                 bool linked;
1358                 public void LinkScopes ()
1359                 {
1360                         if (linked)
1361                                 return;
1362                         
1363                         linked = true;
1364                         if (ParentCaptureContext != null)
1365                                 ParentCaptureContext.LinkScopes ();
1366
1367                         int scope_count = scopes.Keys.Count;
1368                         ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1369                         scopes.Values.CopyTo (scope_list, 0);
1370
1371                         for (int i = 0; i < scope_count; i++){
1372                                 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1373
1374                                 if (parent == null){
1375                                         roots.Add (scope_list [i]);
1376                                         continue;
1377                                 }
1378
1379                                 scope_list [i].ParentScope = parent;
1380                                 parent.AddChild (scope_list [i]);
1381                         }
1382
1383                         //
1384                         // Link the roots to their parent containers if any.
1385                         //
1386                         if (ParentCaptureContext != null && roots.Count != 0){
1387                                 ScopeInfo one_root = (ScopeInfo) roots [0];
1388                                 bool found = false;
1389                                 
1390                                 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1391                                         if (!IsAncestor (a_parent_root, one_root))
1392                                                 continue;
1393
1394                                         found = true;
1395                                         
1396                                         // Found, link all the roots to this root
1397                                         foreach (ScopeInfo root in roots){
1398                                                 root.ParentScope = a_parent_root;
1399                                                 a_parent_root.AddChild (root);
1400                                         }
1401                                         break;
1402                                 }
1403                                 if (!found){
1404                                         //
1405                                         // This is to catch a condition in which it is
1406                                         // not possible to determine the containing ScopeInfo
1407                                         // from an encapsulating CaptureContext
1408                                         //
1409                                         throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");
1410                                 }
1411                         }
1412                 }
1413         }
1414 }