2004-11-04 Zoltan Varga <vargaz@freemail.hu>
[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 class AnonymousMethod : Expression {
24                 // Used to generate unique method names.
25                 static int anonymous_method_count;
26                     
27                 // An array list of AnonymousMethodParameter or null
28                 public Parameters Parameters;
29
30                 //
31                 // The block that makes up the body for the anonymous mehtod
32                 //
33                 public ToplevelBlock Block;
34
35                 //
36                 // The container block for this anonymous method.
37                 //
38                 public Block ContainingBlock;
39
40                 //
41                 // The implicit method we create
42                 //
43                 public Method method;
44
45                 MethodInfo invoke_mb;
46                 
47                 // The emit context for the anonymous method
48                 public EmitContext aec;
49                 public InternalParameters amp;
50                 bool unreachable;
51
52                 //
53                 // The modifiers applied to the method, we aggregate them
54                 //
55                 int method_modifiers = Modifiers.INTERNAL;
56                 
57                 //
58                 // During the resolve stage of the anonymous method body,
59                 // we discover the actual scope where we are hosted, or
60                 // null to host the method in the same class
61                 //
62                 public ScopeInfo Scope;
63
64                 //
65                 // Points to our container anonymous method if its present
66                 //
67                 public AnonymousMethod ContainerAnonymousMethod;
68                 
69                 public AnonymousMethod (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l)
70                 {
71                         Parameters = parameters;
72                         Block = block;
73                         loc = l;
74
75                         //
76                         // The order is important: this setups the CaptureContext tree hierarchy.
77                         //
78                         container.SetHaveAnonymousMethods (l, this);
79                         block.SetHaveAnonymousMethods (l, this);
80                 }
81
82                 public override Expression DoResolve (EmitContext ec)
83                 {
84                         //
85                         // Set class type, set type
86                         //
87
88                         eclass = ExprClass.Value;
89                         
90                         //
91                         // This hack means `The type is not accessible
92                         // anywhere', we depend on special conversion
93                         // rules.
94                         // 
95                         type = TypeManager.anonymous_method_type;
96
97                         return this;
98                 }
99
100                 public override void Emit (EmitContext ec)
101                 {
102                         // nothing, as we only exist to not do anything.
103                 }
104
105                 //
106                 // Creates the host for the anonymous method
107                 //
108                 bool CreateMethodHost (EmitContext ec, Type return_type)
109                 {
110                         //
111                         // Crude hack follows: we replace the TypeBuilder during the
112                         // definition to get the method hosted in the right class
113                         //
114                         
115                         TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
116                         TypeBuilder type_host = Scope == null ? current_type : Scope.ScopeTypeBuilder;
117
118                         if (current_type == null)
119                                 throw new Exception ("The current_type is null");
120                         
121                         if (type_host == null)
122                                 throw new Exception ("Type host is null");
123                         
124                         if (current_type == type_host && ec.IsStatic){
125                                 if (ec.IsStatic)
126                                         method_modifiers |= Modifiers.STATIC;
127                                 current_type = null;
128                         } 
129
130                         method = new Method (
131                                 (TypeContainer) ec.TypeContainer,
132                                 new TypeExpression (return_type, loc),
133                                 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
134                                 Parameters, null, loc);
135                         method.Block = Block;
136
137                         
138                         //
139                         // Swap the TypeBuilder while we define the method, then restore
140                         //
141                         if (current_type != null)
142                                 ec.TypeContainer.TypeBuilder = type_host;
143                         bool res = method.Define ();
144                         if (current_type != null)
145                                 ec.TypeContainer.TypeBuilder = current_type;
146                         return res;
147                 }
148                 
149                 void Error_ParameterMismatch (Type t)
150                 {
151                         Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
152                                       "{0}' since there is a parameter mismatch", t);
153                 }
154
155                 //
156                 // Returns true if this anonymous method can be implicitly
157                 // converted to the delegate type `delegate_type'
158                 //
159                 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
160                 {
161                         //
162                         // At this point its the first time we know the return type that is 
163                         // needed for the anonymous method.  We create the method here.
164                         //
165
166                         invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
167                         ParameterData invoke_pd = Invocation.GetParameterData (invoke_mb);
168
169                         //
170                         // If implicit parameters are set, then we must check for out in the parameters
171                         // and flag it accordingly.
172                         //
173                         bool out_invalid_check = false;
174                         
175                         if (Parameters == null){
176                                 int i, j;
177                                 out_invalid_check = true;
178                                 
179                                 //
180                                 // We provide a set of inaccessible parameters
181                                 //
182                                 int params_idx = -1;
183                                 for (i = 0; i < invoke_pd.Count; i++){
184                                         if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
185                                                 params_idx = i;
186                                 }
187                                 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
188                                 Parameter [] fixedpars = new Parameter [n];
189                                 
190                                 for (i =  j = 0; i < invoke_pd.Count; i++){
191                                         if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
192                                                 continue;
193                                         fixedpars [j] = new Parameter (
194                                                 new TypeExpression (invoke_pd.ParameterType (i), loc),
195                                                 "+" + j, invoke_pd.ParameterModifier (i), null);
196                                         j++;
197                                 }
198                                 
199                                 Parameter variable = null;
200                                 if (params_idx != -1){
201                                         variable = new Parameter (
202                                                 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
203                                                 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null);
204                                 }
205
206                                 Parameters = new Parameters (fixedpars, variable, loc);
207                         }
208                         
209                         //
210                         // First, parameter types of `delegate_type' must be compatible
211                         // with the anonymous method.
212                         //
213                         amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
214                         
215                         if (amp.Count != invoke_pd.Count){
216                                 if (!probe){
217                                         Report.Error (1593, loc, 
218                                               "Anonymous method has {0} parameters, while delegate requires {1}",
219                                               amp.Count, invoke_pd.Count);
220                                         Error_ParameterMismatch (delegate_type);
221                                 }
222                                 return null;
223                         }
224                         
225                         for (int i = 0; i < amp.Count; i++){
226                                 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
227                                 if (amp_mod != invoke_pd.ParameterModifier (i)){
228                                         if (!probe){
229                                                 Report.Error (1676, loc, 
230                                                       "Signature mismatch in parameter modifier for parameter #0", i + 1);
231                                                 Error_ParameterMismatch (delegate_type);
232                                         }
233                                         return null;
234                                 }
235                                 
236                                 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
237                                         if (!probe){
238                                                 Report.Error (1678, loc, 
239                                                                       "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i + 1,
240                                                                       TypeManager.CSharpName (invoke_pd.ParameterType (i)),
241                                                                       TypeManager.CSharpName (amp.ParameterType (i)));
242                                                 Error_ParameterMismatch (delegate_type);
243                                         }
244                                         return null;
245                                 }
246                                 
247                                 if (out_invalid_check && (invoke_pd.ParameterModifier (i) & Parameter.Modifier.OUT) != 0){
248                                         if (!probe){
249                                                 Report.Error (1676, loc,"Parameter {0} must include the `out' modifier ", i+1);
250                                                 Error_ParameterMismatch (delegate_type);
251                                         }
252                                         return null;
253                                 }
254                         }
255
256                         //
257                         // If we are only probing, return ourselves
258                         //
259                         if (probe)
260                                 return this;
261                         
262                         //
263                         // Second: the return type of the delegate must be compatible with 
264                         // the anonymous type.   Instead of doing a pass to examine the block
265                         // we satisfy the rule by setting the return type on the EmitContext
266                         // to be the delegate type return type.
267                         //
268
269                         //MethodBuilder builder = method_data.MethodBuilder;
270                         //ILGenerator ig = builder.GetILGenerator ();
271
272                         
273                         aec = new EmitContext (
274                                 ec.TypeContainer, ec.DeclSpace, loc, null,
275                                 invoke_mb.ReturnType,
276                                 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
277                                 (ec.InUnsafe ? Modifiers.UNSAFE : 0),
278                                 /* No constructor */ false);
279
280                         aec.CurrentAnonymousMethod = this;
281                         ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
282                         ContainingBlock = ec.CurrentBlock;
283                 
284                         if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
285                                 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
286
287                         return null;
288                 }
289
290                 public MethodBuilder GetMethodBuilder ()
291                 {
292                         return method.MethodData.MethodBuilder;
293                 }
294                 
295                 public void EmitMethod (EmitContext ec)
296                 {
297                         if (!CreateMethodHost (ec, invoke_mb.ReturnType))
298                                 return;
299
300                         MethodBuilder builder = GetMethodBuilder ();
301                         ILGenerator ig = builder.GetILGenerator ();
302                         aec.ig = ig;
303                         
304                         Parameters.LabelParameters (aec, builder, loc);
305
306                         //
307                         // Adjust based on the computed state of the
308                         // method from CreateMethodHost
309                         
310                         if ((method_modifiers & Modifiers.STATIC) != 0)
311                                 aec.IsStatic = true;
312                         
313                         aec.EmitMeta (Block, amp);
314                         aec.EmitResolvedTopBlock (Block, unreachable);
315                 }
316         }
317
318         //
319         // This will emit the code for the delegate, as well delegate creation on the host
320         //
321         public class AnonymousDelegate : DelegateCreation {
322                 AnonymousMethod am;
323
324                 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
325                 {
326                         type = target_type;
327                         loc = l;
328                         this.am = am;
329                 }
330
331                 public override Expression DoResolve (EmitContext ec)
332                 {
333                         eclass = ExprClass.Value;
334                         return this;
335                 }
336                 
337                 public override void Emit (EmitContext ec)
338                 {
339                         am.EmitMethod (ec);
340
341                         //
342                         // Now emit the delegate creation.
343                         //
344                         if ((am.method.ModFlags & Modifiers.STATIC) == 0)
345                                 delegate_instance_expression = new AnonymousInstance (am);
346                         
347                         Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
348                         constructor_method = ((MethodGroupExpr) ml).Methods [0];
349                         delegate_method = am.GetMethodBuilder ();
350                         base.Emit (ec);
351                 }
352
353                 class AnonymousInstance : Expression {
354                         AnonymousMethod am;
355                         
356                         public AnonymousInstance (AnonymousMethod am)
357                         {
358                                 this.am = am;
359                                 eclass = ExprClass.Value;
360                         }
361
362                         public override Expression DoResolve (EmitContext ec)
363                         {
364                                 return this;
365                         }
366                         
367                         public override void Emit (EmitContext ec)
368                         {
369                                 am.aec.EmitMethodHostInstance (ec, am);
370                         }
371                 }
372         }
373
374         class CapturedParameter {
375                 public Type Type;
376                 public FieldBuilder FieldBuilder;
377                 public int Idx;
378
379                 public CapturedParameter (Type type, int idx)
380                 {
381                         Type = type;
382                         Idx = idx;
383                 }
384         }
385
386         //
387         // Here we cluster all the variables captured on a given scope, we also
388         // keep some extra information that might be required on each scope.
389         //
390         public class ScopeInfo {
391                 public CaptureContext CaptureContext;
392                 public ScopeInfo ParentScope;
393                 public Block ScopeBlock;
394                 public bool NeedThis = false;
395                 public bool HostsParameters = false;
396                 
397                 // For tracking the number of scopes created.
398                 public int id;
399                 static int count;
400                 bool inited = false;
401                 
402                 ArrayList locals = new ArrayList ();
403                 ArrayList children = new ArrayList ();
404
405                 //
406                 // The types and fields generated
407                 //
408                 public TypeBuilder ScopeTypeBuilder;
409                 public ConstructorBuilder ScopeConstructor;
410                 public FieldBuilder THIS;
411                 public FieldBuilder ParentLink;
412
413                 //
414                 // Points to the object of type `ScopeTypeBuilder' that
415                 // holds the data for the scope
416                 //
417                 public LocalBuilder ScopeInstance;
418
419                 
420                 public ScopeInfo (CaptureContext cc, Block b)
421                 {
422                         CaptureContext = cc;
423                         ScopeBlock = b;
424                         id = count++;
425
426                         cc.AddScope (this);
427                 }
428
429                 public void AddLocal (LocalInfo li)
430                 {
431                         if (locals.Contains (li))
432                                 return;
433
434                         locals.Add (li);
435                 }
436
437                 public bool IsCaptured (LocalInfo li)
438                 {
439                         return locals.Contains (li);
440                 }
441                 
442                 public void AddChild (ScopeInfo si)
443                 {
444                         if (children.Contains (si))
445                                 return;
446                         children.Add (si);
447                 }
448
449                 static int indent = 0;
450
451                 void Pad ()
452                 {
453                         for (int i = 0; i < indent; i++)
454                                 Console.Write ("    ");
455                 }
456
457                 void EmitDebug ()
458                 {
459                         //Console.WriteLine (Environment.StackTrace);
460                         Pad ();
461                         Console.WriteLine ("START");
462                         indent++;
463                         Pad ();
464                         Console.WriteLine ("NeedThis=" + NeedThis);
465                         foreach (LocalInfo li in locals){
466                                 Pad ();
467                                 Console.WriteLine ("var {0}", li.Name);
468                         }
469                         
470                         foreach (ScopeInfo si in children)
471                                 si.EmitDebug ();
472                         indent--;
473                         Pad ();
474                         Console.WriteLine ("END");
475                 }
476                 
477                 public string MakeHelperName ()
478                 {
479                         return String.Format ("<>AnonHelp<{0}>", id);
480                 }
481
482                 public void EmitScopeConstructor ()
483                 {
484                         Type [] constructor_types = TypeManager.NoTypes;
485                         Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
486                         ScopeConstructor = ScopeTypeBuilder.DefineConstructor (
487                                 MethodAttributes.Public | MethodAttributes.HideBySig |
488                                 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
489                                 CallingConventions.HasThis, constructor_types);
490                         InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
491                         TypeManager.RegisterMethod (ScopeConstructor, parameter_info, constructor_types);
492
493                         ILGenerator cig = ScopeConstructor.GetILGenerator ();
494                         cig.Emit (OpCodes.Ldarg_0);
495                         cig.Emit (OpCodes.Call, TypeManager.object_ctor);
496                         cig.Emit (OpCodes.Ret);
497                 }
498                 
499                 public void EmitScopeType (EmitContext ec)
500                 {
501                         //EmitDebug ();
502
503                         if (ScopeTypeBuilder != null)
504                                 return;
505                         
506                         ILGenerator ig = ec.ig;
507                         TypeBuilder container = ec.TypeContainer.TypeBuilder;
508
509                         ScopeTypeBuilder = container.DefineNestedType (
510                                 MakeHelperName (), TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly,
511                                 TypeManager.object_type, null);
512
513                         if (NeedThis)
514                                 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
515
516                         if (ParentScope != null){
517                                 if (ParentScope.ScopeTypeBuilder == null){
518                                         throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
519                                 }
520                                 
521                                 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
522                                                                            FieldAttributes.Assembly);
523                         }
524                         
525                         if (NeedThis && ParentScope != null)
526                                 throw new Exception ("I was not expecting THIS && having a parent");
527
528                         foreach (LocalInfo info in locals){
529                                 info.FieldBuilder = ScopeTypeBuilder.DefineField (
530                                         info.Name, info.VariableType, FieldAttributes.Assembly);
531                         }
532
533                         if (HostsParameters){
534                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
535                                 
536                                 foreach (DictionaryEntry de in captured_parameters){
537                                         string name = (string) de.Key;
538                                         CapturedParameter cp = (CapturedParameter) de.Value;
539                                         FieldBuilder fb;
540                                         
541                                         fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
542                                         cp.FieldBuilder = fb;
543                                 }
544                         }
545
546                         EmitScopeConstructor ();
547                         foreach (ScopeInfo si in children){
548                                 si.EmitScopeType (ec);
549                         }
550                 }
551
552                 public void CloseTypes ()
553                 {
554                         RootContext.RegisterHelperClass (ScopeTypeBuilder);
555                         foreach (ScopeInfo si in children)
556                                 si.CloseTypes ();
557                 }
558
559                 //
560                 // Emits the initialization code for the scope
561                 //
562                 public void EmitInitScope (EmitContext ec)
563                 {
564                         ILGenerator ig = ec.ig;
565
566                         if (inited)
567                                 return;
568
569                         ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
570                         ScopeInstance = ig.DeclareLocal (ScopeTypeBuilder);
571                         ig.Emit (OpCodes.Stloc, ScopeInstance);
572
573                         if (THIS != null){
574                                 ig.Emit (OpCodes.Ldloc, ScopeInstance);
575                                 ig.Emit (OpCodes.Ldarg_0);
576                                 ig.Emit (OpCodes.Stfld, THIS);
577                         }
578
579                         //
580                         // Copy the parameter values, if any
581                         //
582                         int extra = ec.IsStatic ? 0 : 1;
583                         if (HostsParameters){
584                                 Hashtable captured_parameters = CaptureContext.captured_parameters;
585                                 
586                                 foreach (DictionaryEntry de in captured_parameters){
587                                         string name = (string) de.Key;
588                                         CapturedParameter cp = (CapturedParameter) de.Value;
589
590                                         ig.Emit (OpCodes.Ldloc, ScopeInstance);
591                                         ParameterReference.EmitLdArg (ig, cp.Idx + extra);
592                                         ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
593                                 }
594                         }
595                         
596                         if (ParentScope != null){
597                                 //
598                                 // Only emit initialization in our capturecontext world
599                                 //
600                                 if (ParentScope.CaptureContext == CaptureContext){
601                                         ig.Emit (OpCodes.Ldloc, ScopeInstance);
602                                         ig.Emit (OpCodes.Ldloc, ParentScope.ScopeInstance);
603                                         ig.Emit (OpCodes.Stfld, ParentLink);
604                                 } else {
605                                         ig.Emit (OpCodes.Ldloc, ScopeInstance);
606                                         ig.Emit (OpCodes.Ldarg_0);
607                                         ig.Emit (OpCodes.Stfld, ParentLink);
608                                 }
609                         }
610                         inited = true;
611                 }
612
613                 static void DoPath (StringBuilder sb, ScopeInfo start)
614                 {
615                         if (start.ParentScope != null){
616                                 DoPath (sb, start.ParentScope);
617                                 sb.Append (", ");
618                         }
619                         sb.Append ((start.id).ToString ());
620                 }
621                 
622                 public override string ToString ()
623                 {
624                         StringBuilder sb = new StringBuilder ();
625                         
626                         sb.Append ("{");
627                         if (CaptureContext != null){
628                                 sb.Append (CaptureContext.ToString ());
629                                 sb.Append (":");
630                         }
631
632                         DoPath (sb, this);
633                         sb.Append ("}");
634
635                         return sb.ToString ();
636                 }
637         }
638
639         //
640         // CaptureContext objects are created on demand if a method has
641         // anonymous methods and kept on the ToplevelBlock.
642         //
643         // If they exist, all ToplevelBlocks in the containing block are
644         // linked together (children pointing to their parents).
645         //
646         public class CaptureContext {
647                 public static int count;
648                 public int cc_id;
649                 Location loc;
650                 
651                 //
652                 // Points to the toplevel block that owns this CaptureContext
653                 //
654                 ToplevelBlock toplevel_owner;
655                 Hashtable scopes = new Hashtable ();
656                 bool have_captured_vars = false;
657                 ScopeInfo topmost = null;
658
659                 //
660                 // Captured fields
661                 //
662                 Hashtable captured_fields = new Hashtable ();
663                 Hashtable captured_variables = new Hashtable ();
664                 public Hashtable captured_parameters = new Hashtable ();
665                 public AnonymousMethod Host;
666                 
667                 public CaptureContext (ToplevelBlock toplevel_owner, Location loc, AnonymousMethod host)
668                 {
669                         cc_id = count++;
670                         this.toplevel_owner = toplevel_owner;
671                         this.loc = loc;
672
673                         if (host != null)
674                                 Host = host;
675                 }
676
677                 void DoPath (StringBuilder sb, CaptureContext cc)
678                 {
679                         if (cc.ParentCaptureContext != null){
680                                 DoPath (sb, cc.ParentCaptureContext);
681                                 sb.Append (".");
682                         }
683                         sb.Append (cc_id.ToString ());
684                 }
685                 
686                 public override string ToString ()
687                 {
688                         ToplevelBlock parent = ParentToplevel;
689                         StringBuilder sb = new StringBuilder ();
690
691                         sb.Append ("[");
692                         DoPath (sb, this);
693                         sb.Append ("]");
694                         return sb.ToString ();
695                 }
696
697                 public ToplevelBlock ParentToplevel {
698                         get {
699                                 return toplevel_owner.Container;
700                         }
701                 }
702
703                 public CaptureContext ParentCaptureContext {
704                         get {
705                                 ToplevelBlock parent = ParentToplevel;
706                                 
707                                 return (parent == null) ? null : parent.CaptureContext;
708                         }
709                 }
710
711                 // Returns the deepest of two scopes
712                 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
713                 {
714                         ScopeInfo p;
715
716                         if (a == null)
717                                 return b;
718                         if (b == null)
719                                 return a;
720                         if (a == b)
721                                 return a;
722
723                         //
724                         // If they Scopes are on the same CaptureContext, we do the double
725                         // checks just so if there is an invariant change in the future,
726                         // we get the exception at the end
727                         //
728                         for (p = a; p != null; p = p.ParentScope)
729                                 if (p == b)
730                                         return a;
731                         
732                         for (p = b; p != null; p = p.ParentScope)
733                                 if (p == a)
734                                         return b;
735
736                         CaptureContext ca = a.CaptureContext;
737                         CaptureContext cb = b.CaptureContext;
738
739                         for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
740                                 if (c == cb)
741                                         return a;
742
743                         for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
744                                 if (c == ca)
745                                         return b;
746                         throw new Exception ("Should never be reached");
747                 }
748
749                 void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
750                 {
751                         am.Scope = Deepest (am.Scope, scope);
752                 }
753
754                 void LinkScope (ScopeInfo scope, int id)
755                 {
756                         ScopeInfo parent = (ScopeInfo) scopes [id];
757                         scope.ParentScope = parent;
758                         parent.AddChild (scope);
759
760                         if (scope == topmost)
761                                 topmost = parent;
762                 }
763                 
764                 public void AddLocal (AnonymousMethod am, LocalInfo li)
765                 {
766                         if (li.Block.Toplevel != toplevel_owner){
767                                 ParentCaptureContext.AddLocal (am, li);
768                                 return;
769                         }
770                         int block_id = li.Block.ID;
771                         ScopeInfo scope;
772                         if (scopes [block_id] == null){
773                                 scope = new ScopeInfo (this, li.Block);
774                                 scopes [block_id] = scope;
775                         } else
776                                 scope = (ScopeInfo) scopes [block_id];
777
778                         if (topmost == null){
779                                 topmost = scope;
780                         } else {
781                                 // Link to parent
782                                 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
783                                         if (scopes [b.ID] != null){
784                                                 LinkScope (scope, b.ID);
785                                                 break;
786                                         }
787                                 }
788
789                                 if (scope.ParentScope == null && ParentCaptureContext != null){
790                                         CaptureContext pcc = ParentCaptureContext;
791                                         
792                                         for (Block b = am.ContainingBlock; b != null; b = b.Parent){
793                                                 if (pcc.scopes [b.ID] != null){
794                                                         pcc.LinkScope (scope, b.ID);
795                                                         break;
796                                                 }
797                                         }
798                                 }
799                         }
800
801                         //
802                         // Adjust the owner
803                         //
804                         if (Host != null)
805                                 AdjustMethodScope (Host, topmost);
806
807                         //
808                         // Adjust the user
809                         //
810                         AdjustMethodScope (am, scope);
811                         
812                         if (captured_variables [li] != null)
813                                 return;
814                         
815                         have_captured_vars = true;
816                         captured_variables [li] = li;
817                         scope.AddLocal (li);
818                 }
819
820                 //
821                 // Retursn the CaptureContext for the block that defines the parameter `name'
822                 //
823                 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
824                 {
825                         ToplevelBlock container = current.Container;
826                         if (container != null){
827                                 CaptureContext cc = _ContextForParameter (container, name);
828                                 if (cc != null)
829                                         return cc;
830                         }
831                         if (current.IsParameterReference (name))
832                                 return current.ToplevelBlockCaptureContext;
833                         return null;
834                 }
835
836                 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
837                 {
838                         CaptureContext cc = _ContextForParameter (current, name);
839                         if (cc == null)
840                                 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
841                         return cc;
842                 }
843                 
844                 //
845                 // Records the captured parameter at the appropriate CaptureContext
846                 //
847                 public void AddParameter (EmitContext ec, AnonymousMethod am, string name, Type t, int idx)
848                 {
849                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
850
851                         cc.AddParameterToContext (am, name, t, idx);
852                 }
853
854                 //
855                 // Records the parameters in the context
856                 //
857                 void AddParameterToContext (AnonymousMethod am, string name, Type t, int idx)
858                 {
859                         if (captured_parameters == null)
860                                 captured_parameters = new Hashtable ();
861                         if (captured_parameters [name] != null)
862                                 return;
863                         captured_parameters [name] = new CapturedParameter (t, idx);
864
865                         if (topmost == null){
866                                 //
867                                 // Create one ScopeInfo, if there are none.
868                                 //
869                                 topmost = new ScopeInfo (this, toplevel_owner);
870                                 scopes [toplevel_owner.ID] = topmost;
871                         } else {
872                                 //
873                                 // If the topmost ScopeInfo is not at the topblock level, insert
874                                 // a new ScopeInfo there.
875                                 //
876                                 if (topmost.ScopeBlock != toplevel_owner){
877                                         ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
878                                         scopes [toplevel_owner.ID] = topmost;
879                                         topmost.ParentScope = par_si;
880                                         topmost = par_si;
881                                 }
882                         }
883                         
884                         topmost.HostsParameters = true;
885                         AdjustMethodScope (am, topmost);
886                 }
887
888                 //
889                 // Captured fields are only recorded on the topmost CaptureContext, because that
890                 // one is the one linked to the owner of instance fields
891                 //
892                 public void AddField (FieldExpr fe)
893                 {
894                         if (fe.FieldInfo.IsStatic)
895                                 throw new Exception ("Attempt to register a static field as a captured field");
896                         
897                         CaptureContext parent = ParentCaptureContext;
898                         if (parent != null)
899                                 parent.AddField (fe);
900                         else
901                                 captured_fields [fe] = fe;
902                 }
903                 
904
905                 public bool HaveCapturedVariables {
906                         get {
907                                 return have_captured_vars;
908                         }
909                 }
910                 
911                 public bool HaveCapturedFields {
912                         get {
913                                 CaptureContext parent = ParentCaptureContext;
914                                 if (parent != null)
915                                         return parent.HaveCapturedFields;
916                                 return captured_fields.Count > 0;
917                         }
918                 }
919
920                 public bool IsCaptured (LocalInfo local)
921                 {
922                         foreach (ScopeInfo si in scopes.Values){
923                                 if (si.IsCaptured (local))
924                                         return true;
925                         }
926                         return false;
927                 }
928
929                 //
930                 // Returns whether the parameter is captured
931                 //
932                 public bool IsParameterCaptured (string name)
933                 {
934                         if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
935                                 return true;
936                         
937                         if (captured_parameters != null)
938                                 return captured_parameters [name] != null;
939                         return false;
940                 }
941
942                 public void EmitHelperClasses (EmitContext ec)
943                 {
944                         if (topmost != null){
945                                 topmost.NeedThis = HaveCapturedFields;
946                                 topmost.EmitScopeType (ec);
947                         } 
948                 }
949
950                 public void CloseHelperClasses ()
951                 {
952                         if (topmost != null)
953                                 topmost.CloseTypes ();
954                 }
955
956                 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
957                 {
958                         ScopeInfo si;
959                         
960                         si = (ScopeInfo) scopes [b.ID];
961                         if (si == null)
962                                 throw new Exception ("Si is null for block " + b.ID);
963                         si.EmitInitScope (ec);
964
965                         return si;
966                 }
967
968                 //
969                 // Emits the opcodes necessary to load the instance of the captured
970                 // variable in `li'
971                 //
972                 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li, AnonymousMethod am)
973                 {
974                         ILGenerator ig = ec.ig;
975                         ScopeInfo si;
976                         
977                         if (li.Block.Toplevel == toplevel_owner){
978                                 si = GetScopeFromBlock (ec, li.Block);
979                                 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
980                                 return;
981                         }
982
983                         si = am.Scope;
984                         ig.Emit (OpCodes.Ldarg_0);
985                         if (si != null){
986                                 while (si.ScopeBlock.ID != li.Block.ID){
987                                         if (si.ParentLink != null)
988                                                 ig.Emit (OpCodes.Ldfld, si.ParentLink);
989                                         si = si.ParentScope;
990                                         if (si == null) 
991                                                 throw new Exception (
992                                                              String.Format ("Never found block {0} starting at {1} while looking up {2}",
993                                                                             li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
994                                 }
995                         }
996                 }
997
998                 //
999                 // Internal routine that loads the instance to reach parameter `name'
1000                 //
1001                 void EmitParameterInstance (EmitContext ec, string name)
1002                 {
1003                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1004                         if (cc != this){
1005                                 cc.EmitParameterInstance (ec, name);
1006                                 return;
1007                         }
1008                         Block invocation_block = ec.CurrentBlock;
1009                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1010                         ILGenerator ig = ec.ig;
1011
1012                         ScopeInfo si;
1013                         if (ec.CurrentBlock == toplevel_owner){
1014                                 si = GetScopeFromBlock (ec, toplevel_owner);
1015                                 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1016                                 return;
1017                         }
1018                         
1019                         si = ec.CurrentAnonymousMethod.Scope;
1020                         ig.Emit (OpCodes.Ldarg_0);
1021                         if (si != null){
1022                                 while (si.ParentLink != null) {
1023                                         ig.Emit (OpCodes.Ldfld, si.ParentLink);
1024                                         si = si.ParentScope;
1025                                 } 
1026                         }
1027                 }
1028
1029                 //
1030                 // Emits the code necessary to load the parameter named `name' within
1031                 // an anonymous method.
1032                 //
1033                 public void EmitParameter (EmitContext ec, string name)
1034                 {
1035                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1036                         if (cc != this){
1037                                 cc.EmitParameter (ec, name);
1038                                 return;
1039                         }
1040                         EmitParameterInstance (ec, name);
1041                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1042                         ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1043                 }
1044
1045                 //
1046                 // Implements the assignment of `source' to the paramenter named `name' within
1047                 // an anonymous method.
1048                 //
1049                 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1050                 {
1051                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1052                         if (cc != this){
1053                                 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1054                                 return;
1055                         }
1056                         ILGenerator ig = ec.ig;
1057                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1058
1059                         EmitParameterInstance (ec, name);
1060                         source.Emit (ec);
1061                         if (leave_copy)
1062                                 ig.Emit (OpCodes.Dup);
1063                         ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1064                 }
1065
1066                 //
1067                 // Emits the address for the parameter named `name' within
1068                 // an anonymous method.
1069                 //
1070                 public void EmitAddressOfParameter (EmitContext ec, string name)
1071                 {
1072                         CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1073                         if (cc != this){
1074                                 cc.EmitAddressOfParameter (ec, name);
1075                                 return;
1076                         }
1077                         EmitParameterInstance (ec, name);
1078                         CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1079                         ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1080                 }
1081
1082                 //
1083                 // The following methods are only invoked on the host for the
1084                 // anonymous method.
1085                 //
1086                 public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
1087                 {
1088                         ILGenerator ig = target.ig;
1089                         ScopeInfo si = am.Scope;
1090                         
1091                         if (si == null){
1092                                 ig.Emit (OpCodes.Ldarg_0);
1093                                 return;
1094                         }
1095
1096                         si.EmitInitScope (target);
1097                         ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1098                 }
1099
1100                 ArrayList all_scopes = new ArrayList ();
1101                 
1102                 public void AddScope (ScopeInfo si)
1103                 {
1104                         all_scopes.Add (si);
1105                         toplevel_owner.RegisterCaptureContext (this);
1106                 }
1107
1108                 //
1109                 // Links any scopes that were not linked previously
1110                 //
1111                 public void AdjustScopes ()
1112                 {
1113                         foreach (ScopeInfo scope in all_scopes){
1114                                 if (scope.ParentScope != null)
1115                                         continue;
1116
1117                                 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1118                                         if (scopes [b.ID] != null){
1119                                                 LinkScope (scope, b.ID);
1120                                                 break;
1121                                         }
1122                                 }
1123
1124                                 if (scope.ParentScope == null && ParentCaptureContext != null){
1125                                         CaptureContext pcc = ParentCaptureContext;
1126                                         
1127                                         for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1128                                                 if (pcc.scopes [b.ID] != null){
1129                                                         pcc.LinkScope (scope, b.ID);
1130                                                         break;
1131                                                 }
1132                                         }
1133                                 }
1134                         }
1135                 }
1136         }
1137 }