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