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