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