2 // anonymous.cs: Support for anonymous methods
5 // Miguel de Icaza (miguel@ximain.com)
7 // (C) 2003, 2004 Novell, Inc.
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
17 using System.Collections;
18 using System.Reflection;
19 using System.Reflection.Emit;
21 namespace Mono.CSharp {
23 public abstract class AnonymousContainer : Expression
25 // Used to generate unique method names.
26 protected static int anonymous_method_count;
28 // An array list of AnonymousMethodParameter or null
29 public Parameters Parameters;
32 // The block that makes up the body for the anonymous mehtod
34 public ToplevelBlock Block;
37 // The container block for this anonymous method.
39 public Block ContainingBlock;
42 // The implicit method we create
46 protected MethodInfo invoke_mb;
48 // The emit context for the anonymous method
49 public EmitContext aec;
50 public InternalParameters amp;
51 protected bool unreachable;
54 // The modifiers applied to the method, we aggregate them
56 protected int method_modifiers = Modifiers.PRIVATE;
59 // During the resolve stage of the anonymous method body,
60 // we discover the actual scope where we are hosted, or
61 // null to host the method in the same class
63 public ScopeInfo Scope;
66 // Points to our container anonymous method if its present
68 public AnonymousContainer ContainerAnonymousMethod;
70 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
71 ToplevelBlock block, Location l)
73 Parameters = parameters;
78 // The order is important: this setups the CaptureContext tree hierarchy.
80 if (container == null) {
81 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
84 container.SetHaveAnonymousMethods (l, this);
85 block.SetHaveAnonymousMethods (l, this);
88 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
90 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
94 public override Expression DoResolve (EmitContext ec)
97 // Set class type, set type
100 eclass = ExprClass.Value;
103 // This hack means `The type is not accessible
104 // anywhere', we depend on special conversion
107 type = TypeManager.anonymous_method_type;
112 protected abstract bool CreateMethodHost (EmitContext ec);
114 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
116 public abstract bool IsIterator {
121 public class AnonymousMethod : AnonymousContainer
123 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
124 ToplevelBlock block, Location l)
125 : base (parameters, container, block, l)
129 public override bool IsIterator {
130 get { return false; }
133 public override void Emit (EmitContext ec)
135 // nothing, as we only exist to not do anything.
139 // Creates the host for the anonymous method
141 protected override bool CreateMethodHost (EmitContext ec)
144 // Crude hack follows: we replace the TypeBuilder during the
145 // definition to get the method hosted in the right class
148 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
149 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
150 ? current_type : Scope.ScopeTypeBuilder;
152 if (current_type == null)
153 throw new Exception ("The current_type is null");
155 if (type_host == null)
156 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
158 if (current_type != type_host)
159 method_modifiers = Modifiers.INTERNAL;
161 if (current_type == type_host && ec.IsStatic){
162 method_modifiers |= Modifiers.STATIC;
166 method = new Method (
167 (TypeContainer) ec.TypeContainer,
168 new TypeExpression (invoke_mb.ReturnType, loc),
169 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
171 method.Block = Block;
174 // Swap the TypeBuilder while we define the method, then restore
176 if (current_type != null)
177 ec.TypeContainer.TypeBuilder = type_host;
178 bool res = method.Define ();
179 if (current_type != null)
180 ec.TypeContainer.TypeBuilder = current_type;
184 void Error_ParameterMismatch (Type t)
186 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
187 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
191 // Returns true if this anonymous method can be implicitly
192 // converted to the delegate type `delegate_type'
194 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
197 // At this point its the first time we know the return type that is
198 // needed for the anonymous method. We create the method here.
201 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
202 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
204 if (Parameters == null){
208 // We provide a set of inaccessible parameters
211 for (i = 0; i < invoke_pd.Count; i++){
212 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
215 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
216 Parameter [] fixedpars = new Parameter [n];
218 for (i = j = 0; i < invoke_pd.Count; i++){
219 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
221 fixedpars [j] = new Parameter (
222 new TypeExpression (invoke_pd.ParameterType (i), loc),
223 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
227 Parameter variable = null;
228 if (params_idx != -1){
229 variable = new Parameter (
230 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
231 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
234 Parameters = new Parameters (fixedpars, variable);
238 // First, parameter types of `delegate_type' must be compatible
239 // with the anonymous method.
241 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
243 if (amp.Count != invoke_pd.Count){
245 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
246 TypeManager.CSharpName (delegate_type), amp.Count.ToString ());
247 Error_ParameterMismatch (delegate_type);
252 for (int i = 0; i < amp.Count; i++){
253 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
256 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
257 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
258 (i+1).ToString (), amp.ModifierDesc (i));
259 Error_ParameterMismatch (delegate_type);
263 if (amp_mod != invoke_pd.ParameterModifier (i)){
264 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
265 (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
266 Error_ParameterMismatch (delegate_type);
270 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
271 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
273 TypeManager.CSharpName (amp.ParameterType (i)),
274 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
275 Error_ParameterMismatch (delegate_type);
282 // If we are only probing, return ourselves
288 // Second: the return type of the delegate must be compatible with
289 // the anonymous type. Instead of doing a pass to examine the block
290 // we satisfy the rule by setting the return type on the EmitContext
291 // to be the delegate type return type.
294 //MethodBuilder builder = method_data.MethodBuilder;
295 //ILGenerator ig = builder.GetILGenerator ();
298 aec = new EmitContext (
299 ec.TypeContainer, ec.DeclSpace, loc, null,
300 invoke_mb.ReturnType,
301 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
302 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
303 (ec.IsStatic ? Modifiers.STATIC : 0),
304 /* No constructor */ false);
306 aec.CurrentAnonymousMethod = this;
307 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
308 ContainingBlock = ec.CurrentBlock;
310 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
311 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
316 public override string ExprClassName {
318 return "anonymous method";
322 public MethodBuilder GetMethodBuilder ()
324 return method.MethodData.MethodBuilder;
327 public override string GetSignatureForError ()
329 string s = TypeManager.CSharpSignature (invoke_mb);
330 return s.Substring (0, s.IndexOf (".Invoke("));
333 public bool EmitMethod (EmitContext ec)
335 if (!CreateMethodHost (ec))
338 MethodBuilder builder = GetMethodBuilder ();
339 ILGenerator ig = builder.GetILGenerator ();
342 Parameters.LabelParameters (aec, builder);
345 // Adjust based on the computed state of the
346 // method from CreateMethodHost
348 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
350 aec.EmitMeta (Block, amp);
351 aec.EmitResolvedTopBlock (Block, unreachable);
355 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
357 TypeBuilder container = ec.TypeContainer.TypeBuilder;
358 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
360 scope.ScopeTypeBuilder = container.DefineNestedType (
361 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
362 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
364 Type [] constructor_types = TypeManager.NoTypes;
365 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
366 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
367 MethodAttributes.Public | MethodAttributes.HideBySig |
368 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
369 CallingConventions.HasThis, constructor_types);
370 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
371 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
373 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
374 cig.Emit (OpCodes.Ldarg_0);
375 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
376 cig.Emit (OpCodes.Ret);
379 public static void Error_AddressOfCapturedVar (string name, Location loc)
381 Report.Error (1686, loc,
382 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
388 // This will emit the code for the delegate, as well delegate creation on the host
390 public class AnonymousDelegate : DelegateCreation {
393 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
400 public override Expression DoResolve (EmitContext ec)
402 eclass = ExprClass.Value;
407 public override void Emit (EmitContext ec)
409 if (!am.EmitMethod (ec))
413 // Now emit the delegate creation.
415 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
416 delegate_instance_expression = new AnonymousInstance (am);
418 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
419 constructor_method = ((MethodGroupExpr) ml).Methods [0];
420 delegate_method = am.GetMethodBuilder ();
424 class AnonymousInstance : Expression {
427 public AnonymousInstance (AnonymousMethod am)
430 eclass = ExprClass.Value;
433 public override Expression DoResolve (EmitContext ec)
438 public override void Emit (EmitContext ec)
440 am.aec.EmitMethodHostInstance (ec, am);
445 class CapturedParameter {
447 public FieldBuilder FieldBuilder;
450 public CapturedParameter (Type type, int idx)
458 // Here we cluster all the variables captured on a given scope, we also
459 // keep some extra information that might be required on each scope.
461 public class ScopeInfo {
462 public CaptureContext CaptureContext;
463 public ScopeInfo ParentScope;
464 public Block ScopeBlock;
465 public bool NeedThis = false;
466 public bool HostsParameters = false;
468 // For tracking the number of scopes created.
473 ArrayList locals = new ArrayList ();
474 ArrayList children = new ArrayList ();
477 // The types and fields generated
479 public TypeBuilder ScopeTypeBuilder;
480 public ConstructorBuilder ScopeConstructor;
481 public FieldBuilder THIS;
482 public FieldBuilder ParentLink;
485 // Points to the object of type `ScopeTypeBuilder' that
486 // holds the data for the scope
488 LocalBuilder scope_instance;
490 public ScopeInfo (CaptureContext cc, Block b)
499 public void AddLocal (LocalInfo li)
501 if (locals.Contains (li))
507 public bool IsCaptured (LocalInfo li)
509 return locals.Contains (li);
512 internal void AddChild (ScopeInfo si)
514 if (children.Contains (si))
518 // If any of the current children should be a children of `si', move them there
520 ArrayList move_queue = null;
521 foreach (ScopeInfo child in children){
522 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
523 if (move_queue == null)
524 move_queue = new ArrayList ();
525 move_queue.Add (child);
526 child.ParentScope = si;
533 if (move_queue != null){
534 foreach (ScopeInfo child in move_queue){
535 children.Remove (child);
540 static int indent = 0;
544 for (int i = 0; i < indent; i++)
550 //Console.WriteLine (Environment.StackTrace);
552 Console.WriteLine ("START");
555 Console.WriteLine ("NeedThis=" + NeedThis);
556 foreach (LocalInfo li in locals){
558 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
561 foreach (ScopeInfo si in children)
565 Console.WriteLine ("END");
568 public string MakeHelperName ()
570 return String.Format ("<>AnonHelp<{0}>", id);
573 private string MakeFieldName (string local_name)
575 return "<" + id + ":" + local_name + ">";
578 public void EmitScopeType (EmitContext ec)
582 if (ScopeTypeBuilder != null)
585 TypeBuilder container = ec.TypeContainer.TypeBuilder;
587 CaptureContext.Host.CreateScopeType (ec, this);
590 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
592 if (ParentScope != null){
593 if (ParentScope.ScopeTypeBuilder == null){
594 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
597 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
598 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
599 FieldAttributes.Assembly);
602 if (NeedThis && ParentScope != null)
603 throw new Exception ("I was not expecting THIS && having a parent");
605 foreach (LocalInfo info in locals)
606 info.FieldBuilder = ScopeTypeBuilder.DefineField (
607 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
609 if (HostsParameters){
610 Hashtable captured_parameters = CaptureContext.captured_parameters;
612 foreach (DictionaryEntry de in captured_parameters){
613 string name = (string) de.Key;
614 CapturedParameter cp = (CapturedParameter) de.Value;
617 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
618 cp.FieldBuilder = fb;
622 foreach (ScopeInfo si in children){
623 si.EmitScopeType (ec);
627 public void CloseTypes ()
629 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
630 foreach (ScopeInfo si in children)
635 // Emits the initialization code for the scope
637 public void EmitInitScope (EmitContext ec)
639 ILGenerator ig = ec.ig;
644 if (ScopeConstructor == null)
645 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
647 if (!CaptureContext.Host.IsIterator) {
648 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
649 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
650 ig.Emit (OpCodes.Stloc, scope_instance);
654 if (CaptureContext.Host.IsIterator) {
655 ig.Emit (OpCodes.Ldarg_0);
656 ig.Emit (OpCodes.Ldarg_1);
658 ig.Emit (OpCodes.Ldloc, scope_instance);
659 ig.Emit (OpCodes.Ldarg_0);
661 ig.Emit (OpCodes.Stfld, THIS);
665 // Copy the parameter values, if any
667 int extra = ec.IsStatic ? 0 : 1;
668 if (CaptureContext.Host.IsIterator)
670 if (HostsParameters){
671 Hashtable captured_parameters = CaptureContext.captured_parameters;
673 foreach (DictionaryEntry de in captured_parameters){
674 CapturedParameter cp = (CapturedParameter) de.Value;
676 EmitScopeInstance (ig);
677 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
678 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
682 if (ParentScope != null){
683 if (!ParentScope.inited)
684 ParentScope.EmitInitScope (ec);
686 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
688 // Only emit initialization in our capturecontext world
690 if (ParentScope.CaptureContext == CaptureContext){
691 EmitScopeInstance (ig);
692 ParentScope.EmitScopeInstance (ig);
693 ig.Emit (OpCodes.Stfld, ParentLink);
695 EmitScopeInstance (ig);
696 ig.Emit (OpCodes.Ldarg_0);
697 ig.Emit (OpCodes.Stfld, ParentLink);
704 public void EmitScopeInstance (ILGenerator ig)
706 if (CaptureContext.Host.IsIterator)
707 ig.Emit (OpCodes.Ldarg_0);
709 ig.Emit (OpCodes.Ldloc, scope_instance);
712 static void DoPath (StringBuilder sb, ScopeInfo start)
714 if (start.ParentScope != null){
715 DoPath (sb, start.ParentScope);
718 sb.Append ((start.id).ToString ());
721 public override string ToString ()
723 StringBuilder sb = new StringBuilder ();
726 if (CaptureContext != null){
727 sb.Append (CaptureContext.ToString ());
734 return sb.ToString ();
739 // CaptureContext objects are created on demand if a method has
740 // anonymous methods and kept on the ToplevelBlock.
742 // If they exist, all ToplevelBlocks in the containing block are
743 // linked together (children pointing to their parents).
745 public class CaptureContext {
746 public static int count;
751 // Points to the toplevel block that owns this CaptureContext
753 ToplevelBlock toplevel_owner;
754 Hashtable scopes = new Hashtable ();
755 bool have_captured_vars = false;
756 bool referenced_this = false;
757 ScopeInfo topmost = null;
762 Hashtable captured_fields = new Hashtable ();
763 Hashtable captured_variables = new Hashtable ();
764 public Hashtable captured_parameters = new Hashtable ();
765 public AnonymousContainer Host;
767 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
768 AnonymousContainer host)
771 this.toplevel_owner = toplevel_owner;
778 void DoPath (StringBuilder sb, CaptureContext cc)
780 if (cc.ParentCaptureContext != null){
781 DoPath (sb, cc.ParentCaptureContext);
784 sb.Append (cc.cc_id.ToString ());
787 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
789 toplevel_owner = new_toplevel;
792 for (CaptureContext cc = ParentCaptureContext; cc != null;
793 cc = cc.ParentCaptureContext) {
798 public override string ToString ()
800 StringBuilder sb = new StringBuilder ();
804 return sb.ToString ();
807 public ToplevelBlock ParentToplevel {
809 return toplevel_owner.Container;
813 public CaptureContext ParentCaptureContext {
815 ToplevelBlock parent = ParentToplevel;
817 return (parent == null) ? null : parent.CaptureContext;
821 // Returns the deepest of two scopes
822 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
834 // If they Scopes are on the same CaptureContext, we do the double
835 // checks just so if there is an invariant change in the future,
836 // we get the exception at the end
838 for (p = a; p != null; p = p.ParentScope)
842 for (p = b; p != null; p = p.ParentScope)
846 CaptureContext ca = a.CaptureContext;
847 CaptureContext cb = b.CaptureContext;
849 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
853 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
856 throw new Exception ("Should never be reached");
859 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
861 am.Scope = Deepest (am.Scope, scope);
864 void LinkScope (ScopeInfo scope, int id)
866 ScopeInfo parent = (ScopeInfo) scopes [id];
867 scope.ParentScope = parent;
868 parent.AddChild (scope);
870 if (scope == topmost)
874 public void AddLocal (AnonymousContainer am, LocalInfo li)
876 if (li.Block.Toplevel != toplevel_owner){
877 ParentCaptureContext.AddLocal (am, li);
880 int block_id = li.Block.ID;
882 if (scopes [block_id] == null){
883 scope = new ScopeInfo (this, li.Block);
884 scopes [block_id] = scope;
886 scope = (ScopeInfo) scopes [block_id];
888 if (topmost == null){
893 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
894 if (scopes [b.ID] != null){
895 LinkScope (scope, b.ID);
900 if (scope.ParentScope == null && ParentCaptureContext != null){
901 CaptureContext pcc = ParentCaptureContext;
903 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
904 if (pcc.scopes [b.ID] != null){
905 pcc.LinkScope (scope, b.ID);
916 AdjustMethodScope (Host, topmost);
921 AdjustMethodScope (am, scope);
923 if (captured_variables [li] != null)
926 have_captured_vars = true;
927 captured_variables [li] = li;
932 // Retursn the CaptureContext for the block that defines the parameter `name'
934 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
936 ToplevelBlock container = current.Container;
937 if (container != null){
938 CaptureContext cc = _ContextForParameter (container, name);
942 if (current.IsParameterReference (name))
943 return current.ToplevelBlockCaptureContext;
947 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
949 CaptureContext cc = _ContextForParameter (current, name);
951 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
956 // Records the captured parameter at the appropriate CaptureContext
958 public void AddParameter (EmitContext ec, AnonymousContainer am,
959 string name, Type t, int idx)
961 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
963 cc.AddParameterToContext (am, name, t, idx);
967 // Records the parameters in the context
969 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
971 if (captured_parameters == null)
972 captured_parameters = new Hashtable ();
973 if (captured_parameters [name] == null)
974 captured_parameters [name] = new CapturedParameter (t, idx);
976 if (topmost == null){
978 // Create one ScopeInfo, if there are none.
980 topmost = new ScopeInfo (this, toplevel_owner);
981 scopes [toplevel_owner.ID] = topmost;
984 // If the topmost ScopeInfo is not at the topblock level, insert
985 // a new ScopeInfo there.
987 // FIXME: This code probably should be evolved to be like the code
990 if (topmost.ScopeBlock != toplevel_owner){
991 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
992 ScopeInfo old_top = topmost;
993 scopes [toplevel_owner.ID] = topmost;
994 topmost.ParentScope = par_si;
996 topmost.AddChild (old_top);
1000 topmost.HostsParameters = true;
1001 AdjustMethodScope (am, topmost);
1005 // Captured fields are only recorded on the topmost CaptureContext, because that
1006 // one is the one linked to the owner of instance fields
1008 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1010 if (fe.FieldInfo.IsStatic)
1011 throw new Exception ("Attempt to register a static field as a captured field");
1012 CaptureContext parent = ParentCaptureContext;
1013 if (parent != null) {
1014 parent.AddField (ec, am, fe);
1018 if (topmost == null){
1020 // Create one ScopeInfo, if there are none.
1022 topmost = new ScopeInfo (this, toplevel_owner);
1023 scopes [toplevel_owner.ID] = topmost;
1026 AdjustMethodScope (am, topmost);
1029 public void CaptureThis ()
1031 CaptureContext parent = ParentCaptureContext;
1033 parent.CaptureThis ();
1034 referenced_this = true;
1037 public bool HaveCapturedVariables {
1039 return have_captured_vars;
1043 public bool HaveCapturedFields {
1045 CaptureContext parent = ParentCaptureContext;
1047 return parent.HaveCapturedFields;
1048 return captured_fields.Count > 0;
1052 public bool IsCaptured (LocalInfo local)
1054 foreach (ScopeInfo si in scopes.Values){
1055 if (si.IsCaptured (local))
1062 // Returns whether the parameter is captured
1064 public bool IsParameterCaptured (string name)
1066 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1069 if (captured_parameters != null)
1070 return captured_parameters [name] != null;
1074 public void EmitAnonymousHelperClasses (EmitContext ec)
1076 if (topmost != null){
1077 topmost.NeedThis = HaveCapturedFields || referenced_this;
1078 topmost.EmitScopeType (ec);
1082 public void CloseAnonymousHelperClasses ()
1084 if (topmost != null)
1085 topmost.CloseTypes ();
1088 public void EmitInitScope (EmitContext ec)
1090 EmitAnonymousHelperClasses (ec);
1091 if (topmost != null)
1092 topmost.EmitInitScope (ec);
1095 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1099 si = (ScopeInfo) scopes [b.ID];
1101 throw new Exception ("Si is null for block " + b.ID);
1102 si.EmitInitScope (ec);
1108 // Emits the opcodes necessary to load the instance of the captured
1111 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1112 AnonymousContainer am)
1114 ILGenerator ig = ec.ig;
1117 if (li.Block.Toplevel == toplevel_owner){
1118 si = GetScopeFromBlock (ec, li.Block);
1119 si.EmitScopeInstance (ig);
1124 ig.Emit (OpCodes.Ldarg_0);
1126 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1130 while (si.ScopeBlock.ID != li.Block.ID){
1131 if (si.ParentLink != null)
1132 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1133 si = si.ParentScope;
1136 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1137 while (si.ScopeBlock.ID != li.Block.ID){
1138 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1139 si = si.ParentScope;
1142 throw new Exception (
1143 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1144 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1151 // Internal routine that loads the instance to reach parameter `name'
1153 void EmitParameterInstance (EmitContext ec, string name)
1155 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1157 cc.EmitParameterInstance (ec, name);
1161 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1162 if (par_info != null){
1164 // FIXME: implementing this.
1167 ILGenerator ig = ec.ig;
1171 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1172 si = GetScopeFromBlock (ec, toplevel_owner);
1173 si.EmitScopeInstance (ig);
1175 si = ec.CurrentAnonymousMethod.Scope;
1176 ig.Emit (OpCodes.Ldarg_0);
1180 while (si.ParentLink != null) {
1181 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1182 si = si.ParentScope;
1188 // Emits the code necessary to load the parameter named `name' within
1189 // an anonymous method.
1191 public void EmitParameter (EmitContext ec, string name)
1193 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1195 cc.EmitParameter (ec, name);
1198 EmitParameterInstance (ec, name);
1199 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1200 if (par_info != null){
1202 // FIXME: implementing this.
1205 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1209 // Implements the assignment of `source' to the paramenter named `name' within
1210 // an anonymous method.
1212 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1214 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1216 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1219 ILGenerator ig = ec.ig;
1220 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1222 EmitParameterInstance (ec, name);
1224 ig.Emit (OpCodes.Dup);
1226 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1228 ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1233 // Emits the address for the parameter named `name' within
1234 // an anonymous method.
1236 public void EmitAddressOfParameter (EmitContext ec, string name)
1238 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1240 cc.EmitAddressOfParameter (ec, name);
1243 EmitParameterInstance (ec, name);
1244 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1245 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1249 // The following methods are only invoked on the host for the
1250 // anonymous method.
1252 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1254 ILGenerator ig = target.ig;
1255 ScopeInfo si = am.Scope;
1258 ig.Emit (OpCodes.Ldarg_0);
1262 si.EmitInitScope (target);
1263 si.EmitScopeInstance (ig);
1266 ArrayList all_scopes = new ArrayList ();
1268 public void AddScope (ScopeInfo si)
1270 all_scopes.Add (si);
1271 toplevel_owner.RegisterCaptureContext (this);
1275 // Links any scopes that were not linked previously
1277 public void AdjustScopes ()
1279 foreach (ScopeInfo scope in all_scopes){
1280 if (scope.ParentScope != null)
1283 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1284 if (scopes [b.ID] != null){
1285 LinkScope (scope, b.ID);
1290 if (scope.ParentScope == null && ParentCaptureContext != null){
1291 CaptureContext pcc = ParentCaptureContext;
1293 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1294 if (pcc.scopes [b.ID] != null){
1295 pcc.LinkScope (scope, b.ID);