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