**** Merged from MCS ****
[mono-project.git] / mcs / mcs / anonymous.cs
blob1dc3671764ed507307a38f926e0bfe595850862b
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
15 using System;
16 using System.Text;
17 using System.Collections;
18 using System.Reflection;
19 using System.Reflection.Emit;
21 namespace Mono.CSharp {
23 public class AnonymousMethod : Expression {
24 // Used to generate unique method names.
25 static int anonymous_method_count;
27 // An array list of AnonymousMethodParameter or null
28 public Parameters Parameters;
31 // The block that makes up the body for the anonymous mehtod
33 public ToplevelBlock Block;
36 // The container block for this anonymous method.
38 public Block ContainingBlock;
41 // The implicit method we create
43 public Method method;
45 MethodInfo invoke_mb;
47 // The emit context for the anonymous method
48 public EmitContext aec;
49 public InternalParameters amp;
50 bool unreachable;
53 // The modifiers applied to the method, we aggregate them
55 int method_modifiers = Modifiers.INTERNAL;
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
62 public ScopeInfo Scope;
65 // Points to our container anonymous method if its present
67 public AnonymousMethod ContainerAnonymousMethod;
69 public AnonymousMethod (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l)
71 Parameters = parameters;
72 Block = block;
73 loc = l;
76 // The order is important: this setups the CaptureContext tree hierarchy.
78 container.SetHaveAnonymousMethods (l, this);
79 block.SetHaveAnonymousMethods (l, this);
82 public override Expression DoResolve (EmitContext ec)
85 // Set class type, set type
88 eclass = ExprClass.Value;
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;
97 return this;
100 public override void Emit (EmitContext ec)
102 // nothing, as we only exist to not do anything.
106 // Creates the host for the anonymous method
108 bool CreateMethodHost (EmitContext ec, Type return_type)
111 // Crude hack follows: we replace the TypeBuilder during the
112 // definition to get the method hosted in the right class
115 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
116 TypeBuilder type_host = Scope == null ? current_type : Scope.ScopeTypeBuilder;
118 if (current_type == null)
119 throw new Exception ("The current_type is null");
121 if (type_host == null)
122 throw new Exception ("Type host is null");
124 if (current_type == type_host && ec.IsStatic){
125 if (ec.IsStatic)
126 method_modifiers |= Modifiers.STATIC;
127 current_type = null;
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;
139 // Swap the TypeBuilder while we define the method, then restore
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;
149 void Error_ParameterMismatch (Type t)
151 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
152 "{0}' since there is a parameter mismatch", t);
156 // Returns true if this anonymous method can be implicitly
157 // converted to the delegate type `delegate_type'
159 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
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.
166 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
167 ParameterData invoke_pd = Invocation.GetParameterData (invoke_mb);
170 // If implicit parameters are set, then we must check for out in the parameters
171 // and flag it accordingly.
173 bool out_invalid_check = false;
175 if (Parameters == null){
176 int i, j;
177 out_invalid_check = true;
180 // We provide a set of inaccessible parameters
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;
187 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
188 Parameter [] fixedpars = new Parameter [n];
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++;
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);
206 Parameters = new Parameters (fixedpars, variable, loc);
210 // First, parameter types of `delegate_type' must be compatible
211 // with the anonymous method.
213 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
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);
222 return null;
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);
233 return null;
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);
244 return null;
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);
252 return null;
257 // If we are only probing, return ourselves
259 if (probe)
260 return this;
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.
269 //MethodBuilder builder = method_data.MethodBuilder;
270 //ILGenerator ig = builder.GetILGenerator ();
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);
280 aec.CurrentAnonymousMethod = this;
281 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
282 ContainingBlock = ec.CurrentBlock;
284 if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
285 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
287 return null;
290 public MethodBuilder GetMethodBuilder ()
292 return method.MethodData.MethodBuilder;
295 public void EmitMethod (EmitContext ec)
297 if (!CreateMethodHost (ec, invoke_mb.ReturnType))
298 return;
300 MethodBuilder builder = GetMethodBuilder ();
301 ILGenerator ig = builder.GetILGenerator ();
302 aec.ig = ig;
304 Parameters.LabelParameters (aec, builder, loc);
307 // Adjust based on the computed state of the
308 // method from CreateMethodHost
310 if ((method_modifiers & Modifiers.STATIC) != 0)
311 aec.IsStatic = true;
313 aec.EmitMeta (Block, amp);
314 aec.EmitResolvedTopBlock (Block, unreachable);
319 // This will emit the code for the delegate, as well delegate creation on the host
321 public class AnonymousDelegate : DelegateCreation {
322 AnonymousMethod am;
324 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
326 type = target_type;
327 loc = l;
328 this.am = am;
331 public override Expression DoResolve (EmitContext ec)
333 eclass = ExprClass.Value;
334 return this;
337 public override void Emit (EmitContext ec)
339 am.EmitMethod (ec);
342 // Now emit the delegate creation.
344 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
345 delegate_instance_expression = new AnonymousInstance (am);
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);
353 class AnonymousInstance : Expression {
354 AnonymousMethod am;
356 public AnonymousInstance (AnonymousMethod am)
358 this.am = am;
359 eclass = ExprClass.Value;
362 public override Expression DoResolve (EmitContext ec)
364 return this;
367 public override void Emit (EmitContext ec)
369 am.aec.EmitMethodHostInstance (ec, am);
374 class CapturedParameter {
375 public Type Type;
376 public FieldBuilder FieldBuilder;
377 public int Idx;
379 public CapturedParameter (Type type, int idx)
381 Type = type;
382 Idx = idx;
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.
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;
397 // For tracking the number of scopes created.
398 public int id;
399 static int count;
400 bool inited = false;
402 ArrayList locals = new ArrayList ();
403 ArrayList children = new ArrayList ();
406 // The types and fields generated
408 public TypeBuilder ScopeTypeBuilder;
409 public ConstructorBuilder ScopeConstructor;
410 public FieldBuilder THIS;
411 public FieldBuilder ParentLink;
414 // Points to the object of type `ScopeTypeBuilder' that
415 // holds the data for the scope
417 public LocalBuilder ScopeInstance;
420 public ScopeInfo (CaptureContext cc, Block b)
422 CaptureContext = cc;
423 ScopeBlock = b;
424 id = count++;
426 cc.AddScope (this);
429 public void AddLocal (LocalInfo li)
431 if (locals.Contains (li))
432 return;
434 locals.Add (li);
437 public bool IsCaptured (LocalInfo li)
439 return locals.Contains (li);
442 public void AddChild (ScopeInfo si)
444 if (children.Contains (si))
445 return;
446 children.Add (si);
449 static int indent = 0;
451 void Pad ()
453 for (int i = 0; i < indent; i++)
454 Console.Write (" ");
457 void EmitDebug ()
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);
470 foreach (ScopeInfo si in children)
471 si.EmitDebug ();
472 indent--;
473 Pad ();
474 Console.WriteLine ("END");
477 public string MakeHelperName ()
479 return String.Format ("<>AnonHelp<{0}>", id);
482 public void EmitScopeConstructor ()
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);
493 ILGenerator cig = ScopeConstructor.GetILGenerator ();
494 cig.Emit (OpCodes.Ldarg_0);
495 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
496 cig.Emit (OpCodes.Ret);
499 public void EmitScopeType (EmitContext ec)
501 //EmitDebug ();
503 if (ScopeTypeBuilder != null)
504 return;
506 ILGenerator ig = ec.ig;
507 TypeBuilder container = ec.TypeContainer.TypeBuilder;
509 ScopeTypeBuilder = container.DefineNestedType (
510 MakeHelperName (), TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly,
511 TypeManager.object_type, null);
513 if (NeedThis)
514 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
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));
521 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
522 FieldAttributes.Assembly);
525 if (NeedThis && ParentScope != null)
526 throw new Exception ("I was not expecting THIS && having a parent");
528 foreach (LocalInfo info in locals){
529 info.FieldBuilder = ScopeTypeBuilder.DefineField (
530 info.Name, info.VariableType, FieldAttributes.Assembly);
533 if (HostsParameters){
534 Hashtable captured_parameters = CaptureContext.captured_parameters;
536 foreach (DictionaryEntry de in captured_parameters){
537 string name = (string) de.Key;
538 CapturedParameter cp = (CapturedParameter) de.Value;
539 FieldBuilder fb;
541 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
542 cp.FieldBuilder = fb;
546 EmitScopeConstructor ();
547 foreach (ScopeInfo si in children){
548 si.EmitScopeType (ec);
552 public void CloseTypes ()
554 RootContext.RegisterHelperClass (ScopeTypeBuilder);
555 foreach (ScopeInfo si in children)
556 si.CloseTypes ();
560 // Emits the initialization code for the scope
562 public void EmitInitScope (EmitContext ec)
564 ILGenerator ig = ec.ig;
566 if (inited)
567 return;
569 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
570 ScopeInstance = ig.DeclareLocal (ScopeTypeBuilder);
571 ig.Emit (OpCodes.Stloc, ScopeInstance);
573 if (THIS != null){
574 ig.Emit (OpCodes.Ldloc, ScopeInstance);
575 ig.Emit (OpCodes.Ldarg_0);
576 ig.Emit (OpCodes.Stfld, THIS);
580 // Copy the parameter values, if any
582 int extra = ec.IsStatic ? 0 : 1;
583 if (HostsParameters){
584 Hashtable captured_parameters = CaptureContext.captured_parameters;
586 foreach (DictionaryEntry de in captured_parameters){
587 string name = (string) de.Key;
588 CapturedParameter cp = (CapturedParameter) de.Value;
590 ig.Emit (OpCodes.Ldloc, ScopeInstance);
591 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
592 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
596 if (ParentScope != null){
598 // Only emit initialization in our capturecontext world
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);
610 inited = true;
613 static void DoPath (StringBuilder sb, ScopeInfo start)
615 if (start.ParentScope != null){
616 DoPath (sb, start.ParentScope);
617 sb.Append (", ");
619 sb.Append ((start.id).ToString ());
622 public override string ToString ()
624 StringBuilder sb = new StringBuilder ();
626 sb.Append ("{");
627 if (CaptureContext != null){
628 sb.Append (CaptureContext.ToString ());
629 sb.Append (":");
632 DoPath (sb, this);
633 sb.Append ("}");
635 return sb.ToString ();
640 // CaptureContext objects are created on demand if a method has
641 // anonymous methods and kept on the ToplevelBlock.
643 // If they exist, all ToplevelBlocks in the containing block are
644 // linked together (children pointing to their parents).
646 public class CaptureContext {
647 public static int count;
648 public int cc_id;
649 Location loc;
652 // Points to the toplevel block that owns this CaptureContext
654 ToplevelBlock toplevel_owner;
655 Hashtable scopes = new Hashtable ();
656 bool have_captured_vars = false;
657 ScopeInfo topmost = null;
660 // Captured fields
662 Hashtable captured_fields = new Hashtable ();
663 Hashtable captured_variables = new Hashtable ();
664 public Hashtable captured_parameters = new Hashtable ();
665 public AnonymousMethod Host;
667 public CaptureContext (ToplevelBlock toplevel_owner, Location loc, AnonymousMethod host)
669 cc_id = count++;
670 this.toplevel_owner = toplevel_owner;
671 this.loc = loc;
673 if (host != null)
674 Host = host;
677 void DoPath (StringBuilder sb, CaptureContext cc)
679 if (cc.ParentCaptureContext != null){
680 DoPath (sb, cc.ParentCaptureContext);
681 sb.Append (".");
683 sb.Append (cc_id.ToString ());
686 public override string ToString ()
688 ToplevelBlock parent = ParentToplevel;
689 StringBuilder sb = new StringBuilder ();
691 sb.Append ("[");
692 DoPath (sb, this);
693 sb.Append ("]");
694 return sb.ToString ();
697 public ToplevelBlock ParentToplevel {
698 get {
699 return toplevel_owner.Container;
703 public CaptureContext ParentCaptureContext {
704 get {
705 ToplevelBlock parent = ParentToplevel;
707 return (parent == null) ? null : parent.CaptureContext;
711 // Returns the deepest of two scopes
712 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
714 ScopeInfo p;
716 if (a == null)
717 return b;
718 if (b == null)
719 return a;
720 if (a == b)
721 return a;
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
728 for (p = a; p != null; p = p.ParentScope)
729 if (p == b)
730 return a;
732 for (p = b; p != null; p = p.ParentScope)
733 if (p == a)
734 return b;
736 CaptureContext ca = a.CaptureContext;
737 CaptureContext cb = b.CaptureContext;
739 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
740 if (c == cb)
741 return a;
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");
749 void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
751 am.Scope = Deepest (am.Scope, scope);
754 void LinkScope (ScopeInfo scope, int id)
756 ScopeInfo parent = (ScopeInfo) scopes [id];
757 scope.ParentScope = parent;
758 parent.AddChild (scope);
760 if (scope == topmost)
761 topmost = parent;
764 public void AddLocal (AnonymousMethod am, LocalInfo li)
766 if (li.Block.Toplevel != toplevel_owner){
767 ParentCaptureContext.AddLocal (am, li);
768 return;
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];
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;
789 if (scope.ParentScope == null && ParentCaptureContext != null){
790 CaptureContext pcc = ParentCaptureContext;
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;
802 // Adjust the owner
804 if (Host != null)
805 AdjustMethodScope (Host, topmost);
808 // Adjust the user
810 AdjustMethodScope (am, scope);
812 if (captured_variables [li] != null)
813 return;
815 have_captured_vars = true;
816 captured_variables [li] = li;
817 scope.AddLocal (li);
821 // Retursn the CaptureContext for the block that defines the parameter `name'
823 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
825 ToplevelBlock container = current.Container;
826 if (container != null){
827 CaptureContext cc = _ContextForParameter (container, name);
828 if (cc != null)
829 return cc;
831 if (current.IsParameterReference (name))
832 return current.ToplevelBlockCaptureContext;
833 return null;
836 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
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;
845 // Records the captured parameter at the appropriate CaptureContext
847 public void AddParameter (EmitContext ec, AnonymousMethod am, string name, Type t, int idx)
849 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
851 cc.AddParameterToContext (am, name, t, idx);
855 // Records the parameters in the context
857 void AddParameterToContext (AnonymousMethod am, string name, Type t, int idx)
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);
865 if (topmost == null){
867 // Create one ScopeInfo, if there are none.
869 topmost = new ScopeInfo (this, toplevel_owner);
870 scopes [toplevel_owner.ID] = topmost;
871 } else {
873 // If the topmost ScopeInfo is not at the topblock level, insert
874 // a new ScopeInfo there.
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;
884 topmost.HostsParameters = true;
885 AdjustMethodScope (am, topmost);
889 // Captured fields are only recorded on the topmost CaptureContext, because that
890 // one is the one linked to the owner of instance fields
892 public void AddField (FieldExpr fe)
894 if (fe.FieldInfo.IsStatic)
895 throw new Exception ("Attempt to register a static field as a captured field");
897 CaptureContext parent = ParentCaptureContext;
898 if (parent != null)
899 parent.AddField (fe);
900 else
901 captured_fields [fe] = fe;
905 public bool HaveCapturedVariables {
906 get {
907 return have_captured_vars;
911 public bool HaveCapturedFields {
912 get {
913 CaptureContext parent = ParentCaptureContext;
914 if (parent != null)
915 return parent.HaveCapturedFields;
916 return captured_fields.Count > 0;
920 public bool IsCaptured (LocalInfo local)
922 foreach (ScopeInfo si in scopes.Values){
923 if (si.IsCaptured (local))
924 return true;
926 return false;
930 // Returns whether the parameter is captured
932 public bool IsParameterCaptured (string name)
934 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
935 return true;
937 if (captured_parameters != null)
938 return captured_parameters [name] != null;
939 return false;
942 public void EmitHelperClasses (EmitContext ec)
944 if (topmost != null){
945 topmost.NeedThis = HaveCapturedFields;
946 topmost.EmitScopeType (ec);
950 public void CloseHelperClasses ()
952 if (topmost != null)
953 topmost.CloseTypes ();
956 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
958 ScopeInfo si;
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);
965 return si;
969 // Emits the opcodes necessary to load the instance of the captured
970 // variable in `li'
972 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li, AnonymousMethod am)
974 ILGenerator ig = ec.ig;
975 ScopeInfo si;
977 if (li.Block.Toplevel == toplevel_owner){
978 si = GetScopeFromBlock (ec, li.Block);
979 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
980 return;
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));
999 // Internal routine that loads the instance to reach parameter `name'
1001 void EmitParameterInstance (EmitContext ec, string name)
1003 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1004 if (cc != this){
1005 cc.EmitParameterInstance (ec, name);
1006 return;
1008 Block invocation_block = ec.CurrentBlock;
1009 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1010 ILGenerator ig = ec.ig;
1012 ScopeInfo si;
1013 if (ec.CurrentBlock == toplevel_owner){
1014 si = GetScopeFromBlock (ec, toplevel_owner);
1015 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1016 return;
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;
1030 // Emits the code necessary to load the parameter named `name' within
1031 // an anonymous method.
1033 public void EmitParameter (EmitContext ec, string name)
1035 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1036 if (cc != this){
1037 cc.EmitParameter (ec, name);
1038 return;
1040 EmitParameterInstance (ec, name);
1041 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1042 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1046 // Implements the assignment of `source' to the paramenter named `name' within
1047 // an anonymous method.
1049 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
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;
1056 ILGenerator ig = ec.ig;
1057 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
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);
1067 // Emits the address for the parameter named `name' within
1068 // an anonymous method.
1070 public void EmitAddressOfParameter (EmitContext ec, string name)
1072 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1073 if (cc != this){
1074 cc.EmitAddressOfParameter (ec, name);
1075 return;
1077 EmitParameterInstance (ec, name);
1078 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1079 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1083 // The following methods are only invoked on the host for the
1084 // anonymous method.
1086 public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
1088 ILGenerator ig = target.ig;
1089 ScopeInfo si = am.Scope;
1091 if (si == null){
1092 ig.Emit (OpCodes.Ldarg_0);
1093 return;
1096 si.EmitInitScope (target);
1097 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1100 ArrayList all_scopes = new ArrayList ();
1102 public void AddScope (ScopeInfo si)
1104 all_scopes.Add (si);
1105 toplevel_owner.RegisterCaptureContext (this);
1109 // Links any scopes that were not linked previously
1111 public void AdjustScopes ()
1113 foreach (ScopeInfo scope in all_scopes){
1114 if (scope.ParentScope != null)
1115 continue;
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;
1124 if (scope.ParentScope == null && ParentCaptureContext != null){
1125 CaptureContext pcc = ParentCaptureContext;
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;