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