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