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 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
47 // The emit context for the anonymous method
48 public EmitContext aec;
49 public InternalParameters amp;
50 public string[] TypeParameters;
51 public Type[] TypeArguments;
55 // The modifiers applied to the method, we aggregate them
57 int method_modifiers = Modifiers.PRIVATE;
60 // During the resolve stage of the anonymous method body,
61 // we discover the actual scope where we are hosted, or
62 // null to host the method in the same class
64 public ScopeInfo Scope;
67 // Points to our container anonymous method if its present
69 public AnonymousMethod ContainerAnonymousMethod;
71 public AnonymousMethod (Parameters parameters, ToplevelBlock container, 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 public override Expression DoResolve (EmitContext ec)
87 // Set class type, set type
90 eclass = ExprClass.Value;
93 // This hack means `The type is not accessible
94 // anywhere', we depend on special conversion
97 type = TypeManager.anonymous_method_type;
102 public override void Emit (EmitContext ec)
104 // nothing, as we only exist to not do anything.
108 // Creates the host for the anonymous method
110 bool CreateMethodHost (EmitContext ec, Type return_type)
113 // Crude hack follows: we replace the TypeBuilder during the
114 // definition to get the method hosted in the right class
117 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
118 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
119 ? current_type : Scope.ScopeTypeBuilder;
121 if (current_type == null)
122 throw new Exception ("The current_type is null");
124 if (type_host == null)
125 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
127 if (current_type == type_host && ec.IsStatic){
129 method_modifiers |= Modifiers.STATIC;
134 string name = "<#AnonymousMethod>" + anonymous_method_count++;
135 MemberName member_name;
137 GenericMethod generic_method = null;
138 if (TypeParameters != null) {
139 TypeArguments args = new TypeArguments (loc);
140 foreach (string t in TypeParameters)
141 args.Add (new SimpleName (t, loc));
143 member_name = new MemberName (name, args);
145 generic_method = new GenericMethod (
146 ec.DeclSpace.NamespaceEntry,
147 (TypeContainer) ec.TypeContainer,
150 generic_method.SetParameterInfo (null);
152 member_name = new MemberName (name);
154 method = new Method (
155 (TypeContainer) ec.TypeContainer, generic_method,
156 new TypeExpression (return_type, loc),
157 method_modifiers, false, member_name,
158 Parameters, null, loc);
159 method.Block = Block;
162 // Swap the TypeBuilder while we define the method, then restore
164 if (current_type != null)
165 ec.TypeContainer.TypeBuilder = type_host;
166 bool res = method.Define ();
167 if (current_type != null)
168 ec.TypeContainer.TypeBuilder = current_type;
173 void Error_ParameterMismatch (Type t)
175 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
176 "{0}' since there is a parameter mismatch", t);
180 // Returns true if this anonymous method can be implicitly
181 // converted to the delegate type `delegate_type'
183 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
186 // At this point its the first time we know the return type that is
187 // needed for the anonymous method. We create the method here.
190 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec, delegate_type, loc);
191 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
192 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
194 if (delegate_type.IsGenericInstance) {
195 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
197 Type def = delegate_type.GetGenericTypeDefinition ();
198 Type[] tparam = TypeManager.GetTypeArguments (def);
199 TypeParameters = new string [tparam.Length];
200 for (int i = 0; i < tparam.Length; i++)
201 TypeParameters [i] = tparam [i].Name;
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);
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);
234 Parameters = new Parameters (fixedpars, variable, loc);
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,
246 "Anonymous method has {0} parameters, while delegate requires {1}",
247 amp.Count, invoke_pd.Count);
248 Error_ParameterMismatch (delegate_type);
253 for (int i = 0; i < amp.Count; i++){
254 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
256 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
258 Error_ParameterMismatch (delegate_type);
259 Report.Error (1677, loc, "Parameter '{0}' should not be declared with the '{1}' keyword",
260 i+1, amp.ModifierDesc (i));
265 if (amp_mod != invoke_pd.ParameterModifier (i)){
267 Report.Error (1676, loc,
268 "Signature mismatch in parameter modifier for parameter #0", i + 1);
269 Error_ParameterMismatch (delegate_type);
274 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
276 Report.Error (1678, loc,
277 "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i + 1,
278 TypeManager.CSharpName (invoke_pd.ParameterType (i)),
279 TypeManager.CSharpName (amp.ParameterType (i)));
280 Error_ParameterMismatch (delegate_type);
287 // If we are only probing, return ourselves
293 // Second: the return type of the delegate must be compatible with
294 // the anonymous type. Instead of doing a pass to examine the block
295 // we satisfy the rule by setting the return type on the EmitContext
296 // to be the delegate type return type.
299 //MethodBuilder builder = method_data.MethodBuilder;
300 //ILGenerator ig = builder.GetILGenerator ();
303 aec = new EmitContext (
304 ec.TypeContainer, ec.DeclSpace, loc, null,
305 invoke_mb.ReturnType,
306 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
307 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
308 (ec.IsStatic ? Modifiers.STATIC : 0),
309 /* No constructor */ false);
311 aec.CurrentAnonymousMethod = this;
312 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
313 ContainingBlock = ec.CurrentBlock;
315 if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
316 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
321 public MethodInfo GetMethodBuilder ()
323 MethodInfo builder = method.MethodData.MethodBuilder;
324 if (TypeArguments != null)
325 return builder.BindGenericParameters (TypeArguments);
330 public bool EmitMethod (EmitContext ec)
332 if (!CreateMethodHost (ec, invoke_mb.ReturnType))
335 MethodBuilder builder = method.MethodData.MethodBuilder;
336 ILGenerator ig = builder.GetILGenerator ();
339 Parameters.LabelParameters (aec, builder, loc);
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 static void Error_AddressOfCapturedVar (string name, Location loc)
354 Report.Error (1686, loc,
355 "Variable {0} is captured in an anonymous method and its address is also being taken: they are exclusive", name);
360 // This will emit the code for the delegate, as well delegate creation on the host
362 public class AnonymousDelegate : DelegateCreation {
365 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
372 public override Expression DoResolve (EmitContext ec)
374 eclass = ExprClass.Value;
379 public override void Emit (EmitContext ec)
381 if (!am.EmitMethod (ec))
385 // Now emit the delegate creation.
387 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
388 delegate_instance_expression = new AnonymousInstance (am);
390 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
391 constructor_method = ((MethodGroupExpr) ml).Methods [0];
392 delegate_method = am.GetMethodBuilder ();
396 class AnonymousInstance : Expression {
399 public AnonymousInstance (AnonymousMethod am)
402 eclass = ExprClass.Value;
405 public override Expression DoResolve (EmitContext ec)
410 public override void Emit (EmitContext ec)
412 am.aec.EmitMethodHostInstance (ec, am);
417 class CapturedParameter {
419 public FieldBuilder FieldBuilder;
422 public CapturedParameter (Type type, int idx)
430 // Here we cluster all the variables captured on a given scope, we also
431 // keep some extra information that might be required on each scope.
433 public class ScopeInfo {
434 public CaptureContext CaptureContext;
435 public ScopeInfo ParentScope;
436 public Block ScopeBlock;
437 public bool NeedThis = false;
438 public bool HostsParameters = false;
440 // For tracking the number of scopes created.
445 ArrayList locals = new ArrayList ();
446 ArrayList children = new ArrayList ();
449 // The types and fields generated
451 public TypeBuilder ScopeTypeBuilder;
452 public ConstructorBuilder ScopeConstructor;
453 public FieldBuilder THIS;
454 public FieldBuilder ParentLink;
457 // Points to the object of type `ScopeTypeBuilder' that
458 // holds the data for the scope
460 public LocalBuilder ScopeInstance;
463 public ScopeInfo (CaptureContext cc, Block b)
472 public void AddLocal (LocalInfo li)
474 if (locals.Contains (li))
480 public bool IsCaptured (LocalInfo li)
482 return locals.Contains (li);
485 internal void AddChild (ScopeInfo si)
487 if (children.Contains (si))
491 // If any of the current children should be a children of `si', move them there
493 ArrayList move_queue = null;
494 foreach (ScopeInfo child in children){
495 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
496 if (move_queue == null)
497 move_queue = new ArrayList ();
498 move_queue.Add (child);
499 child.ParentScope = si;
506 if (move_queue != null){
507 foreach (ScopeInfo child in move_queue){
508 children.Remove (child);
513 static int indent = 0;
517 for (int i = 0; i < indent; i++)
523 //Console.WriteLine (Environment.StackTrace);
525 Console.WriteLine ("START");
528 Console.WriteLine ("NeedThis=" + NeedThis);
529 foreach (LocalInfo li in locals){
531 Console.WriteLine ("var {0}", li.Name);
534 foreach (ScopeInfo si in children)
538 Console.WriteLine ("END");
541 public string MakeHelperName ()
543 return String.Format ("<>AnonHelp<{0}>", id);
546 public void EmitScopeConstructor ()
548 Type [] constructor_types = TypeManager.NoTypes;
549 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
550 ScopeConstructor = ScopeTypeBuilder.DefineConstructor (
551 MethodAttributes.Public | MethodAttributes.HideBySig |
552 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
553 CallingConventions.HasThis, constructor_types);
554 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
555 TypeManager.RegisterMethod (ScopeConstructor, parameter_info, constructor_types);
557 ILGenerator cig = ScopeConstructor.GetILGenerator ();
558 cig.Emit (OpCodes.Ldarg_0);
559 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
560 cig.Emit (OpCodes.Ret);
563 public void EmitScopeType (EmitContext ec)
567 if (ScopeTypeBuilder != null)
570 TypeBuilder container = ec.TypeContainer.TypeBuilder;
572 ScopeTypeBuilder = container.DefineNestedType (
573 MakeHelperName (), TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly,
574 TypeManager.object_type, null);
577 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
579 if (ParentScope != null){
580 if (ParentScope.ScopeTypeBuilder == null){
581 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
584 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
585 FieldAttributes.Assembly);
588 if (NeedThis && ParentScope != null)
589 throw new Exception ("I was not expecting THIS && having a parent");
591 foreach (LocalInfo info in locals){
592 info.FieldBuilder = ScopeTypeBuilder.DefineField (
593 info.Name, info.VariableType, FieldAttributes.Assembly);
596 if (HostsParameters){
597 Hashtable captured_parameters = CaptureContext.captured_parameters;
599 foreach (DictionaryEntry de in captured_parameters){
600 string name = (string) de.Key;
601 CapturedParameter cp = (CapturedParameter) de.Value;
604 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
605 cp.FieldBuilder = fb;
609 EmitScopeConstructor ();
610 foreach (ScopeInfo si in children){
611 si.EmitScopeType (ec);
615 public void CloseTypes ()
617 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
618 foreach (ScopeInfo si in children)
623 // Emits the initialization code for the scope
625 public void EmitInitScope (EmitContext ec)
627 ILGenerator ig = ec.ig;
632 if (ScopeConstructor == null)
633 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
635 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
636 ScopeInstance = ig.DeclareLocal (ScopeTypeBuilder);
637 ig.Emit (OpCodes.Stloc, ScopeInstance);
640 ig.Emit (OpCodes.Ldloc, ScopeInstance);
641 ig.Emit (OpCodes.Ldarg_0);
642 ig.Emit (OpCodes.Stfld, THIS);
646 // Copy the parameter values, if any
648 int extra = ec.IsStatic ? 0 : 1;
649 if (HostsParameters){
650 Hashtable captured_parameters = CaptureContext.captured_parameters;
652 foreach (DictionaryEntry de in captured_parameters){
653 CapturedParameter cp = (CapturedParameter) de.Value;
655 ig.Emit (OpCodes.Ldloc, ScopeInstance);
656 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
657 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
661 if (ParentScope != null){
662 if (!ParentScope.inited)
663 ParentScope.EmitInitScope (ec);
666 // Only emit initialization in our capturecontext world
668 if (ParentScope.CaptureContext == CaptureContext){
669 ig.Emit (OpCodes.Ldloc, ScopeInstance);
670 ig.Emit (OpCodes.Ldloc, ParentScope.ScopeInstance);
671 ig.Emit (OpCodes.Stfld, ParentLink);
673 ig.Emit (OpCodes.Ldloc, ScopeInstance);
674 ig.Emit (OpCodes.Ldarg_0);
675 ig.Emit (OpCodes.Stfld, ParentLink);
681 static void DoPath (StringBuilder sb, ScopeInfo start)
683 if (start.ParentScope != null){
684 DoPath (sb, start.ParentScope);
687 sb.Append ((start.id).ToString ());
690 public override string ToString ()
692 StringBuilder sb = new StringBuilder ();
695 if (CaptureContext != null){
696 sb.Append (CaptureContext.ToString ());
703 return sb.ToString ();
708 // CaptureContext objects are created on demand if a method has
709 // anonymous methods and kept on the ToplevelBlock.
711 // If they exist, all ToplevelBlocks in the containing block are
712 // linked together (children pointing to their parents).
714 public class CaptureContext {
715 public static int count;
720 // Points to the toplevel block that owns this CaptureContext
722 ToplevelBlock toplevel_owner;
723 Hashtable scopes = new Hashtable ();
724 bool have_captured_vars = false;
725 bool referenced_this = false;
726 ScopeInfo topmost = null;
731 Hashtable captured_fields = new Hashtable ();
732 Hashtable captured_variables = new Hashtable ();
733 public Hashtable captured_parameters = new Hashtable ();
734 public AnonymousMethod Host;
736 public CaptureContext (ToplevelBlock toplevel_owner, Location loc, AnonymousMethod host)
739 this.toplevel_owner = toplevel_owner;
746 void DoPath (StringBuilder sb, CaptureContext cc)
748 if (cc.ParentCaptureContext != null){
749 DoPath (sb, cc.ParentCaptureContext);
752 sb.Append (cc_id.ToString ());
755 public override string ToString ()
757 StringBuilder sb = new StringBuilder ();
761 return sb.ToString ();
764 public ToplevelBlock ParentToplevel {
766 return toplevel_owner.Container;
770 public CaptureContext ParentCaptureContext {
772 ToplevelBlock parent = ParentToplevel;
774 return (parent == null) ? null : parent.CaptureContext;
778 // Returns the deepest of two scopes
779 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
791 // If they Scopes are on the same CaptureContext, we do the double
792 // checks just so if there is an invariant change in the future,
793 // we get the exception at the end
795 for (p = a; p != null; p = p.ParentScope)
799 for (p = b; p != null; p = p.ParentScope)
803 CaptureContext ca = a.CaptureContext;
804 CaptureContext cb = b.CaptureContext;
806 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
810 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
813 throw new Exception ("Should never be reached");
816 void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
818 am.Scope = Deepest (am.Scope, scope);
821 void LinkScope (ScopeInfo scope, int id)
823 ScopeInfo parent = (ScopeInfo) scopes [id];
824 scope.ParentScope = parent;
825 parent.AddChild (scope);
827 if (scope == topmost)
831 public void AddLocal (AnonymousMethod am, LocalInfo li)
833 if (li.Block.Toplevel != toplevel_owner){
834 ParentCaptureContext.AddLocal (am, li);
837 int block_id = li.Block.ID;
839 if (scopes [block_id] == null){
840 scope = new ScopeInfo (this, li.Block);
841 scopes [block_id] = scope;
843 scope = (ScopeInfo) scopes [block_id];
845 if (topmost == null){
850 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
851 if (scopes [b.ID] != null){
852 LinkScope (scope, b.ID);
857 if (scope.ParentScope == null && ParentCaptureContext != null){
858 CaptureContext pcc = ParentCaptureContext;
860 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
861 if (pcc.scopes [b.ID] != null){
862 pcc.LinkScope (scope, b.ID);
873 AdjustMethodScope (Host, topmost);
878 AdjustMethodScope (am, scope);
880 if (captured_variables [li] != null)
883 have_captured_vars = true;
884 captured_variables [li] = li;
889 // Retursn the CaptureContext for the block that defines the parameter `name'
891 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
893 ToplevelBlock container = current.Container;
894 if (container != null){
895 CaptureContext cc = _ContextForParameter (container, name);
899 if (current.IsParameterReference (name))
900 return current.ToplevelBlockCaptureContext;
904 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
906 CaptureContext cc = _ContextForParameter (current, name);
908 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
913 // Records the captured parameter at the appropriate CaptureContext
915 public void AddParameter (EmitContext ec, AnonymousMethod am, string name, Type t, int idx)
917 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
919 cc.AddParameterToContext (am, name, t, idx);
923 // Records the parameters in the context
925 void AddParameterToContext (AnonymousMethod am, string name, Type t, int idx)
927 if (captured_parameters == null)
928 captured_parameters = new Hashtable ();
929 if (captured_parameters [name] != null)
931 captured_parameters [name] = new CapturedParameter (t, idx);
933 if (topmost == null){
935 // Create one ScopeInfo, if there are none.
937 topmost = new ScopeInfo (this, toplevel_owner);
938 scopes [toplevel_owner.ID] = topmost;
941 // If the topmost ScopeInfo is not at the topblock level, insert
942 // a new ScopeInfo there.
944 // FIXME: This code probably should be evolved to be like the code
947 if (topmost.ScopeBlock != toplevel_owner){
948 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
949 ScopeInfo old_top = topmost;
950 scopes [toplevel_owner.ID] = topmost;
951 topmost.ParentScope = par_si;
953 topmost.AddChild (old_top);
957 topmost.HostsParameters = true;
958 AdjustMethodScope (am, topmost);
962 // Captured fields are only recorded on the topmost CaptureContext, because that
963 // one is the one linked to the owner of instance fields
965 public void AddField (FieldExpr fe)
967 if (fe.FieldInfo.IsStatic)
968 throw new Exception ("Attempt to register a static field as a captured field");
970 CaptureContext parent = ParentCaptureContext;
972 parent.AddField (fe);
974 captured_fields [fe] = fe;
977 public void CaptureThis ()
979 CaptureContext parent = ParentCaptureContext;
981 parent.CaptureThis ();
982 referenced_this = true;
985 public bool HaveCapturedVariables {
987 return have_captured_vars;
991 public bool HaveCapturedFields {
993 CaptureContext parent = ParentCaptureContext;
995 return parent.HaveCapturedFields;
996 return captured_fields.Count > 0;
1000 public bool IsCaptured (LocalInfo local)
1002 foreach (ScopeInfo si in scopes.Values){
1003 if (si.IsCaptured (local))
1010 // Returns whether the parameter is captured
1012 public bool IsParameterCaptured (string name)
1014 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1017 if (captured_parameters != null)
1018 return captured_parameters [name] != null;
1022 public void EmitAnonymousHelperClasses (EmitContext ec)
1024 if (topmost != null){
1025 topmost.NeedThis = HaveCapturedFields || referenced_this;
1026 topmost.EmitScopeType (ec);
1030 public void CloseAnonymousHelperClasses ()
1032 if (topmost != null)
1033 topmost.CloseTypes ();
1036 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1040 si = (ScopeInfo) scopes [b.ID];
1042 throw new Exception ("Si is null for block " + b.ID);
1043 si.EmitInitScope (ec);
1049 // Emits the opcodes necessary to load the instance of the captured
1052 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li, AnonymousMethod am)
1054 ILGenerator ig = ec.ig;
1057 if (li.Block.Toplevel == toplevel_owner){
1058 si = GetScopeFromBlock (ec, li.Block);
1059 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1064 ig.Emit (OpCodes.Ldarg_0);
1066 while (si.ScopeBlock.ID != li.Block.ID){
1067 if (si.ParentLink != null)
1068 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1069 si = si.ParentScope;
1072 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1073 while (si.ScopeBlock.ID != li.Block.ID){
1074 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1075 si = si.ParentScope;
1078 throw new Exception (
1079 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1080 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1087 // Internal routine that loads the instance to reach parameter `name'
1089 void EmitParameterInstance (EmitContext ec, string name)
1091 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1093 cc.EmitParameterInstance (ec, name);
1097 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1098 if (par_info != null){
1100 // FIXME: implementing this.
1103 ILGenerator ig = ec.ig;
1107 if (ec.CurrentBlock.Toplevel == toplevel_owner){
1108 si = GetScopeFromBlock (ec, toplevel_owner);
1109 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1113 si = ec.CurrentAnonymousMethod.Scope;
1114 ig.Emit (OpCodes.Ldarg_0);
1116 while (si.ParentLink != null) {
1117 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1118 si = si.ParentScope;
1124 // Emits the code necessary to load the parameter named `name' within
1125 // an anonymous method.
1127 public void EmitParameter (EmitContext ec, string name)
1129 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1131 cc.EmitParameter (ec, name);
1134 EmitParameterInstance (ec, name);
1135 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1136 if (par_info != null){
1138 // FIXME: implementing this.
1141 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1145 // Implements the assignment of `source' to the paramenter named `name' within
1146 // an anonymous method.
1148 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1150 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1152 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1155 ILGenerator ig = ec.ig;
1156 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1158 EmitParameterInstance (ec, name);
1161 ig.Emit (OpCodes.Dup);
1162 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1166 // Emits the address for the parameter named `name' within
1167 // an anonymous method.
1169 public void EmitAddressOfParameter (EmitContext ec, string name)
1171 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1173 cc.EmitAddressOfParameter (ec, name);
1176 EmitParameterInstance (ec, name);
1177 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1178 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1182 // The following methods are only invoked on the host for the
1183 // anonymous method.
1185 public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
1187 ILGenerator ig = target.ig;
1188 ScopeInfo si = am.Scope;
1191 ig.Emit (OpCodes.Ldarg_0);
1195 si.EmitInitScope (target);
1196 ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
1199 ArrayList all_scopes = new ArrayList ();
1201 public void AddScope (ScopeInfo si)
1203 all_scopes.Add (si);
1204 toplevel_owner.RegisterCaptureContext (this);
1208 // Links any scopes that were not linked previously
1210 public void AdjustScopes ()
1212 foreach (ScopeInfo scope in all_scopes){
1213 if (scope.ParentScope != null)
1216 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1217 if (scopes [b.ID] != null){
1218 LinkScope (scope, b.ID);
1223 if (scope.ParentScope == null && ParentCaptureContext != null){
1224 CaptureContext pcc = ParentCaptureContext;
1226 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1227 if (pcc.scopes [b.ID] != null){
1228 pcc.LinkScope (scope, b.ID);