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 protected bool unreachable;
53 ScopeInfo method_scope;
54 bool computed_method_scope = false;
57 // The modifiers applied to the method, we aggregate them
59 protected int method_modifiers = Modifiers.PRIVATE;
62 // Track the scopes that this method has used. At the
63 // end this is used to determine the ScopeInfo that will
66 ArrayList scopes_used = new ArrayList ();
69 // Points to our container anonymous method if its present
71 public AnonymousContainer ContainerAnonymousMethod;
73 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
74 ToplevelBlock block, Location l)
76 Parameters = parameters;
81 // The order is important: this setups the CaptureContext tree hierarchy.
83 if (container == null) {
86 container.SetHaveAnonymousMethods (l, this);
87 block.SetHaveAnonymousMethods (l, this);
90 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
92 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
96 public override Expression DoResolve (EmitContext ec)
99 // Set class type, set type
102 eclass = ExprClass.Value;
105 // This hack means `The type is not accessible
106 // anywhere', we depend on special conversion
109 type = TypeManager.anonymous_method_type;
114 public void RegisterScope (ScopeInfo scope)
116 if (scopes_used.Contains (scope))
118 scopes_used.Add (scope);
121 // Returns the deepest of two scopes
122 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
134 // If they Scopes are on the same CaptureContext, we do the double
135 // checks just so if there is an invariant change in the future,
136 // we get the exception at the end
138 for (p = a; p != null; p = p.ParentScope)
142 for (p = b; p != null; p = p.ParentScope)
146 CaptureContext ca = a.CaptureContext;
147 CaptureContext cb = b.CaptureContext;
149 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
153 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
156 throw new Exception ("Should never be reached");
160 // Determines the proper host for a method considering the
161 // scopes it references
163 public void ComputeMethodHost ()
165 if (computed_method_scope)
169 int top = scopes_used.Count;
170 computed_method_scope = true;
175 method_scope = (ScopeInfo) scopes_used [0];
179 for (int i = 1; i < top; i++)
180 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
183 public ScopeInfo Scope {
185 if (computed_method_scope)
189 // This means that ComputeMethodHost is not being called, most
190 // likely by someone who overwrote the CreateMethodHost method
192 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
197 protected abstract bool CreateMethodHost (EmitContext ec);
199 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
201 public abstract bool IsIterator {
206 public class AnonymousMethod : AnonymousContainer
210 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
211 ToplevelBlock block, Location l)
212 : base (parameters, container, block, l)
217 public override bool IsIterator {
218 get { return false; }
221 public override void Emit (EmitContext ec)
223 // nothing, as we only exist to not do anything.
227 // Creates the host for the anonymous method
229 protected override bool CreateMethodHost (EmitContext ec)
231 ComputeMethodHost ();
234 // Crude hack follows: we replace the TypeBuilder during the
235 // definition to get the method hosted in the right class
237 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
238 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
240 if (current_type == null)
241 throw new Exception ("The current_type is null");
243 if (type_host == null)
244 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
246 if (current_type != type_host)
247 method_modifiers = Modifiers.INTERNAL;
249 if (current_type == type_host && ec.IsStatic){
250 method_modifiers |= Modifiers.STATIC;
254 method = new Method (
255 (TypeContainer) ec.TypeContainer,
256 new TypeExpression (invoke_mb.ReturnType, loc),
257 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
259 method.Block = Block;
262 // Swap the TypeBuilder while we define the method, then restore
264 if (current_type != null)
265 ec.TypeContainer.TypeBuilder = type_host;
266 bool res = method.Define ();
267 if (current_type != null)
268 ec.TypeContainer.TypeBuilder = current_type;
272 void Error_ParameterMismatch (Type t)
274 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
275 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
278 public bool ImplicitStandardConversionExists (Type delegate_type)
280 if (Parameters == null)
283 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
284 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
286 if (Parameters.Count != invoke_pd.Count)
289 for (int i = 0; i < Parameters.Count; ++i) {
290 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
297 // Returns true if this anonymous method can be implicitly
298 // converted to the delegate type `delegate_type'
300 public Expression Compatible (EmitContext ec, Type delegate_type)
303 // At this point its the first time we know the return type that is
304 // needed for the anonymous method. We create the method here.
307 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
308 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
310 if (Parameters == null) {
312 // We provide a set of inaccessible parameters
314 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
316 for (int i = 0; i < invoke_pd.Count; i++){
317 fixedpars [i] = new Parameter (
318 invoke_pd.ParameterType (i),
319 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
322 Parameters = new Parameters (fixedpars);
324 if (Parameters.Count != invoke_pd.Count) {
325 Report.SymbolRelatedToPreviousError (delegate_type);
326 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
327 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
328 Error_ParameterMismatch (delegate_type);
332 for (int i = 0; i < Parameters.Count; ++i) {
333 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
334 if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
335 if (p_mod == Parameter.Modifier.NONE)
336 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
337 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
339 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
340 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
341 Error_ParameterMismatch (delegate_type);
345 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
346 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
348 TypeManager.CSharpName (Parameters.ParameterType (i)),
349 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
350 Error_ParameterMismatch (delegate_type);
357 // Second: the return type of the delegate must be compatible with
358 // the anonymous type. Instead of doing a pass to examine the block
359 // we satisfy the rule by setting the return type on the EmitContext
360 // to be the delegate type return type.
363 //MethodBuilder builder = method_data.MethodBuilder;
364 //ILGenerator ig = builder.GetILGenerator ();
366 aec = new EmitContext (ec.ResolveContext,
367 ec.TypeContainer, ec.DeclContainer, loc, null,
368 invoke_mb.ReturnType,
369 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
370 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
371 (ec.IsStatic ? Modifiers.STATIC : 0),
372 /* No constructor */ false);
374 aec.CurrentAnonymousMethod = this;
375 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
376 ContainingBlock = ec.CurrentBlock;
378 if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable))
379 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
384 public override Expression DoResolve (EmitContext ec)
386 if (!ec.IsAnonymousMethodAllowed) {
387 Report.Error (1706, loc, "Anonymous methods are not allowed in the attribute declaration");
391 if (Parameters != null && !Parameters.Resolve (ec)) {
395 return base.DoResolve (ec);
399 public override string ExprClassName {
401 return "anonymous method";
405 public MethodBuilder GetMethodBuilder ()
407 return method.MethodBuilder;
410 public override string GetSignatureForError ()
412 string s = TypeManager.CSharpSignature (invoke_mb);
413 return s.Substring (0, s.IndexOf (".Invoke("));
416 public bool EmitMethod (EmitContext ec)
418 if (!CreateMethodHost (ec))
421 MethodBuilder builder = GetMethodBuilder ();
422 ILGenerator ig = builder.GetILGenerator ();
425 Parameters.ApplyAttributes (builder);
428 // Adjust based on the computed state of the
429 // method from CreateMethodHost
431 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
433 aec.EmitMeta (Block);
434 aec.EmitResolvedTopBlock (Block, unreachable);
438 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
440 TypeBuilder container = ec.TypeContainer.TypeBuilder;
441 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
443 scope.ScopeTypeBuilder = container.DefineNestedType (
444 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
445 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
447 Type [] constructor_types = Type.EmptyTypes;
448 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
449 MethodAttributes.Public | MethodAttributes.HideBySig |
450 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
451 CallingConventions.HasThis, constructor_types);
453 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
455 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
456 cig.Emit (OpCodes.Ldarg_0);
457 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
458 cig.Emit (OpCodes.Ret);
461 public static void Error_AddressOfCapturedVar (string name, Location loc)
463 Report.Error (1686, loc,
464 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
470 // This will emit the code for the delegate, as well delegate creation on the host
472 public class AnonymousDelegate : DelegateCreation {
475 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
482 public override Expression DoResolve (EmitContext ec)
484 eclass = ExprClass.Value;
489 public override void Emit (EmitContext ec)
491 if (!am.EmitMethod (ec))
495 // Now emit the delegate creation.
497 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
498 delegate_instance_expression = new AnonymousInstance (am);
500 Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", loc);
501 constructor_method = ((MethodGroupExpr) ml).Methods [0];
502 delegate_method = am.GetMethodBuilder ();
506 class AnonymousInstance : Expression {
509 public AnonymousInstance (AnonymousMethod am)
512 eclass = ExprClass.Value;
515 public override Expression DoResolve (EmitContext ec)
520 public override void Emit (EmitContext ec)
522 am.aec.EmitMethodHostInstance (ec, am);
527 class CapturedParameter {
529 public FieldBuilder FieldBuilder;
532 public CapturedParameter (Type type, int idx)
540 // Here we cluster all the variables captured on a given scope, we also
541 // keep some extra information that might be required on each scope.
543 public class ScopeInfo {
544 public CaptureContext CaptureContext;
545 public ScopeInfo ParentScope;
546 public Block ScopeBlock;
547 public bool NeedThis = false;
548 public bool HostsParameters = false;
550 // For tracking the number of scopes created.
555 ArrayList locals = new ArrayList ();
556 ArrayList children = new ArrayList ();
559 // The types and fields generated
561 public TypeBuilder ScopeTypeBuilder;
562 public ConstructorBuilder ScopeConstructor;
563 public FieldBuilder THIS;
564 public FieldBuilder ParentLink;
567 // Points to the object of type `ScopeTypeBuilder' that
568 // holds the data for the scope
570 LocalBuilder scope_instance;
572 public ScopeInfo (CaptureContext cc, Block b)
578 cc.RegisterCaptureContext ();
581 public void AddLocal (LocalInfo li)
583 if (locals.Contains (li))
589 public bool IsCaptured (LocalInfo li)
591 return locals.Contains (li);
594 internal void AddChild (ScopeInfo si)
596 if (children.Contains (si))
600 // If any of the current children should be a children of `si', move them there
602 ArrayList move_queue = null;
603 foreach (ScopeInfo child in children){
604 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
605 if (move_queue == null)
606 move_queue = new ArrayList ();
607 move_queue.Add (child);
608 child.ParentScope = si;
615 if (move_queue != null){
616 foreach (ScopeInfo child in move_queue){
617 children.Remove (child);
622 static int indent = 0;
626 for (int i = 0; i < indent; i++)
632 //Console.WriteLine (Environment.StackTrace);
634 Console.WriteLine ("START");
637 Console.WriteLine ("NeedThis=" + NeedThis);
638 foreach (LocalInfo li in locals){
640 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
643 foreach (ScopeInfo si in children)
647 Console.WriteLine ("END");
650 public string MakeHelperName ()
652 return String.Format ("<>AnonHelp<{0}>", id);
655 private string MakeFieldName (string local_name)
657 return "<" + id + ":" + local_name + ">";
660 public void EmitScopeType (EmitContext ec)
664 if (ScopeTypeBuilder != null)
667 TypeBuilder container = ec.TypeContainer.TypeBuilder;
669 CaptureContext.Host.CreateScopeType (ec, this);
672 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
674 if (ParentScope != null){
675 if (ParentScope.ScopeTypeBuilder == null){
676 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
679 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
680 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
681 FieldAttributes.Assembly);
684 if (NeedThis && ParentScope != null)
685 throw new Exception ("I was not expecting THIS && having a parent");
687 foreach (LocalInfo info in locals)
688 info.FieldBuilder = ScopeTypeBuilder.DefineField (
689 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
691 if (HostsParameters){
692 Hashtable captured_parameters = CaptureContext.captured_parameters;
694 foreach (DictionaryEntry de in captured_parameters){
695 string name = (string) de.Key;
696 CapturedParameter cp = (CapturedParameter) de.Value;
699 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
700 cp.FieldBuilder = fb;
704 foreach (ScopeInfo si in children){
705 si.EmitScopeType (ec);
709 public void CloseTypes ()
711 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
712 foreach (ScopeInfo si in children)
717 // Emits the initialization code for the scope
719 public void EmitInitScope (EmitContext ec)
721 ILGenerator ig = ec.ig;
726 if (ScopeConstructor == null)
727 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
729 if (!CaptureContext.Host.IsIterator) {
730 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
731 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
732 ig.Emit (OpCodes.Stloc, scope_instance);
736 if (CaptureContext.Host.IsIterator) {
737 ig.Emit (OpCodes.Ldarg_0);
738 ig.Emit (OpCodes.Ldarg_1);
740 ig.Emit (OpCodes.Ldloc, scope_instance);
741 ig.Emit (OpCodes.Ldarg_0);
743 ig.Emit (OpCodes.Stfld, THIS);
747 // Copy the parameter values, if any
749 int extra = ec.IsStatic ? 0 : 1;
750 if (CaptureContext.Host.IsIterator)
752 if (HostsParameters){
753 Hashtable captured_parameters = CaptureContext.captured_parameters;
755 foreach (DictionaryEntry de in captured_parameters){
756 CapturedParameter cp = (CapturedParameter) de.Value;
758 EmitScopeInstance (ig);
759 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
760 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
764 if (ParentScope != null){
765 if (!ParentScope.inited)
766 ParentScope.EmitInitScope (ec);
768 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
770 // Only emit initialization in our capturecontext world
772 if (ParentScope.CaptureContext == CaptureContext){
773 EmitScopeInstance (ig);
774 ParentScope.EmitScopeInstance (ig);
775 ig.Emit (OpCodes.Stfld, ParentLink);
777 EmitScopeInstance (ig);
778 ig.Emit (OpCodes.Ldarg_0);
779 ig.Emit (OpCodes.Stfld, ParentLink);
786 public void EmitScopeInstance (ILGenerator ig)
788 if (CaptureContext.Host.IsIterator)
789 ig.Emit (OpCodes.Ldarg_0);
791 if (scope_instance == null){
793 // This is needed if someone overwrites the Emit method
794 // of Statement and manually calls Block.Emit without
795 // this snippet first:
797 // ec.EmitScopeInitFromBlock (The_Block);
798 // The_Block.Emit (ec);
802 "The scope_instance has not been emitted, this typically means\n" +
803 "that inside the compiler someone is calling Block.Emit without\n" +
804 "first calling EmitScopeInitFromBlock for the block. See compiler" +
805 "source code for an explanation");
806 throw new Exception ("Internal compiler error");
809 ig.Emit (OpCodes.Ldloc, scope_instance);
813 public static void CheckCycles (string msg, ScopeInfo s)
815 ArrayList l = new ArrayList ();
818 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
820 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
821 throw new Exception ();
827 static void DoPath (StringBuilder sb, ScopeInfo start)
829 CheckCycles ("print", start);
831 if (start.ParentScope != null){
832 DoPath (sb, start.ParentScope);
835 sb.Append ((start.id).ToString ());
838 public override string ToString ()
840 StringBuilder sb = new StringBuilder ();
843 if (CaptureContext != null){
844 sb.Append (CaptureContext.ToString ());
851 return sb.ToString ();
856 // CaptureContext objects are created on demand if a method has
857 // anonymous methods and kept on the ToplevelBlock.
859 // If they exist, all ToplevelBlocks in the containing block are
860 // linked together (children pointing to their parents).
862 public class CaptureContext {
863 public static int count;
868 // Points to the toplevel block that owns this CaptureContext
870 ToplevelBlock toplevel_owner;
873 // All the scopes we capture
875 Hashtable scopes = new Hashtable ();
878 // All the root scopes
880 ArrayList roots = new ArrayList ();
882 bool have_captured_vars = false;
883 bool referenced_this = false;
888 Hashtable captured_fields = new Hashtable ();
889 Hashtable captured_variables = new Hashtable ();
890 public Hashtable captured_parameters = new Hashtable ();
891 public AnonymousContainer Host;
893 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
894 AnonymousContainer host)
897 this.toplevel_owner = toplevel_owner;
904 void DoPath (StringBuilder sb, CaptureContext cc)
906 if (cc.ParentCaptureContext != null){
907 DoPath (sb, cc.ParentCaptureContext);
910 sb.Append (cc.cc_id.ToString ());
913 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
915 toplevel_owner = new_toplevel;
918 for (CaptureContext cc = ParentCaptureContext; cc != null;
919 cc = cc.ParentCaptureContext) {
924 public override string ToString ()
926 StringBuilder sb = new StringBuilder ();
930 return sb.ToString ();
933 public ToplevelBlock ParentToplevel {
935 return toplevel_owner.Container;
939 public CaptureContext ParentCaptureContext {
941 ToplevelBlock parent = ParentToplevel;
943 return (parent == null) ? null : parent.CaptureContext;
947 ScopeInfo GetScopeForBlock (Block block)
949 ScopeInfo si = (ScopeInfo) scopes [block.ID];
952 si = new ScopeInfo (this, block);
953 scopes [block.ID] = si;
957 public void AddLocal (AnonymousContainer am, LocalInfo li)
959 if (li.Block.Toplevel != toplevel_owner){
960 ParentCaptureContext.AddLocal (am, li);
963 ScopeInfo scope = GetScopeForBlock (li.Block);
969 Host.RegisterScope (scope);
974 am.RegisterScope (scope);
976 if (captured_variables [li] != null)
979 have_captured_vars = true;
980 captured_variables [li] = li;
985 // Retursn the CaptureContext for the block that defines the parameter `name'
987 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
989 ToplevelBlock container = current.Container;
990 if (container != null){
991 CaptureContext cc = _ContextForParameter (container, name);
995 if (current.IsParameterReference (name))
996 return current.ToplevelBlockCaptureContext;
1000 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1002 CaptureContext cc = _ContextForParameter (current, name);
1004 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1009 // Records the captured parameter at the appropriate CaptureContext
1011 public void AddParameter (EmitContext ec, AnonymousContainer am,
1012 string name, Type t, int idx)
1014 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1016 cc.AddParameterToContext (am, name, t, idx);
1020 // Records the parameters in the context
1022 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1024 if (captured_parameters == null)
1025 captured_parameters = new Hashtable ();
1026 if (captured_parameters [name] == null)
1027 captured_parameters [name] = new CapturedParameter (t, idx);
1029 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1030 scope.HostsParameters = true;
1031 am.RegisterScope (scope);
1035 // Captured fields are only recorded on the topmost CaptureContext, because that
1036 // one is the one linked to the owner of instance fields
1038 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1040 if (fe.FieldInfo.IsStatic)
1041 throw new Exception ("Attempt to register a static field as a captured field");
1042 CaptureContext parent = ParentCaptureContext;
1043 if (parent != null) {
1044 parent.AddField (ec, am, fe);
1048 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1049 am.RegisterScope (scope);
1052 public void CaptureThis (AnonymousContainer am)
1055 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1056 CaptureContext parent = ParentCaptureContext;
1057 if (parent != null) {
1058 parent.CaptureThis (am);
1061 referenced_this = true;
1063 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1064 am.RegisterScope (scope);
1067 public bool HaveCapturedVariables {
1069 return have_captured_vars;
1073 public bool HaveCapturedFields {
1075 CaptureContext parent = ParentCaptureContext;
1077 return parent.HaveCapturedFields;
1078 return captured_fields.Count > 0;
1082 public bool IsCaptured (LocalInfo local)
1084 foreach (ScopeInfo si in scopes.Values){
1085 if (si.IsCaptured (local))
1092 // Returns whether the parameter is captured
1094 public bool IsParameterCaptured (string name)
1096 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1099 if (captured_parameters != null)
1100 return captured_parameters [name] != null;
1104 public void EmitAnonymousHelperClasses (EmitContext ec)
1106 if (roots.Count != 0){
1107 foreach (ScopeInfo root in roots){
1109 // FIXME: We really should do this in a per-ScopeInfo
1110 // basis, instead of having the NeedThis applied to
1111 // all of the roots.
1113 root.NeedThis = HaveCapturedFields || referenced_this;
1115 root.EmitScopeType (ec);
1120 public void CloseAnonymousHelperClasses ()
1122 if (roots.Count != 0)
1123 foreach (ScopeInfo root in roots)
1127 public void EmitInitScope (EmitContext ec)
1129 EmitAnonymousHelperClasses (ec);
1130 if (roots.Count != 0)
1131 foreach (ScopeInfo root in roots)
1132 root.EmitInitScope (ec); }
1135 // This is called externally when we start emitting code for a block
1136 // if the block has a ScopeInfo associated, emit the init code
1138 public void EmitScopeInitFromBlock (EmitContext ec, Block b)
1140 ScopeInfo si = (ScopeInfo) scopes [b.ID];
1144 si.EmitInitScope (ec);
1148 // Emits the opcodes necessary to load the instance of the captured
1151 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1152 AnonymousContainer am)
1154 ILGenerator ig = ec.ig;
1157 if (li.Block.Toplevel == toplevel_owner){
1158 si = (ScopeInfo) scopes [li.Block.ID];
1159 si.EmitScopeInstance (ig);
1164 ig.Emit (OpCodes.Ldarg_0);
1166 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1170 while (si.ScopeBlock.ID != li.Block.ID){
1171 if (si.ParentLink != null)
1172 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1173 si = si.ParentScope;
1176 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1177 while (si.ScopeBlock.ID != li.Block.ID){
1178 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1179 si = si.ParentScope;
1182 throw new Exception (
1183 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1184 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1191 // Internal routine that loads the instance to reach parameter `name'
1193 void EmitParameterInstance (EmitContext ec, string name)
1195 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1197 cc.EmitParameterInstance (ec, name);
1201 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1202 if (par_info != null){
1204 // FIXME: implementing this.
1207 ILGenerator ig = ec.ig;
1211 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1212 si = (ScopeInfo) scopes [toplevel_owner.ID];
1213 si.EmitScopeInstance (ig);
1215 si = ec.CurrentAnonymousMethod.Scope;
1216 ig.Emit (OpCodes.Ldarg_0);
1220 while (si.ParentLink != null) {
1221 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1222 si = si.ParentScope;
1228 // Emits the code necessary to load the parameter named `name' within
1229 // an anonymous method.
1231 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1233 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1235 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1239 EmitParameterInstance (ec, name);
1240 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1241 if (par_info != null){
1243 // FIXME: implementing this.
1246 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1249 ec.ig.Emit (OpCodes.Dup);
1250 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1256 // Implements the assignment of `source' to the paramenter named `name' within
1257 // an anonymous method.
1259 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1261 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1263 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1266 ILGenerator ig = ec.ig;
1267 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1269 EmitParameterInstance (ec, name);
1270 if (prepare_for_load)
1271 ig.Emit (OpCodes.Dup);
1274 ig.Emit (OpCodes.Dup);
1275 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1278 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1284 // Emits the address for the parameter named `name' within
1285 // an anonymous method.
1287 public void EmitAddressOfParameter (EmitContext ec, string name)
1289 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1291 cc.EmitAddressOfParameter (ec, name);
1294 EmitParameterInstance (ec, name);
1295 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1296 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1300 // The following methods are only invoked on the host for the
1301 // anonymous method.
1303 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1305 ILGenerator ig = target.ig;
1306 ScopeInfo si = am.Scope;
1308 AnonymousContainer container = am.ContainerAnonymousMethod;
1310 if ((si == null) || ((container != null) && (si == container.Scope))) {
1311 ig.Emit (OpCodes.Ldarg_0);
1315 si.EmitInitScope (target);
1316 si.EmitScopeInstance (ig);
1319 public void RegisterCaptureContext ()
1321 toplevel_owner.RegisterCaptureContext (this);
1325 // Returs true if `probe' is an ancestor of `scope' in the
1328 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1330 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1331 if (probe.ScopeBlock == b)
1338 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1339 // of `scope' found in `scope_list'.
1341 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1343 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1345 object ancestors = null;
1347 for (int i = 0; i < scope_list.Length; i++){
1348 // Ignore the same scope
1349 if (scope_list [i] == scope)
1352 if (IsAncestor (scope_list [i], scope)){
1353 if (ancestors == null){
1354 ancestors = scope_list [i];
1358 if (ancestors is ScopeInfo){
1359 object old = ancestors;
1360 ancestors = new ArrayList (4);
1361 ((ArrayList)ancestors).Add (old);
1364 ((ArrayList)ancestors).Add (scope_list [i]);
1371 // Returns the immediate parent of `scope' from all the captured
1372 // scopes found in `scope_list', or null if this is a toplevel scope.
1374 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1376 object ancestors = GetAncestorScopes (scope, scope_list);
1377 if (ancestors == null)
1380 // Single match, thats the parent.
1381 if (ancestors is ScopeInfo)
1382 return (ScopeInfo) ancestors;
1384 ArrayList candidates = (ArrayList) ancestors;
1385 ScopeInfo parent = (ScopeInfo) candidates [0];
1386 for (int i = 1; i < candidates.Count; i++){
1387 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1388 parent = (ScopeInfo) candidates [i];
1394 // Links all the scopes
1397 public void LinkScopes ()
1403 if (ParentCaptureContext != null)
1404 ParentCaptureContext.LinkScopes ();
1406 int scope_count = scopes.Keys.Count;
1407 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1408 scopes.Values.CopyTo (scope_list, 0);
1410 for (int i = 0; i < scope_count; i++){
1411 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1413 if (parent == null){
1414 roots.Add (scope_list [i]);
1418 scope_list [i].ParentScope = parent;
1419 parent.AddChild (scope_list [i]);
1423 // Link the roots to their parent containers if any.
1425 if (ParentCaptureContext != null && roots.Count != 0){
1426 ScopeInfo one_root = (ScopeInfo) roots [0];
1429 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1430 if (!IsAncestor (a_parent_root, one_root))
1435 // Found, link all the roots to this root
1436 foreach (ScopeInfo root in roots){
1437 root.ParentScope = a_parent_root;
1438 a_parent_root.AddChild (root);
1444 // This is to catch a condition in which it is
1445 // not possible to determine the containing ScopeInfo
1446 // from an encapsulating CaptureContext
1448 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");