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 public string[] TypeParameters;
52 public Type[] TypeArguments;
53 protected bool unreachable;
56 // The modifiers applied to the method, we aggregate them
58 protected int method_modifiers = Modifiers.PRIVATE;
61 // During the resolve stage of the anonymous method body,
62 // we discover the actual scope where we are hosted, or
63 // null to host the method in the same class
65 public ScopeInfo Scope;
68 // Points to our container anonymous method if its present
70 public AnonymousContainer ContainerAnonymousMethod;
72 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
73 ToplevelBlock block, Location l)
75 Parameters = parameters;
80 // The order is important: this setups the CaptureContext tree hierarchy.
82 container.SetHaveAnonymousMethods (l, this);
83 block.SetHaveAnonymousMethods (l, this);
86 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
89 Parameters = parameters;
90 Block = new ToplevelBlock (container, Parameters, l);
94 // The order is important: this setups the CaptureContext tree hierarchy.
96 container.SetHaveAnonymousMethods (loc, this);
97 Block.SetHaveAnonymousMethods (loc, this);
100 public override Expression DoResolve (EmitContext ec)
103 // Set class type, set type
106 eclass = ExprClass.Value;
109 // This hack means `The type is not accessible
110 // anywhere', we depend on special conversion
113 type = TypeManager.anonymous_method_type;
118 protected abstract bool CreateMethodHost (EmitContext ec);
120 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
122 public abstract Iterator Iterator {
126 public abstract bool IsIterator {
131 public class AnonymousMethod : AnonymousContainer
133 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
134 ToplevelBlock block, Location l)
135 : base (parameters, container, block, l)
139 public override Iterator Iterator {
143 public override bool IsIterator {
144 get { return false; }
147 public override void Emit (EmitContext ec)
149 // nothing, as we only exist to not do anything.
153 // Creates the host for the anonymous method
155 protected override bool CreateMethodHost (EmitContext ec)
158 // Crude hack follows: we replace the TypeBuilder during the
159 // definition to get the method hosted in the right class
162 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
163 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
164 ? current_type : Scope.ScopeTypeBuilder;
166 if (current_type == null)
167 throw new Exception ("The current_type is null");
169 if (type_host == null)
170 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
172 if (current_type == type_host && ec.IsStatic){
174 method_modifiers |= Modifiers.STATIC;
179 string name = "<#AnonymousMethod>" + anonymous_method_count++;
180 MemberName member_name;
182 GenericMethod generic_method = null;
183 if (TypeParameters != null) {
184 TypeArguments args = new TypeArguments (loc);
185 foreach (string t in TypeParameters)
186 args.Add (new SimpleName (t, loc));
188 member_name = new MemberName (name, args);
190 generic_method = new GenericMethod (
191 ec.DeclSpace.NamespaceEntry,
192 (TypeContainer) ec.TypeContainer,
195 generic_method.SetParameterInfo (null);
197 member_name = new MemberName (name);
199 method = new Method (
200 (TypeContainer) ec.TypeContainer, generic_method,
201 new TypeExpression (invoke_mb.ReturnType, loc),
202 method_modifiers, false, member_name,
203 Parameters, null, loc);
204 method.Block = Block;
207 // Swap the TypeBuilder while we define the method, then restore
209 if (current_type != null)
210 ec.TypeContainer.TypeBuilder = type_host;
211 bool res = method.Define ();
212 if (current_type != null)
213 ec.TypeContainer.TypeBuilder = current_type;
218 void Error_ParameterMismatch (Type t)
220 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
221 "{0}' since there is a parameter mismatch", t);
225 // Returns true if this anonymous method can be implicitly
226 // converted to the delegate type `delegate_type'
228 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
231 // At this point its the first time we know the return type that is
232 // needed for the anonymous method. We create the method here.
235 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec, delegate_type, loc);
236 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
237 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
239 if (delegate_type.IsGenericInstance) {
240 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
242 Type def = delegate_type.GetGenericTypeDefinition ();
243 Type[] tparam = TypeManager.GetTypeArguments (def);
244 TypeParameters = new string [tparam.Length];
245 for (int i = 0; i < tparam.Length; i++)
246 TypeParameters [i] = tparam [i].Name;
249 if (Parameters == null){
253 // We provide a set of inaccessible parameters
256 for (i = 0; i < invoke_pd.Count; i++){
257 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
260 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
261 Parameter [] fixedpars = new Parameter [n];
263 for (i = j = 0; i < invoke_pd.Count; i++){
264 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
266 fixedpars [j] = new Parameter (
267 new TypeExpression (invoke_pd.ParameterType (i), loc),
268 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
272 Parameter variable = null;
273 if (params_idx != -1){
274 variable = new Parameter (
275 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
276 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
279 Parameters = new Parameters (fixedpars, variable);
283 // First, parameter types of `delegate_type' must be compatible
284 // with the anonymous method.
286 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
288 if (amp.Count != invoke_pd.Count){
290 Report.Error (1593, loc,
291 "Anonymous method has {0} parameters, while delegate requires {1}",
292 amp.Count, invoke_pd.Count);
293 Error_ParameterMismatch (delegate_type);
298 for (int i = 0; i < amp.Count; i++){
299 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
301 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
303 Error_ParameterMismatch (delegate_type);
304 Report.Error (1677, loc, "Parameter '{0}' should not be declared with the '{1}' keyword",
305 i+1, amp.ModifierDesc (i));
310 if (amp_mod != invoke_pd.ParameterModifier (i)){
312 Report.Error (1676, loc,
313 "Signature mismatch in parameter modifier for parameter #0", i + 1);
314 Error_ParameterMismatch (delegate_type);
319 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
321 Report.Error (1678, loc,
322 "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i + 1,
323 TypeManager.CSharpName (invoke_pd.ParameterType (i)),
324 TypeManager.CSharpName (amp.ParameterType (i)));
325 Error_ParameterMismatch (delegate_type);
332 // If we are only probing, return ourselves
338 // Second: the return type of the delegate must be compatible with
339 // the anonymous type. Instead of doing a pass to examine the block
340 // we satisfy the rule by setting the return type on the EmitContext
341 // to be the delegate type return type.
344 //MethodBuilder builder = method_data.MethodBuilder;
345 //ILGenerator ig = builder.GetILGenerator ();
348 aec = new EmitContext (
349 ec.TypeContainer, ec.DeclSpace, loc, null,
350 invoke_mb.ReturnType,
351 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
352 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
353 (ec.IsStatic ? Modifiers.STATIC : 0),
354 /* No constructor */ false);
356 aec.CurrentAnonymousMethod = this;
357 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
358 ContainingBlock = ec.CurrentBlock;
360 if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
361 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
366 public MethodInfo GetMethodBuilder ()
368 MethodInfo builder = method.MethodData.MethodBuilder;
369 if (TypeArguments != null)
370 return builder.BindGenericParameters (TypeArguments);
375 public bool EmitMethod (EmitContext ec)
377 if (!CreateMethodHost (ec))
380 MethodBuilder builder = method.MethodData.MethodBuilder;
381 ILGenerator ig = builder.GetILGenerator ();
384 Parameters.LabelParameters (aec, builder);
387 // Adjust based on the computed state of the
388 // method from CreateMethodHost
390 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
392 aec.EmitMeta (Block, amp);
393 aec.EmitResolvedTopBlock (Block, unreachable);
397 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
399 TypeBuilder container = ec.TypeContainer.TypeBuilder;
400 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
402 scope.ScopeTypeBuilder = container.DefineNestedType (
403 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
404 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
406 Type [] constructor_types = TypeManager.NoTypes;
407 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
408 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
409 MethodAttributes.Public | MethodAttributes.HideBySig |
410 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
411 CallingConventions.HasThis, constructor_types);
412 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
413 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
415 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
416 cig.Emit (OpCodes.Ldarg_0);
417 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
418 cig.Emit (OpCodes.Ret);
421 public static void Error_AddressOfCapturedVar (string name, Location loc)
423 Report.Error (1686, loc,
424 "Variable {0} is captured in an anonymous method and its address is also being taken: they are exclusive", name);
429 // This will emit the code for the delegate, as well delegate creation on the host
431 public class AnonymousDelegate : DelegateCreation {
434 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
441 public override Expression DoResolve (EmitContext ec)
443 eclass = ExprClass.Value;
448 public override void Emit (EmitContext ec)
450 if (!am.EmitMethod (ec))
454 // Now emit the delegate creation.
456 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
457 delegate_instance_expression = new AnonymousInstance (am);
459 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
460 constructor_method = ((MethodGroupExpr) ml).Methods [0];
461 delegate_method = am.GetMethodBuilder ();
465 class AnonymousInstance : Expression {
468 public AnonymousInstance (AnonymousMethod am)
471 eclass = ExprClass.Value;
474 public override Expression DoResolve (EmitContext ec)
479 public override void Emit (EmitContext ec)
481 am.aec.EmitMethodHostInstance (ec, am);
486 class CapturedParameter {
488 public FieldBuilder FieldBuilder;
491 public CapturedParameter (Type type, int idx)
499 // Here we cluster all the variables captured on a given scope, we also
500 // keep some extra information that might be required on each scope.
502 public class ScopeInfo {
503 public CaptureContext CaptureContext;
504 public ScopeInfo ParentScope;
505 public Block ScopeBlock;
506 public bool NeedThis = false;
507 public bool HostsParameters = false;
509 // For tracking the number of scopes created.
514 ArrayList locals = new ArrayList ();
515 ArrayList children = new ArrayList ();
518 // The types and fields generated
520 public TypeBuilder ScopeTypeBuilder;
521 public ConstructorBuilder ScopeConstructor;
522 public FieldBuilder THIS;
523 public FieldBuilder ParentLink;
526 // Points to the object of type `ScopeTypeBuilder' that
527 // holds the data for the scope
529 LocalBuilder scope_instance;
531 public ScopeInfo (CaptureContext cc, Block b)
540 public void AddLocal (LocalInfo li)
542 if (locals.Contains (li))
548 public bool IsCaptured (LocalInfo li)
550 return locals.Contains (li);
553 internal void AddChild (ScopeInfo si)
555 if (children.Contains (si))
559 // If any of the current children should be a children of `si', move them there
561 ArrayList move_queue = null;
562 foreach (ScopeInfo child in children){
563 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
564 if (move_queue == null)
565 move_queue = new ArrayList ();
566 move_queue.Add (child);
567 child.ParentScope = si;
574 if (move_queue != null){
575 foreach (ScopeInfo child in move_queue){
576 children.Remove (child);
581 static int indent = 0;
585 for (int i = 0; i < indent; i++)
591 //Console.WriteLine (Environment.StackTrace);
593 Console.WriteLine ("START");
596 Console.WriteLine ("NeedThis=" + NeedThis);
597 foreach (LocalInfo li in locals){
599 Console.WriteLine ("var {0}", li.Name);
602 foreach (ScopeInfo si in children)
606 Console.WriteLine ("END");
609 public string MakeHelperName ()
611 return String.Format ("<>AnonHelp<{0}>", id);
614 public void EmitScopeType (EmitContext ec)
618 if (ScopeTypeBuilder != null)
621 TypeBuilder container = ec.TypeContainer.TypeBuilder;
623 CaptureContext.Host.CreateScopeType (ec, this);
626 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
628 if (ParentScope != null){
629 if (ParentScope.ScopeTypeBuilder == null){
630 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
633 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
634 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
635 FieldAttributes.Assembly);
638 if (NeedThis && ParentScope != null)
639 throw new Exception ("I was not expecting THIS && having a parent");
641 foreach (LocalInfo info in locals)
642 info.FieldBuilder = ScopeTypeBuilder.DefineField (
643 info.Name, info.VariableType, FieldAttributes.Assembly);
645 if (HostsParameters){
646 Hashtable captured_parameters = CaptureContext.captured_parameters;
648 foreach (DictionaryEntry de in captured_parameters){
649 string name = (string) de.Key;
650 CapturedParameter cp = (CapturedParameter) de.Value;
653 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
654 cp.FieldBuilder = fb;
658 foreach (ScopeInfo si in children){
659 si.EmitScopeType (ec);
663 public void CloseTypes ()
665 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
666 foreach (ScopeInfo si in children)
671 // Emits the initialization code for the scope
673 public void EmitInitScope (EmitContext ec)
675 ILGenerator ig = ec.ig;
680 if (ScopeConstructor == null)
681 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
683 if (!CaptureContext.Host.IsIterator) {
684 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
685 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
686 ig.Emit (OpCodes.Stloc, scope_instance);
690 if (CaptureContext.Host.IsIterator) {
691 ig.Emit (OpCodes.Ldarg_0);
692 ig.Emit (OpCodes.Ldarg_1);
694 ig.Emit (OpCodes.Ldloc, scope_instance);
695 ig.Emit (OpCodes.Ldarg_0);
697 ig.Emit (OpCodes.Stfld, THIS);
701 // Copy the parameter values, if any
703 int extra = ec.IsStatic ? 0 : 1;
704 if (CaptureContext.Host.IsIterator)
706 if (HostsParameters){
707 Hashtable captured_parameters = CaptureContext.captured_parameters;
709 foreach (DictionaryEntry de in captured_parameters){
710 CapturedParameter cp = (CapturedParameter) de.Value;
712 EmitScopeInstance (ig);
713 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
714 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
718 if (ParentScope != null){
719 if (!ParentScope.inited)
720 ParentScope.EmitInitScope (ec);
722 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
724 // Only emit initialization in our capturecontext world
726 if (ParentScope.CaptureContext == CaptureContext){
727 EmitScopeInstance (ig);
728 ParentScope.EmitScopeInstance (ig);
729 ig.Emit (OpCodes.Stfld, ParentLink);
731 EmitScopeInstance (ig);
732 ig.Emit (OpCodes.Ldarg_0);
733 ig.Emit (OpCodes.Stfld, ParentLink);
740 public void EmitScopeInstance (ILGenerator ig)
742 if (CaptureContext.Host.IsIterator)
743 ig.Emit (OpCodes.Ldarg_0);
745 ig.Emit (OpCodes.Ldloc, scope_instance);
748 static void DoPath (StringBuilder sb, ScopeInfo start)
750 if (start.ParentScope != null){
751 DoPath (sb, start.ParentScope);
754 sb.Append ((start.id).ToString ());
757 public override string ToString ()
759 StringBuilder sb = new StringBuilder ();
762 if (CaptureContext != null){
763 sb.Append (CaptureContext.ToString ());
770 return sb.ToString ();
775 // CaptureContext objects are created on demand if a method has
776 // anonymous methods and kept on the ToplevelBlock.
778 // If they exist, all ToplevelBlocks in the containing block are
779 // linked together (children pointing to their parents).
781 public class CaptureContext {
782 public static int count;
787 // Points to the toplevel block that owns this CaptureContext
789 ToplevelBlock toplevel_owner;
790 Hashtable scopes = new Hashtable ();
791 bool have_captured_vars = false;
792 bool referenced_this = false;
793 ScopeInfo topmost = null;
798 Hashtable captured_fields = new Hashtable ();
799 Hashtable captured_variables = new Hashtable ();
800 public Hashtable captured_parameters = new Hashtable ();
801 public AnonymousContainer Host;
803 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
804 AnonymousContainer host)
807 this.toplevel_owner = toplevel_owner;
814 void DoPath (StringBuilder sb, CaptureContext cc)
816 if (cc.ParentCaptureContext != null){
817 DoPath (sb, cc.ParentCaptureContext);
820 sb.Append (cc.cc_id.ToString ());
823 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
825 toplevel_owner = new_toplevel;
828 for (CaptureContext cc = ParentCaptureContext; cc != null;
829 cc = cc.ParentCaptureContext) {
834 public override string ToString ()
836 StringBuilder sb = new StringBuilder ();
840 return sb.ToString ();
843 public ToplevelBlock ParentToplevel {
845 return toplevel_owner.Container;
849 public CaptureContext ParentCaptureContext {
851 ToplevelBlock parent = ParentToplevel;
853 return (parent == null) ? null : parent.CaptureContext;
857 // Returns the deepest of two scopes
858 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
870 // If they Scopes are on the same CaptureContext, we do the double
871 // checks just so if there is an invariant change in the future,
872 // we get the exception at the end
874 for (p = a; p != null; p = p.ParentScope)
878 for (p = b; p != null; p = p.ParentScope)
882 CaptureContext ca = a.CaptureContext;
883 CaptureContext cb = b.CaptureContext;
885 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
889 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
892 throw new Exception ("Should never be reached");
895 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
897 am.Scope = Deepest (am.Scope, scope);
900 void LinkScope (ScopeInfo scope, int id)
902 ScopeInfo parent = (ScopeInfo) scopes [id];
903 scope.ParentScope = parent;
904 parent.AddChild (scope);
906 if (scope == topmost)
910 public void AddLocal (AnonymousContainer am, LocalInfo li)
912 if (li.Block.Toplevel != toplevel_owner){
913 ParentCaptureContext.AddLocal (am, li);
916 int block_id = li.Block.ID;
918 if (scopes [block_id] == null){
919 scope = new ScopeInfo (this, li.Block);
920 scopes [block_id] = scope;
922 scope = (ScopeInfo) scopes [block_id];
924 if (topmost == null){
929 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
930 if (scopes [b.ID] != null){
931 LinkScope (scope, b.ID);
936 if (scope.ParentScope == null && ParentCaptureContext != null){
937 CaptureContext pcc = ParentCaptureContext;
939 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
940 if (pcc.scopes [b.ID] != null){
941 pcc.LinkScope (scope, b.ID);
952 AdjustMethodScope (Host, topmost);
957 AdjustMethodScope (am, scope);
959 if (captured_variables [li] != null)
962 have_captured_vars = true;
963 captured_variables [li] = li;
968 // Retursn the CaptureContext for the block that defines the parameter `name'
970 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
972 ToplevelBlock container = current.Container;
973 if (container != null){
974 CaptureContext cc = _ContextForParameter (container, name);
978 if (current.IsParameterReference (name))
979 return current.ToplevelBlockCaptureContext;
983 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
985 CaptureContext cc = _ContextForParameter (current, name);
987 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
992 // Records the captured parameter at the appropriate CaptureContext
994 public void AddParameter (EmitContext ec, AnonymousContainer am,
995 string name, Type t, int idx)
997 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
999 cc.AddParameterToContext (am, name, t, idx);
1003 // Records the parameters in the context
1005 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1007 if (captured_parameters == null)
1008 captured_parameters = new Hashtable ();
1009 if (captured_parameters [name] != null)
1011 captured_parameters [name] = new CapturedParameter (t, idx);
1013 if (topmost == null){
1015 // Create one ScopeInfo, if there are none.
1017 topmost = new ScopeInfo (this, toplevel_owner);
1018 scopes [toplevel_owner.ID] = topmost;
1021 // If the topmost ScopeInfo is not at the topblock level, insert
1022 // a new ScopeInfo there.
1024 // FIXME: This code probably should be evolved to be like the code
1027 if (topmost.ScopeBlock != toplevel_owner){
1028 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
1029 ScopeInfo old_top = topmost;
1030 scopes [toplevel_owner.ID] = topmost;
1031 topmost.ParentScope = par_si;
1033 topmost.AddChild (old_top);
1037 topmost.HostsParameters = true;
1038 AdjustMethodScope (am, topmost);
1042 // Captured fields are only recorded on the topmost CaptureContext, because that
1043 // one is the one linked to the owner of instance fields
1045 public void AddField (FieldExpr fe)
1047 if (fe.FieldInfo.IsStatic)
1048 throw new Exception ("Attempt to register a static field as a captured field");
1050 CaptureContext parent = ParentCaptureContext;
1052 parent.AddField (fe);
1054 captured_fields [fe] = fe;
1057 public void CaptureThis ()
1059 CaptureContext parent = ParentCaptureContext;
1061 parent.CaptureThis ();
1062 referenced_this = true;
1065 public bool HaveCapturedVariables {
1067 return have_captured_vars;
1071 public bool HaveCapturedFields {
1073 CaptureContext parent = ParentCaptureContext;
1075 return parent.HaveCapturedFields;
1076 return captured_fields.Count > 0;
1080 public bool IsCaptured (LocalInfo local)
1082 foreach (ScopeInfo si in scopes.Values){
1083 if (si.IsCaptured (local))
1090 // Returns whether the parameter is captured
1092 public bool IsParameterCaptured (string name)
1094 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1097 if (captured_parameters != null)
1098 return captured_parameters [name] != null;
1102 public void EmitAnonymousHelperClasses (EmitContext ec)
1104 if (topmost != null){
1105 topmost.NeedThis = HaveCapturedFields || referenced_this;
1106 topmost.EmitScopeType (ec);
1110 public void CloseAnonymousHelperClasses ()
1112 if (topmost != null)
1113 topmost.CloseTypes ();
1116 public void EmitInitScope (EmitContext ec)
1118 EmitAnonymousHelperClasses (ec);
1119 if (topmost != null)
1120 topmost.EmitInitScope (ec);
1123 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1127 si = (ScopeInfo) scopes [b.ID];
1129 throw new Exception ("Si is null for block " + b.ID);
1130 si.EmitInitScope (ec);
1136 // Emits the opcodes necessary to load the instance of the captured
1139 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1140 AnonymousContainer am)
1142 ILGenerator ig = ec.ig;
1145 if (li.Block.Toplevel == toplevel_owner){
1146 si = GetScopeFromBlock (ec, li.Block);
1147 si.EmitScopeInstance (ig);
1152 ig.Emit (OpCodes.Ldarg_0);
1154 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1158 while (si.ScopeBlock.ID != li.Block.ID){
1159 if (si.ParentLink != null)
1160 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1161 si = si.ParentScope;
1164 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1165 while (si.ScopeBlock.ID != li.Block.ID){
1166 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1167 si = si.ParentScope;
1170 throw new Exception (
1171 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1172 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1179 // Internal routine that loads the instance to reach parameter `name'
1181 void EmitParameterInstance (EmitContext ec, string name)
1183 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1185 cc.EmitParameterInstance (ec, name);
1189 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1190 if (par_info != null){
1192 // FIXME: implementing this.
1195 ILGenerator ig = ec.ig;
1199 if (ec.CurrentBlock.Toplevel == toplevel_owner){
1200 si = GetScopeFromBlock (ec, toplevel_owner);
1201 si.EmitScopeInstance (ig);
1205 si = ec.CurrentAnonymousMethod.Scope;
1206 ig.Emit (OpCodes.Ldarg_0);
1208 while (si.ParentLink != null) {
1209 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1210 si = si.ParentScope;
1216 // Emits the code necessary to load the parameter named `name' within
1217 // an anonymous method.
1219 public void EmitParameter (EmitContext ec, string name)
1221 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1223 cc.EmitParameter (ec, name);
1226 EmitParameterInstance (ec, name);
1227 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1228 if (par_info != null){
1230 // FIXME: implementing this.
1233 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1237 // Implements the assignment of `source' to the paramenter named `name' within
1238 // an anonymous method.
1240 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1242 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1244 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1247 ILGenerator ig = ec.ig;
1248 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1250 EmitParameterInstance (ec, name);
1253 ig.Emit (OpCodes.Dup);
1254 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1258 // Emits the address for the parameter named `name' within
1259 // an anonymous method.
1261 public void EmitAddressOfParameter (EmitContext ec, string name)
1263 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1265 cc.EmitAddressOfParameter (ec, name);
1268 EmitParameterInstance (ec, name);
1269 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1270 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1274 // The following methods are only invoked on the host for the
1275 // anonymous method.
1277 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1279 ILGenerator ig = target.ig;
1280 ScopeInfo si = am.Scope;
1283 ig.Emit (OpCodes.Ldarg_0);
1287 si.EmitInitScope (target);
1288 si.EmitScopeInstance (ig);
1291 ArrayList all_scopes = new ArrayList ();
1293 public void AddScope (ScopeInfo si)
1295 all_scopes.Add (si);
1296 toplevel_owner.RegisterCaptureContext (this);
1300 // Links any scopes that were not linked previously
1302 public void AdjustScopes ()
1304 foreach (ScopeInfo scope in all_scopes){
1305 if (scope.ParentScope != null)
1308 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1309 if (scopes [b.ID] != null){
1310 LinkScope (scope, b.ID);
1315 if (scope.ParentScope == null && ParentCaptureContext != null){
1316 CaptureContext pcc = ParentCaptureContext;
1318 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1319 if (pcc.scopes [b.ID] != null){
1320 pcc.LinkScope (scope, b.ID);