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