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