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