new tests + update
[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 override string ExprClassName {
323                         get {
324                                 return "anonymous method";
325                         }
326                 }
327
328                 public MethodBuilder GetMethodBuilder ()
329                 {
330                         return method.MethodData.MethodBuilder;
331                 }
332
333                 public override string GetSignatureForError ()
334                 {
335                         string s = TypeManager.CSharpSignature (invoke_mb);
336                         return s.Substring (0, s.IndexOf (".Invoke("));
337                 }
338                 
339                 public bool EmitMethod (EmitContext ec)
340                 {
341                         if (!CreateMethodHost (ec))
342                                 return false;
343
344                         MethodBuilder builder = GetMethodBuilder ();
345                         ILGenerator ig = builder.GetILGenerator ();
346                         aec.ig = ig;
347                         
348                         Parameters.LabelParameters (aec, builder);
349
350                         //
351                         // Adjust based on the computed state of the
352                         // method from CreateMethodHost
353                         
354                         aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
355                         
356                         aec.EmitMeta (Block, amp);
357                         aec.EmitResolvedTopBlock (Block, unreachable);
358                         return true;
359                 }
360
361                 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
362                 {
363                         TypeBuilder container = ec.TypeContainer.TypeBuilder;
364                         string name = String.Format ("<>AnonHelp<{0}>", scope.id);
365
366                         scope.ScopeTypeBuilder = container.DefineNestedType (
367                                 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
368                                 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
369
370                         Type [] constructor_types = TypeManager.NoTypes;
371                         Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
372                         scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
373                                 MethodAttributes.Public | MethodAttributes.HideBySig |
374                                 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
375                                 CallingConventions.HasThis, constructor_types);
376                         InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
377                         TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
378
379                         ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
380                         cig.Emit (OpCodes.Ldarg_0);
381                         cig.Emit (OpCodes.Call, TypeManager.object_ctor);
382                         cig.Emit (OpCodes.Ret);
383                 }
384
385                 public static void Error_AddressOfCapturedVar (string name, Location loc)
386                 {
387                         Report.Error (1686, loc,
388                                 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
389                                 name);
390                 }
391         }
392
393         //
394         // This will emit the code for the delegate, as well delegate creation on the host
395         //
396         public class AnonymousDelegate : DelegateCreation {
397                 AnonymousMethod am;
398
399                 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
400                 {
401                         type = target_type;
402                         loc = l;
403                         this.am = am;
404                 }
405
406                 public override Expression DoResolve (EmitContext ec)
407                 {
408                         eclass = ExprClass.Value;
409
410                         return this;
411                 }
412                 
413                 public override void Emit (EmitContext ec)
414                 {
415                         if (!am.EmitMethod (ec))
416                                 return;
417
418                         //
419                         // Now emit the delegate creation.
420                         //
421                         if ((am.method.ModFlags & Modifiers.STATIC) == 0)
422                                 delegate_instance_expression = new AnonymousInstance (am);
423                         
424                         Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
425                         constructor_method = ((MethodGroupExpr) ml).Methods [0];
426                         delegate_method = am.GetMethodBuilder ();
427                         base.Emit (ec);
428                 }
429
430                 class AnonymousInstance : Expression {
431                         AnonymousMethod am;
432                         
433                         public AnonymousInstance (AnonymousMethod am)
434                         {
435                                 this.am = am;
436                                 eclass = ExprClass.Value;
437                         }
438
439                         public override Expression DoResolve (EmitContext ec)
440                         {
441                                 return this;
442                         }
443                         
444                         public override void Emit (EmitContext ec)
445                         {
446                                 am.aec.EmitMethodHostInstance (ec, am);
447                         }
448                 }
449         }
450
451         class CapturedParameter {
452                 public Type Type;
453                 public FieldBuilder FieldBuilder;
454                 public int Idx;
455
456                 public CapturedParameter (Type type, int idx)
457                 {
458                         Type = type;
459                         Idx = idx;
460                 }
461         }
462
463         //
464         // Here we cluster all the variables captured on a given scope, we also
465         // keep some extra information that might be required on each scope.
466         //
467         public class ScopeInfo {
468                 public CaptureContext CaptureContext;
469                 public ScopeInfo ParentScope;
470                 public Block ScopeBlock;
471                 public bool NeedThis = false;
472                 public bool HostsParameters = false;
473                 
474                 // For tracking the number of scopes created.
475                 public int id;
476                 static int count;
477                 bool inited = false;
478                 
479                 ArrayList locals = new ArrayList ();
480                 ArrayList children = new ArrayList ();
481
482                 //
483                 // The types and fields generated
484                 //
485                 public TypeBuilder ScopeTypeBuilder;
486                 public ConstructorBuilder ScopeConstructor;
487                 public FieldBuilder THIS;
488                 public FieldBuilder ParentLink;
489
490                 //
491                 // Points to the object of type `ScopeTypeBuilder' that
492                 // holds the data for the scope
493                 //
494                 LocalBuilder scope_instance;
495                 
496                 public ScopeInfo (CaptureContext cc, Block b)
497                 {
498                         CaptureContext = cc;
499                         ScopeBlock = b;
500                         id = count++;
501
502                         cc.AddScope (this);
503                 }
504
505                 public void AddLocal (LocalInfo li)
506                 {
507                         if (locals.Contains (li))
508                                 return;
509
510                         locals.Add (li);
511                 }
512
513                 public bool IsCaptured (LocalInfo li)
514                 {
515                         return locals.Contains (li);
516                 }
517                 
518                 internal void AddChild (ScopeInfo si)
519                 {
520                         if (children.Contains (si))
521                                 return;
522
523                         //
524                         // If any of the current children should be a children of `si', move them there
525                         //
526                         ArrayList move_queue = null;
527                         foreach (ScopeInfo child in children){
528                                 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
529                                         if (move_queue == null)
530                                                 move_queue = new ArrayList ();
531                                         move_queue.Add (child);
532                                         child.ParentScope = si;
533                                         si.AddChild (child);
534                                 }
535                         }
536                         
537                         children.Add (si);
538
539                         if (move_queue != null){
540                                 foreach (ScopeInfo child in move_queue){
541                                         children.Remove (child);
542                                 }
543                         }
544                 } 
545
546                 static int indent = 0;
547
548                 void Pad ()
549                 {
550                         for (int i = 0; i < indent; i++)
551                                 Console.Write ("    ");
552                 }
553
554                 void EmitDebug ()
555                 {
556                         //Console.WriteLine (Environment.StackTrace);
557                         Pad ();
558                         Console.WriteLine ("START");
559                         indent++;
560                         Pad ();
561                         Console.WriteLine ("NeedThis=" + NeedThis);
562                         foreach (LocalInfo li in locals){
563                                 Pad ();
564                                 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
565                         }
566                         
567                         foreach (ScopeInfo si in children)
568                                 si.EmitDebug ();
569                         indent--;
570                         Pad ();
571                         Console.WriteLine ("END");
572                 }
573                 
574                 public string MakeHelperName ()
575                 {
576                         return String.Format ("<>AnonHelp<{0}>", id);
577                 }
578
579                 private string MakeFieldName (string local_name)
580                 {
581                         return "<" + id + ":" + local_name + ">";
582                 }
583
584                 public void EmitScopeType (EmitContext ec)
585                 {
586                         // EmitDebug ();
587
588                         if (ScopeTypeBuilder != null)
589                                 return;
590                         
591                         TypeBuilder container = ec.TypeContainer.TypeBuilder;
592
593                         CaptureContext.Host.CreateScopeType (ec, this);
594                         
595                         if (NeedThis)
596                                 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
597
598                         if (ParentScope != null){
599                                 if (ParentScope.ScopeTypeBuilder == null){
600                                         throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
601                                 }
602
603                                 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
604                                         ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
605                                                                                    FieldAttributes.Assembly);
606                         }
607                         
608                         if (NeedThis && ParentScope != null)
609                                 throw new Exception ("I was not expecting THIS && having a parent");
610
611                         foreach (LocalInfo info in locals)
612                                 info.FieldBuilder = ScopeTypeBuilder.DefineField (
613                                         MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
614
615                         if (HostsParameters){
616                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
617                                 
618                                 foreach (DictionaryEntry de in captured_parameters){
619                                         string name = (string) de.Key;
620                                         CapturedParameter cp = (CapturedParameter) de.Value;
621                                         FieldBuilder fb;
622                                         
623                                         fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
624                                         cp.FieldBuilder = fb;
625                                 }
626                         }
627
628                         foreach (ScopeInfo si in children){
629                                 si.EmitScopeType (ec);
630                         }
631                 }
632
633                 public void CloseTypes ()
634                 {
635                         RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
636                         foreach (ScopeInfo si in children)
637                                 si.CloseTypes ();
638                 }
639
640                 //
641                 // Emits the initialization code for the scope
642                 //
643                 public void EmitInitScope (EmitContext ec)
644                 {
645                         ILGenerator ig = ec.ig;
646
647                         if (inited)
648                                 return;
649
650                         if (ScopeConstructor == null)
651                                 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
652                         
653                         if (!CaptureContext.Host.IsIterator) {
654                                 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
655                                 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
656                                 ig.Emit (OpCodes.Stloc, scope_instance);
657                         }
658
659                         if (THIS != null){
660                                 if (CaptureContext.Host.IsIterator) {
661                                         ig.Emit (OpCodes.Ldarg_0);
662                                         ig.Emit (OpCodes.Ldarg_1);
663                                 } else {
664                                         ig.Emit (OpCodes.Ldloc, scope_instance);
665                                         ig.Emit (OpCodes.Ldarg_0);
666                                 }
667                                 ig.Emit (OpCodes.Stfld, THIS);
668                         }
669
670                         //
671                         // Copy the parameter values, if any
672                         //
673                         int extra = ec.IsStatic ? 0 : 1;
674                         if (CaptureContext.Host.IsIterator)
675                                 extra++;
676                         if (HostsParameters){
677                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
678                                 
679                                 foreach (DictionaryEntry de in captured_parameters){
680                                         CapturedParameter cp = (CapturedParameter) de.Value;
681
682                                         EmitScopeInstance (ig);
683                                         ParameterReference.EmitLdArg (ig, cp.Idx + extra);
684                                         ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
685                                 }
686                         }
687
688                         if (ParentScope != null){
689                                 if (!ParentScope.inited)
690                                         ParentScope.EmitInitScope (ec);
691
692                                 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
693                                         //
694                                         // Only emit initialization in our capturecontext world
695                                         //
696                                         if (ParentScope.CaptureContext == CaptureContext){
697                                                 EmitScopeInstance (ig);
698                                                 ParentScope.EmitScopeInstance (ig);
699                                                 ig.Emit (OpCodes.Stfld, ParentLink);
700                                         } else {
701                                                 EmitScopeInstance (ig);
702                                                 ig.Emit (OpCodes.Ldarg_0);
703                                                 ig.Emit (OpCodes.Stfld, ParentLink);
704                                         }
705                                 }
706                         }
707                         inited = true;
708                 }
709
710                 public void EmitScopeInstance (ILGenerator ig)
711                 {
712                         if (CaptureContext.Host.IsIterator)
713                                 ig.Emit (OpCodes.Ldarg_0);
714                         else
715                                 ig.Emit (OpCodes.Ldloc, scope_instance);
716                 }
717
718                 static void DoPath (StringBuilder sb, ScopeInfo start)
719                 {
720                         if (start.ParentScope != null){
721                                 DoPath (sb, start.ParentScope);
722                                 sb.Append (", ");
723                         }
724                         sb.Append ((start.id).ToString ());
725                 }
726                 
727                 public override string ToString ()
728                 {
729                         StringBuilder sb = new StringBuilder ();
730                         
731                         sb.Append ("{");
732                         if (CaptureContext != null){
733                                 sb.Append (CaptureContext.ToString ());
734                                 sb.Append (":");
735                         }
736
737                         DoPath (sb, this);
738                         sb.Append ("}");
739
740                         return sb.ToString ();
741                 }
742         }
743
744         //
745         // CaptureContext objects are created on demand if a method has
746         // anonymous methods and kept on the ToplevelBlock.
747         //
748         // If they exist, all ToplevelBlocks in the containing block are
749         // linked together (children pointing to their parents).
750         //
751         public class CaptureContext {
752                 public static int count;
753                 public int cc_id;
754                 public Location loc;
755                 
756                 //
757                 // Points to the toplevel block that owns this CaptureContext
758                 //
759                 ToplevelBlock toplevel_owner;
760                 Hashtable scopes = new Hashtable ();
761                 bool have_captured_vars = false;
762                 bool referenced_this = false;
763                 ScopeInfo topmost = null;
764
765                 //
766                 // Captured fields
767                 //
768                 Hashtable captured_fields = new Hashtable ();
769                 Hashtable captured_variables = new Hashtable ();
770                 public Hashtable captured_parameters = new Hashtable ();
771                 public AnonymousContainer Host;
772                 
773                 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
774                                        AnonymousContainer host)
775                 {
776                         cc_id = count++;
777                         this.toplevel_owner = toplevel_owner;
778                         this.loc = loc;
779
780                         if (host != null)
781                                 Host = host;
782                 }
783
784                 void DoPath (StringBuilder sb, CaptureContext cc)
785                 {
786                         if (cc.ParentCaptureContext != null){
787                                 DoPath (sb, cc.ParentCaptureContext);
788                                 sb.Append (".");
789                         }
790                         sb.Append (cc.cc_id.ToString ());
791                 }
792
793                 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
794                 {
795                         toplevel_owner = new_toplevel;
796                         Host = new_host;
797
798                         for (CaptureContext cc = ParentCaptureContext; cc != null;
799                              cc = cc.ParentCaptureContext) {
800                                 cc.Host = new_host;
801                         }
802                 }
803                 
804                 public override string ToString ()
805                 {
806                         StringBuilder sb = new StringBuilder ();
807                         sb.Append ("[");
808                         DoPath (sb, this);
809                         sb.Append ("]");
810                         return sb.ToString ();
811                 }
812
813                 public ToplevelBlock ParentToplevel {
814                         get {
815                                 return toplevel_owner.Container;
816                         }
817                 }
818
819                 public CaptureContext ParentCaptureContext {
820                         get {
821                                 ToplevelBlock parent = ParentToplevel;
822                                 
823                                 return (parent == null) ? null : parent.CaptureContext;
824                         }
825                 }
826
827                 // Returns the deepest of two scopes
828                 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
829                 {
830                         ScopeInfo p;
831
832                         if (a == null)
833                                 return b;
834                         if (b == null)
835                                 return a;
836                         if (a == b)
837                                 return a;
838
839                         //
840                         // If they Scopes are on the same CaptureContext, we do the double
841                         // checks just so if there is an invariant change in the future,
842                         // we get the exception at the end
843                         //
844                         for (p = a; p != null; p = p.ParentScope)
845                                 if (p == b)
846                                         return a;
847                         
848                         for (p = b; p != null; p = p.ParentScope)
849                                 if (p == a)
850                                         return b;
851
852                         CaptureContext ca = a.CaptureContext;
853                         CaptureContext cb = b.CaptureContext;
854
855                         for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
856                                 if (c == cb)
857                                         return a;
858
859                         for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
860                                 if (c == ca)
861                                         return b;
862                         throw new Exception ("Should never be reached");
863                 }
864
865                 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
866                 {
867                         am.Scope = Deepest (am.Scope, scope);
868                 }
869
870                 void LinkScope (ScopeInfo scope, int id)
871                 {
872                         ScopeInfo parent = (ScopeInfo) scopes [id];
873                         scope.ParentScope = parent;
874                         parent.AddChild (scope);
875
876                         if (scope == topmost)
877                                 topmost = parent;
878                 }
879                 
880                 public void AddLocal (AnonymousContainer am, LocalInfo li)
881                 {
882                         if (li.Block.Toplevel != toplevel_owner){
883                                 ParentCaptureContext.AddLocal (am, li);
884                                 return;
885                         }
886                         int block_id = li.Block.ID;
887                         ScopeInfo scope;
888                         if (scopes [block_id] == null){
889                                 scope = new ScopeInfo (this, li.Block);
890                                 scopes [block_id] = scope;
891                         } else
892                                 scope = (ScopeInfo) scopes [block_id];
893
894                         if (topmost == null){
895                                 topmost = scope;
896                         } else {
897                                 // Link to parent
898
899                                 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
900                                         if (scopes [b.ID] != null){
901                                                 LinkScope (scope, b.ID);
902                                                 break;
903                                         }
904                                 }
905
906                                 if (scope.ParentScope == null && ParentCaptureContext != null){
907                                         CaptureContext pcc = ParentCaptureContext;
908                                         
909                                         for (Block b = am.ContainingBlock; b != null; b = b.Parent){
910                                                 if (pcc.scopes [b.ID] != null){
911                                                         pcc.LinkScope (scope, b.ID);
912                                                         break;
913                                                 }
914                                         }
915                                 }
916                         }
917
918                         //
919                         // Adjust the owner
920                         //
921                         if (Host != null)
922                                 AdjustMethodScope (Host, topmost);
923
924                         //
925                         // Adjust the user
926                         //
927                         AdjustMethodScope (am, scope);
928                         
929                         if (captured_variables [li] != null)
930                                 return;
931                         
932                         have_captured_vars = true;
933                         captured_variables [li] = li;
934                         scope.AddLocal (li);
935                 }
936
937                 //
938                 // Retursn the CaptureContext for the block that defines the parameter `name'
939                 //
940                 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
941                 {
942                         ToplevelBlock container = current.Container;
943                         if (container != null){
944                                 CaptureContext cc = _ContextForParameter (container, name);
945                                 if (cc != null)
946                                         return cc;
947                         }
948                         if (current.IsParameterReference (name))
949                                 return current.ToplevelBlockCaptureContext;
950                         return null;
951                 }
952
953                 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
954                 {
955                         CaptureContext cc = _ContextForParameter (current, name);
956                         if (cc == null)
957                                 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
958                         return cc;
959                 }
960                 
961                 //
962                 // Records the captured parameter at the appropriate CaptureContext
963                 //
964                 public void AddParameter (EmitContext ec, AnonymousContainer am,
965                                           string name, Type t, int idx)
966                 {
967                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
968
969                         cc.AddParameterToContext (am, name, t, idx);
970                 }
971
972                 //
973                 // Records the parameters in the context
974                 //
975                 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
976                 {
977                         if (captured_parameters == null)
978                                 captured_parameters = new Hashtable ();
979                         if (captured_parameters [name] != null)
980                                 return;
981                         captured_parameters [name] = new CapturedParameter (t, idx);
982
983                         if (topmost == null){
984                                 //
985                                 // Create one ScopeInfo, if there are none.
986                                 //
987                                 topmost = new ScopeInfo (this, toplevel_owner);
988                                 scopes [toplevel_owner.ID] = topmost;
989                         } else {
990                                 //
991                                 // If the topmost ScopeInfo is not at the topblock level, insert
992                                 // a new ScopeInfo there.
993                                 //
994                                 // FIXME: This code probably should be evolved to be like the code
995                                 // in AddLocal
996                                 //
997                                 if (topmost.ScopeBlock != toplevel_owner){
998                                         ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
999                                         ScopeInfo old_top = topmost;
1000                                         scopes [toplevel_owner.ID] = topmost;
1001                                         topmost.ParentScope = par_si;
1002                                         topmost = par_si;
1003                                         topmost.AddChild (old_top);
1004                                 }
1005                         }
1006                         
1007                         topmost.HostsParameters = true;
1008                         AdjustMethodScope (am, topmost);
1009                 }
1010
1011                 //
1012                 // Captured fields are only recorded on the topmost CaptureContext, because that
1013                 // one is the one linked to the owner of instance fields
1014                 //
1015                 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1016                 {
1017                         if (fe.FieldInfo.IsStatic)
1018                                 throw new Exception ("Attempt to register a static field as a captured field");
1019                         CaptureContext parent = ParentCaptureContext;
1020                         if (parent != null) {
1021                                 parent.AddField (ec, am, fe);
1022                                 return;
1023                         }
1024
1025                         if (topmost == null){
1026                                 //
1027                                 // Create one ScopeInfo, if there are none.
1028                                 //
1029                                 topmost = new ScopeInfo (this, toplevel_owner);
1030                                 scopes [toplevel_owner.ID] = topmost;
1031                         }
1032                         
1033                         AdjustMethodScope (am, topmost);
1034                 }
1035
1036                 public void CaptureThis ()
1037                 {
1038                         CaptureContext parent = ParentCaptureContext;
1039                         if (parent != null)
1040                                 parent.CaptureThis ();
1041                         referenced_this = true;
1042                 }
1043
1044                 public bool HaveCapturedVariables {
1045                         get {
1046                                 return have_captured_vars;
1047                         }
1048                 }
1049                 
1050                 public bool HaveCapturedFields {
1051                         get {
1052                                 CaptureContext parent = ParentCaptureContext;
1053                                 if (parent != null)
1054                                         return parent.HaveCapturedFields;
1055                                 return captured_fields.Count > 0;
1056                         }
1057                 }
1058
1059                 public bool IsCaptured (LocalInfo local)
1060                 {
1061                         foreach (ScopeInfo si in scopes.Values){
1062                                 if (si.IsCaptured (local))
1063                                         return true;
1064                         }
1065                         return false;
1066                 }
1067
1068                 //
1069                 // Returns whether the parameter is captured
1070                 //
1071                 public bool IsParameterCaptured (string name)
1072                 {
1073                         if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1074                                 return true;
1075                         
1076                         if (captured_parameters != null)
1077                                 return captured_parameters [name] != null;
1078                         return false;
1079                 }
1080
1081                 public void EmitAnonymousHelperClasses (EmitContext ec)
1082                 {
1083                         if (topmost != null){
1084                                 topmost.NeedThis = HaveCapturedFields || referenced_this;
1085                                 topmost.EmitScopeType (ec);
1086                         } 
1087                 }
1088
1089                 public void CloseAnonymousHelperClasses ()
1090                 {
1091                         if (topmost != null)
1092                                 topmost.CloseTypes ();
1093                 }
1094
1095                 public void EmitInitScope (EmitContext ec)
1096                 {
1097                         EmitAnonymousHelperClasses (ec);
1098                         if (topmost != null)
1099                                 topmost.EmitInitScope (ec);
1100                 }
1101
1102                 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1103                 {
1104                         ScopeInfo si;
1105                         
1106                         si = (ScopeInfo) scopes [b.ID];
1107                         if (si == null)
1108                                 throw new Exception ("Si is null for block " + b.ID);
1109                         si.EmitInitScope (ec);
1110
1111                         return si;
1112                 }
1113
1114                 //
1115                 // Emits the opcodes necessary to load the instance of the captured
1116                 // variable in `li'
1117                 //
1118                 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1119                                                           AnonymousContainer am)
1120                 {
1121                         ILGenerator ig = ec.ig;
1122                         ScopeInfo si;
1123
1124                         if (li.Block.Toplevel == toplevel_owner){
1125                                 si = GetScopeFromBlock (ec, li.Block);
1126                                 si.EmitScopeInstance (ig);
1127                                 return;
1128                         }
1129
1130                         si = am.Scope;
1131                         ig.Emit (OpCodes.Ldarg_0);
1132                         if (si != null){
1133                                 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1134                                         return;
1135                                 }
1136
1137                                 while (si.ScopeBlock.ID != li.Block.ID){
1138                                         if (si.ParentLink != null)
1139                                                 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1140                                         si = si.ParentScope;
1141                                         if (si == null) {
1142                                                 si = am.Scope;
1143                                                 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1144                                                 while (si.ScopeBlock.ID != li.Block.ID){
1145                                                         Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1146                                                         si = si.ParentScope;
1147                                                 }
1148
1149                                                 throw new Exception (
1150                                                              String.Format ("Never found block {0} starting at {1} while looking up {2}",
1151                                                                             li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1152                                         }
1153                                 }
1154                         }
1155                 }
1156
1157                 //
1158                 // Internal routine that loads the instance to reach parameter `name'
1159                 //
1160                 void EmitParameterInstance (EmitContext ec, string name)
1161                 {
1162                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1163                         if (cc != this){
1164                                 cc.EmitParameterInstance (ec, name);
1165                                 return;
1166                         }
1167
1168                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1169                         if (par_info != null){
1170                                 // 
1171                                 // FIXME: implementing this.
1172                                 //
1173                         }
1174                         ILGenerator ig = ec.ig;
1175
1176                         ScopeInfo si;
1177
1178                         if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1179                                 si = GetScopeFromBlock (ec, toplevel_owner);
1180                                 si.EmitScopeInstance (ig);
1181                         } else {
1182                                 si = ec.CurrentAnonymousMethod.Scope;
1183                                 ig.Emit (OpCodes.Ldarg_0);
1184                         }
1185
1186                         if (si != null){
1187                                 while (si.ParentLink != null) {
1188                                         ig.Emit (OpCodes.Ldfld, si.ParentLink);
1189                                         si = si.ParentScope;
1190                                 } 
1191                         }
1192                 }
1193
1194                 //
1195                 // Emits the code necessary to load the parameter named `name' within
1196                 // an anonymous method.
1197                 //
1198                 public void EmitParameter (EmitContext ec, string name)
1199                 {
1200                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1201                         if (cc != this){
1202                                 cc.EmitParameter (ec, name);
1203                                 return;
1204                         }
1205                         EmitParameterInstance (ec, name);
1206                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1207                         if (par_info != null){
1208                                 // 
1209                                 // FIXME: implementing this.
1210                                 //
1211                         }
1212                         ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1213                 }
1214
1215                 //
1216                 // Implements the assignment of `source' to the paramenter named `name' within
1217                 // an anonymous method.
1218                 //
1219                 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1220                 {
1221                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1222                         if (cc != this){
1223                                 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1224                                 return;
1225                         }
1226                         ILGenerator ig = ec.ig;
1227                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1228
1229                         EmitParameterInstance (ec, name);
1230                         source.Emit (ec);
1231                         if (leave_copy)
1232                                 ig.Emit (OpCodes.Dup);
1233                         ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1234                 }
1235
1236                 //
1237                 // Emits the address for the parameter named `name' within
1238                 // an anonymous method.
1239                 //
1240                 public void EmitAddressOfParameter (EmitContext ec, string name)
1241                 {
1242                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1243                         if (cc != this){
1244                                 cc.EmitAddressOfParameter (ec, name);
1245                                 return;
1246                         }
1247                         EmitParameterInstance (ec, name);
1248                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1249                         ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1250                 }
1251
1252                 //
1253                 // The following methods are only invoked on the host for the
1254                 // anonymous method.
1255                 //
1256                 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1257                 {
1258                         ILGenerator ig = target.ig;
1259                         ScopeInfo si = am.Scope;
1260
1261                         if (si == null){
1262                                 ig.Emit (OpCodes.Ldarg_0);
1263                                 return;
1264                         }
1265
1266                         si.EmitInitScope (target);
1267                         si.EmitScopeInstance (ig);
1268                 }
1269
1270                 ArrayList all_scopes = new ArrayList ();
1271                 
1272                 public void AddScope (ScopeInfo si)
1273                 {
1274                         all_scopes.Add (si);
1275                         toplevel_owner.RegisterCaptureContext (this);
1276                 }
1277
1278                 //
1279                 // Links any scopes that were not linked previously
1280                 //
1281                 public void AdjustScopes ()
1282                 {
1283                         foreach (ScopeInfo scope in all_scopes){
1284                                 if (scope.ParentScope != null)
1285                                         continue;
1286
1287                                 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1288                                         if (scopes [b.ID] != null){
1289                                                 LinkScope (scope, b.ID);
1290                                                 break;
1291                                         }
1292                                 }
1293
1294                                 if (scope.ParentScope == null && ParentCaptureContext != null){
1295                                         CaptureContext pcc = ParentCaptureContext;
1296                                         
1297                                         for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1298                                                 if (pcc.scopes [b.ID] != null){
1299                                                         pcc.LinkScope (scope, b.ID);
1300                                                         break;
1301                                                 }
1302                                         }
1303                                 }
1304                         }
1305                 }
1306         }
1307 }