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 (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1003 if (fe.FieldInfo.IsStatic)
1004 throw new Exception ("Attempt to register a static field as a captured field");
1005 CaptureContext parent = ParentCaptureContext;
1006 if (parent != null) {
1007 parent.AddField (ec, am, fe);
1011 if (topmost == null){
1013 // Create one ScopeInfo, if there are none.
1015 topmost = new ScopeInfo (this, toplevel_owner);
1016 scopes [toplevel_owner.ID] = topmost;
1019 AdjustMethodScope (am, topmost);
1022 public void CaptureThis ()
1024 CaptureContext parent = ParentCaptureContext;
1026 parent.CaptureThis ();
1027 referenced_this = true;
1030 public bool HaveCapturedVariables {
1032 return have_captured_vars;
1036 public bool HaveCapturedFields {
1038 CaptureContext parent = ParentCaptureContext;
1040 return parent.HaveCapturedFields;
1041 return captured_fields.Count > 0;
1045 public bool IsCaptured (LocalInfo local)
1047 foreach (ScopeInfo si in scopes.Values){
1048 if (si.IsCaptured (local))
1055 // Returns whether the parameter is captured
1057 public bool IsParameterCaptured (string name)
1059 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1062 if (captured_parameters != null)
1063 return captured_parameters [name] != null;
1067 public void EmitAnonymousHelperClasses (EmitContext ec)
1069 if (topmost != null){
1070 topmost.NeedThis = HaveCapturedFields || referenced_this;
1071 topmost.EmitScopeType (ec);
1075 public void CloseAnonymousHelperClasses ()
1077 if (topmost != null)
1078 topmost.CloseTypes ();
1081 public void EmitInitScope (EmitContext ec)
1083 EmitAnonymousHelperClasses (ec);
1084 if (topmost != null)
1085 topmost.EmitInitScope (ec);
1088 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1092 si = (ScopeInfo) scopes [b.ID];
1094 throw new Exception ("Si is null for block " + b.ID);
1095 si.EmitInitScope (ec);
1101 // Emits the opcodes necessary to load the instance of the captured
1104 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1105 AnonymousContainer am)
1107 ILGenerator ig = ec.ig;
1110 if (li.Block.Toplevel == toplevel_owner){
1111 si = GetScopeFromBlock (ec, li.Block);
1112 si.EmitScopeInstance (ig);
1117 ig.Emit (OpCodes.Ldarg_0);
1119 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1123 while (si.ScopeBlock.ID != li.Block.ID){
1124 if (si.ParentLink != null)
1125 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1126 si = si.ParentScope;
1129 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1130 while (si.ScopeBlock.ID != li.Block.ID){
1131 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1132 si = si.ParentScope;
1135 throw new Exception (
1136 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1137 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1144 // Internal routine that loads the instance to reach parameter `name'
1146 void EmitParameterInstance (EmitContext ec, string name)
1148 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1150 cc.EmitParameterInstance (ec, name);
1154 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1155 if (par_info != null){
1157 // FIXME: implementing this.
1160 ILGenerator ig = ec.ig;
1164 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1165 si = GetScopeFromBlock (ec, toplevel_owner);
1166 si.EmitScopeInstance (ig);
1168 si = ec.CurrentAnonymousMethod.Scope;
1169 ig.Emit (OpCodes.Ldarg_0);
1173 while (si.ParentLink != null) {
1174 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1175 si = si.ParentScope;
1181 // Emits the code necessary to load the parameter named `name' within
1182 // an anonymous method.
1184 public void EmitParameter (EmitContext ec, string name)
1186 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1188 cc.EmitParameter (ec, name);
1191 EmitParameterInstance (ec, name);
1192 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1193 if (par_info != null){
1195 // FIXME: implementing this.
1198 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1202 // Implements the assignment of `source' to the paramenter named `name' within
1203 // an anonymous method.
1205 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1207 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1209 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1212 ILGenerator ig = ec.ig;
1213 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1215 EmitParameterInstance (ec, name);
1218 ig.Emit (OpCodes.Dup);
1219 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1223 // Emits the address for the parameter named `name' within
1224 // an anonymous method.
1226 public void EmitAddressOfParameter (EmitContext ec, string name)
1228 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1230 cc.EmitAddressOfParameter (ec, name);
1233 EmitParameterInstance (ec, name);
1234 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1235 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1239 // The following methods are only invoked on the host for the
1240 // anonymous method.
1242 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1244 ILGenerator ig = target.ig;
1245 ScopeInfo si = am.Scope;
1248 ig.Emit (OpCodes.Ldarg_0);
1252 si.EmitInitScope (target);
1253 si.EmitScopeInstance (ig);
1256 ArrayList all_scopes = new ArrayList ();
1258 public void AddScope (ScopeInfo si)
1260 all_scopes.Add (si);
1261 toplevel_owner.RegisterCaptureContext (this);
1265 // Links any scopes that were not linked previously
1267 public void AdjustScopes ()
1269 foreach (ScopeInfo scope in all_scopes){
1270 if (scope.ParentScope != null)
1273 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1274 if (scopes [b.ID] != null){
1275 LinkScope (scope, b.ID);
1280 if (scope.ParentScope == null && ParentCaptureContext != null){
1281 CaptureContext pcc = ParentCaptureContext;
1283 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1284 if (pcc.scopes [b.ID] != null){
1285 pcc.LinkScope (scope, b.ID);