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