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