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