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