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 container.SetHaveAnonymousMethods (l, this);
81 block.SetHaveAnonymousMethods (l, this);
84 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
87 Parameters = parameters;
88 Block = new ToplevelBlock (container, Parameters, l);
92 // The order is important: this setups the CaptureContext tree hierarchy.
94 container.SetHaveAnonymousMethods (loc, this);
95 Block.SetHaveAnonymousMethods (loc, this);
98 public override Expression DoResolve (EmitContext ec)
101 // Set class type, set type
104 eclass = ExprClass.Value;
107 // This hack means `The type is not accessible
108 // anywhere', we depend on special conversion
111 type = TypeManager.anonymous_method_type;
116 protected abstract bool CreateMethodHost (EmitContext ec);
118 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
120 public abstract bool IsIterator {
125 public class AnonymousMethod : AnonymousContainer
127 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
128 ToplevelBlock block, Location l)
129 : base (parameters, container, block, l)
133 public override bool IsIterator {
134 get { return false; }
137 public override void Emit (EmitContext ec)
139 // nothing, as we only exist to not do anything.
143 // Creates the host for the anonymous method
145 protected override bool CreateMethodHost (EmitContext ec)
148 // Crude hack follows: we replace the TypeBuilder during the
149 // definition to get the method hosted in the right class
152 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
153 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
154 ? current_type : Scope.ScopeTypeBuilder;
156 if (current_type == null)
157 throw new Exception ("The current_type is null");
159 if (type_host == null)
160 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
162 if (current_type == type_host && ec.IsStatic){
164 method_modifiers |= Modifiers.STATIC;
169 method = new Method (
170 (TypeContainer) ec.TypeContainer,
171 new TypeExpression (invoke_mb.ReturnType, loc),
172 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
173 Parameters, null, loc);
174 method.Block = Block;
177 // Swap the TypeBuilder while we define the method, then restore
179 if (current_type != null)
180 ec.TypeContainer.TypeBuilder = type_host;
181 bool res = method.Define ();
182 if (current_type != null)
183 ec.TypeContainer.TypeBuilder = current_type;
187 void Error_ParameterMismatch (Type t)
189 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
190 "{0}' since there is a parameter mismatch", t);
194 // Returns true if this anonymous method can be implicitly
195 // converted to the delegate type `delegate_type'
197 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
200 // At this point its the first time we know the return type that is
201 // needed for the anonymous method. We create the method here.
204 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
205 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
207 if (Parameters == null){
211 // We provide a set of inaccessible parameters
214 for (i = 0; i < invoke_pd.Count; i++){
215 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
218 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
219 Parameter [] fixedpars = new Parameter [n];
221 for (i = j = 0; i < invoke_pd.Count; i++){
222 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
224 fixedpars [j] = new Parameter (
225 new TypeExpression (invoke_pd.ParameterType (i), loc),
226 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
230 Parameter variable = null;
231 if (params_idx != -1){
232 variable = new Parameter (
233 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
234 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
237 Parameters = new Parameters (fixedpars, variable);
241 // First, parameter types of `delegate_type' must be compatible
242 // with the anonymous method.
244 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
246 if (amp.Count != invoke_pd.Count){
248 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
249 TypeManager.CSharpName (delegate_type), amp.Count);
250 Error_ParameterMismatch (delegate_type);
255 for (int i = 0; i < amp.Count; i++){
256 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
259 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
260 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
261 i+1, amp.ModifierDesc (i));
262 Error_ParameterMismatch (delegate_type);
266 if (amp_mod != invoke_pd.ParameterModifier (i)){
267 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
268 i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
269 Error_ParameterMismatch (delegate_type);
273 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
274 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
276 TypeManager.CSharpName (amp.ParameterType (i)),
277 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
278 Error_ParameterMismatch (delegate_type);
285 // If we are only probing, return ourselves
291 // Second: the return type of the delegate must be compatible with
292 // the anonymous type. Instead of doing a pass to examine the block
293 // we satisfy the rule by setting the return type on the EmitContext
294 // to be the delegate type return type.
297 //MethodBuilder builder = method_data.MethodBuilder;
298 //ILGenerator ig = builder.GetILGenerator ();
301 aec = new EmitContext (
302 ec.TypeContainer, ec.DeclSpace, loc, null,
303 invoke_mb.ReturnType,
304 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
305 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
306 (ec.IsStatic ? Modifiers.STATIC : 0),
307 /* No constructor */ false);
309 aec.CurrentAnonymousMethod = this;
310 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
311 ContainingBlock = ec.CurrentBlock;
313 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
314 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
319 public MethodBuilder GetMethodBuilder ()
321 return method.MethodData.MethodBuilder;
324 public override string GetSignatureForError ()
326 string s = TypeManager.CSharpSignature (invoke_mb);
327 return s.Substring (0, s.IndexOf (".Invoke("));
330 public bool EmitMethod (EmitContext ec)
332 if (!CreateMethodHost (ec))
335 MethodBuilder builder = GetMethodBuilder ();
336 ILGenerator ig = builder.GetILGenerator ();
339 Parameters.LabelParameters (aec, builder);
342 // Adjust based on the computed state of the
343 // method from CreateMethodHost
345 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
347 aec.EmitMeta (Block, amp);
348 aec.EmitResolvedTopBlock (Block, unreachable);
352 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
354 TypeBuilder container = ec.TypeContainer.TypeBuilder;
355 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
357 scope.ScopeTypeBuilder = container.DefineNestedType (
358 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
359 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
361 Type [] constructor_types = TypeManager.NoTypes;
362 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
363 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
364 MethodAttributes.Public | MethodAttributes.HideBySig |
365 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
366 CallingConventions.HasThis, constructor_types);
367 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
368 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
370 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
371 cig.Emit (OpCodes.Ldarg_0);
372 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
373 cig.Emit (OpCodes.Ret);
376 public static void Error_AddressOfCapturedVar (string name, Location loc)
378 Report.Error (1686, loc,
379 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
385 // This will emit the code for the delegate, as well delegate creation on the host
387 public class AnonymousDelegate : DelegateCreation {
390 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
397 public override Expression DoResolve (EmitContext ec)
399 eclass = ExprClass.Value;
404 public override void Emit (EmitContext ec)
406 if (!am.EmitMethod (ec))
410 // Now emit the delegate creation.
412 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
413 delegate_instance_expression = new AnonymousInstance (am);
415 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
416 constructor_method = ((MethodGroupExpr) ml).Methods [0];
417 delegate_method = am.GetMethodBuilder ();
421 class AnonymousInstance : Expression {
424 public AnonymousInstance (AnonymousMethod am)
427 eclass = ExprClass.Value;
430 public override Expression DoResolve (EmitContext ec)
435 public override void Emit (EmitContext ec)
437 am.aec.EmitMethodHostInstance (ec, am);
442 class CapturedParameter {
444 public FieldBuilder FieldBuilder;
447 public CapturedParameter (Type type, int idx)
455 // Here we cluster all the variables captured on a given scope, we also
456 // keep some extra information that might be required on each scope.
458 public class ScopeInfo {
459 public CaptureContext CaptureContext;
460 public ScopeInfo ParentScope;
461 public Block ScopeBlock;
462 public bool NeedThis = false;
463 public bool HostsParameters = false;
465 // For tracking the number of scopes created.
470 ArrayList locals = new ArrayList ();
471 ArrayList children = new ArrayList ();
474 // The types and fields generated
476 public TypeBuilder ScopeTypeBuilder;
477 public ConstructorBuilder ScopeConstructor;
478 public FieldBuilder THIS;
479 public FieldBuilder ParentLink;
482 // Points to the object of type `ScopeTypeBuilder' that
483 // holds the data for the scope
485 LocalBuilder scope_instance;
487 public ScopeInfo (CaptureContext cc, Block b)
496 public void AddLocal (LocalInfo li)
498 if (locals.Contains (li))
504 public bool IsCaptured (LocalInfo li)
506 return locals.Contains (li);
509 internal void AddChild (ScopeInfo si)
511 if (children.Contains (si))
515 // If any of the current children should be a children of `si', move them there
517 ArrayList move_queue = null;
518 foreach (ScopeInfo child in children){
519 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
520 if (move_queue == null)
521 move_queue = new ArrayList ();
522 move_queue.Add (child);
523 child.ParentScope = si;
530 if (move_queue != null){
531 foreach (ScopeInfo child in move_queue){
532 children.Remove (child);
537 static int indent = 0;
541 for (int i = 0; i < indent; i++)
547 //Console.WriteLine (Environment.StackTrace);
549 Console.WriteLine ("START");
552 Console.WriteLine ("NeedThis=" + NeedThis);
553 foreach (LocalInfo li in locals){
555 Console.WriteLine ("var {0}", li.Name);
558 foreach (ScopeInfo si in children)
562 Console.WriteLine ("END");
565 public string MakeHelperName ()
567 return String.Format ("<>AnonHelp<{0}>", id);
570 public void EmitScopeType (EmitContext ec)
574 if (ScopeTypeBuilder != null)
577 TypeBuilder container = ec.TypeContainer.TypeBuilder;
579 CaptureContext.Host.CreateScopeType (ec, this);
582 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
584 if (ParentScope != null){
585 if (ParentScope.ScopeTypeBuilder == null){
586 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
589 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
590 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
591 FieldAttributes.Assembly);
594 if (NeedThis && ParentScope != null)
595 throw new Exception ("I was not expecting THIS && having a parent");
597 foreach (LocalInfo info in locals)
598 info.FieldBuilder = ScopeTypeBuilder.DefineField (
599 info.Name, info.VariableType, FieldAttributes.Assembly);
601 if (HostsParameters){
602 Hashtable captured_parameters = CaptureContext.captured_parameters;
604 foreach (DictionaryEntry de in captured_parameters){
605 string name = (string) de.Key;
606 CapturedParameter cp = (CapturedParameter) de.Value;
609 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
610 cp.FieldBuilder = fb;
614 foreach (ScopeInfo si in children){
615 si.EmitScopeType (ec);
619 public void CloseTypes ()
621 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
622 foreach (ScopeInfo si in children)
627 // Emits the initialization code for the scope
629 public void EmitInitScope (EmitContext ec)
631 ILGenerator ig = ec.ig;
636 if (ScopeConstructor == null)
637 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
639 if (!CaptureContext.Host.IsIterator) {
640 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
641 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
642 ig.Emit (OpCodes.Stloc, scope_instance);
646 if (CaptureContext.Host.IsIterator) {
647 ig.Emit (OpCodes.Ldarg_0);
648 ig.Emit (OpCodes.Ldarg_1);
650 ig.Emit (OpCodes.Ldloc, scope_instance);
651 ig.Emit (OpCodes.Ldarg_0);
653 ig.Emit (OpCodes.Stfld, THIS);
657 // Copy the parameter values, if any
659 int extra = ec.IsStatic ? 0 : 1;
660 if (CaptureContext.Host.IsIterator)
662 if (HostsParameters){
663 Hashtable captured_parameters = CaptureContext.captured_parameters;
665 foreach (DictionaryEntry de in captured_parameters){
666 CapturedParameter cp = (CapturedParameter) de.Value;
668 EmitScopeInstance (ig);
669 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
670 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
674 if (ParentScope != null){
675 if (!ParentScope.inited)
676 ParentScope.EmitInitScope (ec);
678 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
680 // Only emit initialization in our capturecontext world
682 if (ParentScope.CaptureContext == CaptureContext){
683 EmitScopeInstance (ig);
684 ParentScope.EmitScopeInstance (ig);
685 ig.Emit (OpCodes.Stfld, ParentLink);
687 EmitScopeInstance (ig);
688 ig.Emit (OpCodes.Ldarg_0);
689 ig.Emit (OpCodes.Stfld, ParentLink);
696 public void EmitScopeInstance (ILGenerator ig)
698 if (CaptureContext.Host.IsIterator)
699 ig.Emit (OpCodes.Ldarg_0);
701 ig.Emit (OpCodes.Ldloc, scope_instance);
704 static void DoPath (StringBuilder sb, ScopeInfo start)
706 if (start.ParentScope != null){
707 DoPath (sb, start.ParentScope);
710 sb.Append ((start.id).ToString ());
713 public override string ToString ()
715 StringBuilder sb = new StringBuilder ();
718 if (CaptureContext != null){
719 sb.Append (CaptureContext.ToString ());
726 return sb.ToString ();
731 // CaptureContext objects are created on demand if a method has
732 // anonymous methods and kept on the ToplevelBlock.
734 // If they exist, all ToplevelBlocks in the containing block are
735 // linked together (children pointing to their parents).
737 public class CaptureContext {
738 public static int count;
743 // Points to the toplevel block that owns this CaptureContext
745 ToplevelBlock toplevel_owner;
746 Hashtable scopes = new Hashtable ();
747 bool have_captured_vars = false;
748 bool referenced_this = false;
749 ScopeInfo topmost = null;
754 Hashtable captured_fields = new Hashtable ();
755 Hashtable captured_variables = new Hashtable ();
756 public Hashtable captured_parameters = new Hashtable ();
757 public AnonymousContainer Host;
759 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
760 AnonymousContainer host)
763 this.toplevel_owner = toplevel_owner;
770 void DoPath (StringBuilder sb, CaptureContext cc)
772 if (cc.ParentCaptureContext != null){
773 DoPath (sb, cc.ParentCaptureContext);
776 sb.Append (cc.cc_id.ToString ());
779 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
781 toplevel_owner = new_toplevel;
784 for (CaptureContext cc = ParentCaptureContext; cc != null;
785 cc = cc.ParentCaptureContext) {
790 public override string ToString ()
792 StringBuilder sb = new StringBuilder ();
796 return sb.ToString ();
799 public ToplevelBlock ParentToplevel {
801 return toplevel_owner.Container;
805 public CaptureContext ParentCaptureContext {
807 ToplevelBlock parent = ParentToplevel;
809 return (parent == null) ? null : parent.CaptureContext;
813 // Returns the deepest of two scopes
814 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
826 // If they Scopes are on the same CaptureContext, we do the double
827 // checks just so if there is an invariant change in the future,
828 // we get the exception at the end
830 for (p = a; p != null; p = p.ParentScope)
834 for (p = b; p != null; p = p.ParentScope)
838 CaptureContext ca = a.CaptureContext;
839 CaptureContext cb = b.CaptureContext;
841 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
845 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
848 throw new Exception ("Should never be reached");
851 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
853 am.Scope = Deepest (am.Scope, scope);
856 void LinkScope (ScopeInfo scope, int id)
858 ScopeInfo parent = (ScopeInfo) scopes [id];
859 scope.ParentScope = parent;
860 parent.AddChild (scope);
862 if (scope == topmost)
866 public void AddLocal (AnonymousContainer am, LocalInfo li)
868 if (li.Block.Toplevel != toplevel_owner){
869 ParentCaptureContext.AddLocal (am, li);
872 int block_id = li.Block.ID;
874 if (scopes [block_id] == null){
875 scope = new ScopeInfo (this, li.Block);
876 scopes [block_id] = scope;
878 scope = (ScopeInfo) scopes [block_id];
880 if (topmost == null){
885 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
886 if (scopes [b.ID] != null){
887 LinkScope (scope, b.ID);
892 if (scope.ParentScope == null && ParentCaptureContext != null){
893 CaptureContext pcc = ParentCaptureContext;
895 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
896 if (pcc.scopes [b.ID] != null){
897 pcc.LinkScope (scope, b.ID);
908 AdjustMethodScope (Host, topmost);
913 AdjustMethodScope (am, scope);
915 if (captured_variables [li] != null)
918 have_captured_vars = true;
919 captured_variables [li] = li;
924 // Retursn the CaptureContext for the block that defines the parameter `name'
926 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
928 ToplevelBlock container = current.Container;
929 if (container != null){
930 CaptureContext cc = _ContextForParameter (container, name);
934 if (current.IsParameterReference (name))
935 return current.ToplevelBlockCaptureContext;
939 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
941 CaptureContext cc = _ContextForParameter (current, name);
943 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
948 // Records the captured parameter at the appropriate CaptureContext
950 public void AddParameter (EmitContext ec, AnonymousContainer am,
951 string name, Type t, int idx)
953 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
955 cc.AddParameterToContext (am, name, t, idx);
959 // Records the parameters in the context
961 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
963 if (captured_parameters == null)
964 captured_parameters = new Hashtable ();
965 if (captured_parameters [name] != null)
967 captured_parameters [name] = new CapturedParameter (t, idx);
969 if (topmost == null){
971 // Create one ScopeInfo, if there are none.
973 topmost = new ScopeInfo (this, toplevel_owner);
974 scopes [toplevel_owner.ID] = topmost;
977 // If the topmost ScopeInfo is not at the topblock level, insert
978 // a new ScopeInfo there.
980 // FIXME: This code probably should be evolved to be like the code
983 if (topmost.ScopeBlock != toplevel_owner){
984 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
985 ScopeInfo old_top = topmost;
986 scopes [toplevel_owner.ID] = topmost;
987 topmost.ParentScope = par_si;
989 topmost.AddChild (old_top);
993 topmost.HostsParameters = true;
994 AdjustMethodScope (am, topmost);
998 // Captured fields are only recorded on the topmost CaptureContext, because that
999 // one is the one linked to the owner of instance fields
1001 public void AddField (FieldExpr fe)
1003 if (fe.FieldInfo.IsStatic)
1004 throw new Exception ("Attempt to register a static field as a captured field");
1006 CaptureContext parent = ParentCaptureContext;
1008 parent.AddField (fe);
1010 captured_fields [fe] = fe;
1013 public void CaptureThis ()
1015 CaptureContext parent = ParentCaptureContext;
1017 parent.CaptureThis ();
1018 referenced_this = true;
1021 public bool HaveCapturedVariables {
1023 return have_captured_vars;
1027 public bool HaveCapturedFields {
1029 CaptureContext parent = ParentCaptureContext;
1031 return parent.HaveCapturedFields;
1032 return captured_fields.Count > 0;
1036 public bool IsCaptured (LocalInfo local)
1038 foreach (ScopeInfo si in scopes.Values){
1039 if (si.IsCaptured (local))
1046 // Returns whether the parameter is captured
1048 public bool IsParameterCaptured (string name)
1050 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1053 if (captured_parameters != null)
1054 return captured_parameters [name] != null;
1058 public void EmitAnonymousHelperClasses (EmitContext ec)
1060 if (topmost != null){
1061 topmost.NeedThis = HaveCapturedFields || referenced_this;
1062 topmost.EmitScopeType (ec);
1066 public void CloseAnonymousHelperClasses ()
1068 if (topmost != null)
1069 topmost.CloseTypes ();
1072 public void EmitInitScope (EmitContext ec)
1074 EmitAnonymousHelperClasses (ec);
1075 if (topmost != null)
1076 topmost.EmitInitScope (ec);
1079 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1083 si = (ScopeInfo) scopes [b.ID];
1085 throw new Exception ("Si is null for block " + b.ID);
1086 si.EmitInitScope (ec);
1092 // Emits the opcodes necessary to load the instance of the captured
1095 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1096 AnonymousContainer am)
1098 ILGenerator ig = ec.ig;
1101 if (li.Block.Toplevel == toplevel_owner){
1102 si = GetScopeFromBlock (ec, li.Block);
1103 si.EmitScopeInstance (ig);
1108 ig.Emit (OpCodes.Ldarg_0);
1110 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1114 while (si.ScopeBlock.ID != li.Block.ID){
1115 if (si.ParentLink != null)
1116 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1117 si = si.ParentScope;
1120 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1121 while (si.ScopeBlock.ID != li.Block.ID){
1122 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1123 si = si.ParentScope;
1126 throw new Exception (
1127 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1128 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1135 // Internal routine that loads the instance to reach parameter `name'
1137 void EmitParameterInstance (EmitContext ec, string name)
1139 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1141 cc.EmitParameterInstance (ec, name);
1145 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1146 if (par_info != null){
1148 // FIXME: implementing this.
1151 ILGenerator ig = ec.ig;
1155 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1156 si = GetScopeFromBlock (ec, toplevel_owner);
1157 si.EmitScopeInstance (ig);
1159 si = ec.CurrentAnonymousMethod.Scope;
1160 ig.Emit (OpCodes.Ldarg_0);
1164 while (si.ParentLink != null) {
1165 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1166 si = si.ParentScope;
1172 // Emits the code necessary to load the parameter named `name' within
1173 // an anonymous method.
1175 public void EmitParameter (EmitContext ec, string name)
1177 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1179 cc.EmitParameter (ec, name);
1182 EmitParameterInstance (ec, name);
1183 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1184 if (par_info != null){
1186 // FIXME: implementing this.
1189 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1193 // Implements the assignment of `source' to the paramenter named `name' within
1194 // an anonymous method.
1196 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1198 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1200 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1203 ILGenerator ig = ec.ig;
1204 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1206 EmitParameterInstance (ec, name);
1209 ig.Emit (OpCodes.Dup);
1210 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1214 // Emits the address for the parameter named `name' within
1215 // an anonymous method.
1217 public void EmitAddressOfParameter (EmitContext ec, string name)
1219 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1221 cc.EmitAddressOfParameter (ec, name);
1224 EmitParameterInstance (ec, name);
1225 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1226 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1230 // The following methods are only invoked on the host for the
1231 // anonymous method.
1233 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1235 ILGenerator ig = target.ig;
1236 ScopeInfo si = am.Scope;
1239 ig.Emit (OpCodes.Ldarg_0);
1243 si.EmitInitScope (target);
1244 si.EmitScopeInstance (ig);
1247 ArrayList all_scopes = new ArrayList ();
1249 public void AddScope (ScopeInfo si)
1251 all_scopes.Add (si);
1252 toplevel_owner.RegisterCaptureContext (this);
1256 // Links any scopes that were not linked previously
1258 public void AdjustScopes ()
1260 foreach (ScopeInfo scope in all_scopes){
1261 if (scope.ParentScope != null)
1264 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1265 if (scopes [b.ID] != null){
1266 LinkScope (scope, b.ID);
1271 if (scope.ParentScope == null && ParentCaptureContext != null){
1272 CaptureContext pcc = ParentCaptureContext;
1274 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1275 if (pcc.scopes [b.ID] != null){
1276 pcc.LinkScope (scope, b.ID);