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