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