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) {
84 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
87 container.SetHaveAnonymousMethods (l, this);
88 block.SetHaveAnonymousMethods (l, this);
91 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
93 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
97 public override Expression DoResolve (EmitContext ec)
100 // Set class type, set type
103 eclass = ExprClass.Value;
106 // This hack means `The type is not accessible
107 // anywhere', we depend on special conversion
110 type = TypeManager.anonymous_method_type;
115 public void RegisterScope (ScopeInfo scope)
117 if (scopes_used.Contains (scope))
119 scopes_used.Add (scope);
122 // Returns the deepest of two scopes
123 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
135 // If they Scopes are on the same CaptureContext, we do the double
136 // checks just so if there is an invariant change in the future,
137 // we get the exception at the end
139 for (p = a; p != null; p = p.ParentScope)
143 for (p = b; p != null; p = p.ParentScope)
147 CaptureContext ca = a.CaptureContext;
148 CaptureContext cb = b.CaptureContext;
150 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
154 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
157 throw new Exception ("Should never be reached");
161 // Determines the proper host for a method considering the
162 // scopes it references
164 public void ComputeMethodHost ()
166 if (computed_method_scope)
170 int top = scopes_used.Count;
171 computed_method_scope = true;
176 method_scope = (ScopeInfo) scopes_used [0];
180 for (int i = 1; i < top; i++)
181 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
184 public ScopeInfo Scope {
186 if (computed_method_scope)
190 // This means that ComputeMethodHost is not being called, most
191 // likely by someone who overwrote the CreateMethodHost method
193 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
198 protected abstract bool CreateMethodHost (EmitContext ec);
200 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
202 public abstract bool IsIterator {
207 public class AnonymousMethod : AnonymousContainer
211 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
212 ToplevelBlock block, Location l)
213 : base (parameters, container, block, l)
218 public override bool IsIterator {
219 get { return false; }
222 public override void Emit (EmitContext ec)
224 // nothing, as we only exist to not do anything.
228 // Creates the host for the anonymous method
230 protected override bool CreateMethodHost (EmitContext ec)
232 ComputeMethodHost ();
235 // Crude hack follows: we replace the TypeBuilder during the
236 // definition to get the method hosted in the right class
238 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
239 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
241 if (current_type == null)
242 throw new Exception ("The current_type is null");
244 if (type_host == null)
245 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
247 if (current_type != type_host)
248 method_modifiers = Modifiers.INTERNAL;
250 if (current_type == type_host && ec.IsStatic){
251 method_modifiers |= Modifiers.STATIC;
255 method = new Method (
256 (TypeContainer) ec.TypeContainer,
257 new TypeExpression (invoke_mb.ReturnType, loc),
258 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
260 method.Block = Block;
263 // Swap the TypeBuilder while we define the method, then restore
265 if (current_type != null)
266 ec.TypeContainer.TypeBuilder = type_host;
267 bool res = method.Define ();
268 if (current_type != null)
269 ec.TypeContainer.TypeBuilder = current_type;
273 void Error_ParameterMismatch (Type t)
275 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
276 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
279 public bool ImplicitStandardConversionExists (Type delegate_type)
281 if (Parameters == null)
284 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
285 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
287 if (Parameters.Count != invoke_pd.Count)
290 for (int i = 0; i < Parameters.Count; ++i) {
291 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
298 // Returns true if this anonymous method can be implicitly
299 // converted to the delegate type `delegate_type'
301 public Expression Compatible (EmitContext ec, Type delegate_type)
304 // At this point its the first time we know the return type that is
305 // needed for the anonymous method. We create the method here.
308 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
309 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
311 if (Parameters == null) {
313 // We provide a set of inaccessible parameters
315 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
317 for (int i = 0; i < invoke_pd.Count; i++){
318 fixedpars [i] = new Parameter (
319 invoke_pd.ParameterType (i),
320 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
323 Parameters = new Parameters (fixedpars);
325 if (Parameters.Count != invoke_pd.Count) {
326 Report.SymbolRelatedToPreviousError (delegate_type);
327 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
328 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
329 Error_ParameterMismatch (delegate_type);
333 for (int i = 0; i < Parameters.Count; ++i) {
334 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
335 if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
336 if (p_mod == Parameter.Modifier.NONE)
337 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
338 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
340 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
341 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
342 Error_ParameterMismatch (delegate_type);
346 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
347 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
349 TypeManager.CSharpName (Parameters.ParameterType (i)),
350 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
351 Error_ParameterMismatch (delegate_type);
358 // Second: the return type of the delegate must be compatible with
359 // the anonymous type. Instead of doing a pass to examine the block
360 // we satisfy the rule by setting the return type on the EmitContext
361 // to be the delegate type return type.
364 //MethodBuilder builder = method_data.MethodBuilder;
365 //ILGenerator ig = builder.GetILGenerator ();
367 aec = new EmitContext (ec.ResolveContext,
368 ec.TypeContainer, ec.DeclContainer, loc, null,
369 invoke_mb.ReturnType,
370 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
371 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
372 (ec.IsStatic ? Modifiers.STATIC : 0),
373 /* No constructor */ false);
375 aec.CurrentAnonymousMethod = this;
376 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
377 ContainingBlock = ec.CurrentBlock;
379 if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable))
380 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
385 public override Expression DoResolve (EmitContext ec)
387 if (Parameters != null && !Parameters.Resolve (ec)) {
391 return base.DoResolve (ec);
395 public override string ExprClassName {
397 return "anonymous method";
401 public MethodBuilder GetMethodBuilder ()
403 return method.MethodData.MethodBuilder;
406 public override string GetSignatureForError ()
408 string s = TypeManager.CSharpSignature (invoke_mb);
409 return s.Substring (0, s.IndexOf (".Invoke("));
412 public bool EmitMethod (EmitContext ec)
414 if (!CreateMethodHost (ec))
417 MethodBuilder builder = GetMethodBuilder ();
418 ILGenerator ig = builder.GetILGenerator ();
421 Parameters.ApplyAttributes (builder);
424 // Adjust based on the computed state of the
425 // method from CreateMethodHost
427 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
429 aec.EmitMeta (Block);
430 aec.EmitResolvedTopBlock (Block, unreachable);
434 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
436 TypeBuilder container = ec.TypeContainer.TypeBuilder;
437 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
439 scope.ScopeTypeBuilder = container.DefineNestedType (
440 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
441 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
443 Type [] constructor_types = Type.EmptyTypes;
444 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
445 MethodAttributes.Public | MethodAttributes.HideBySig |
446 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
447 CallingConventions.HasThis, constructor_types);
449 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
451 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
452 cig.Emit (OpCodes.Ldarg_0);
453 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
454 cig.Emit (OpCodes.Ret);
457 public static void Error_AddressOfCapturedVar (string name, Location loc)
459 Report.Error (1686, loc,
460 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
466 // This will emit the code for the delegate, as well delegate creation on the host
468 public class AnonymousDelegate : DelegateCreation {
471 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
478 public override Expression DoResolve (EmitContext ec)
480 eclass = ExprClass.Value;
485 public override void Emit (EmitContext ec)
487 if (!am.EmitMethod (ec))
491 // Now emit the delegate creation.
493 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
494 delegate_instance_expression = new AnonymousInstance (am);
496 Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", loc);
497 constructor_method = ((MethodGroupExpr) ml).Methods [0];
498 delegate_method = am.GetMethodBuilder ();
502 class AnonymousInstance : Expression {
505 public AnonymousInstance (AnonymousMethod am)
508 eclass = ExprClass.Value;
511 public override Expression DoResolve (EmitContext ec)
516 public override void Emit (EmitContext ec)
518 am.aec.EmitMethodHostInstance (ec, am);
523 class CapturedParameter {
525 public FieldBuilder FieldBuilder;
528 public CapturedParameter (Type type, int idx)
536 // Here we cluster all the variables captured on a given scope, we also
537 // keep some extra information that might be required on each scope.
539 public class ScopeInfo {
540 public CaptureContext CaptureContext;
541 public ScopeInfo ParentScope;
542 public Block ScopeBlock;
543 public bool NeedThis = false;
544 public bool HostsParameters = false;
546 // For tracking the number of scopes created.
551 ArrayList locals = new ArrayList ();
552 ArrayList children = new ArrayList ();
555 // The types and fields generated
557 public TypeBuilder ScopeTypeBuilder;
558 public ConstructorBuilder ScopeConstructor;
559 public FieldBuilder THIS;
560 public FieldBuilder ParentLink;
563 // Points to the object of type `ScopeTypeBuilder' that
564 // holds the data for the scope
566 LocalBuilder scope_instance;
568 public ScopeInfo (CaptureContext cc, Block b)
574 cc.RegisterCaptureContext ();
577 public void AddLocal (LocalInfo li)
579 if (locals.Contains (li))
585 public bool IsCaptured (LocalInfo li)
587 return locals.Contains (li);
590 internal void AddChild (ScopeInfo si)
592 if (children.Contains (si))
596 // If any of the current children should be a children of `si', move them there
598 ArrayList move_queue = null;
599 foreach (ScopeInfo child in children){
600 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
601 if (move_queue == null)
602 move_queue = new ArrayList ();
603 move_queue.Add (child);
604 child.ParentScope = si;
611 if (move_queue != null){
612 foreach (ScopeInfo child in move_queue){
613 children.Remove (child);
618 static int indent = 0;
622 for (int i = 0; i < indent; i++)
628 //Console.WriteLine (Environment.StackTrace);
630 Console.WriteLine ("START");
633 Console.WriteLine ("NeedThis=" + NeedThis);
634 foreach (LocalInfo li in locals){
636 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
639 foreach (ScopeInfo si in children)
643 Console.WriteLine ("END");
646 public string MakeHelperName ()
648 return String.Format ("<>AnonHelp<{0}>", id);
651 private string MakeFieldName (string local_name)
653 return "<" + id + ":" + local_name + ">";
656 public void EmitScopeType (EmitContext ec)
660 if (ScopeTypeBuilder != null)
663 TypeBuilder container = ec.TypeContainer.TypeBuilder;
665 CaptureContext.Host.CreateScopeType (ec, this);
668 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
670 if (ParentScope != null){
671 if (ParentScope.ScopeTypeBuilder == null){
672 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
675 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
676 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
677 FieldAttributes.Assembly);
680 if (NeedThis && ParentScope != null)
681 throw new Exception ("I was not expecting THIS && having a parent");
683 foreach (LocalInfo info in locals)
684 info.FieldBuilder = ScopeTypeBuilder.DefineField (
685 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
687 if (HostsParameters){
688 Hashtable captured_parameters = CaptureContext.captured_parameters;
690 foreach (DictionaryEntry de in captured_parameters){
691 string name = (string) de.Key;
692 CapturedParameter cp = (CapturedParameter) de.Value;
695 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
696 cp.FieldBuilder = fb;
700 foreach (ScopeInfo si in children){
701 si.EmitScopeType (ec);
705 public void CloseTypes ()
707 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
708 foreach (ScopeInfo si in children)
713 // Emits the initialization code for the scope
715 public void EmitInitScope (EmitContext ec)
717 ILGenerator ig = ec.ig;
722 if (ScopeConstructor == null)
723 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
725 if (!CaptureContext.Host.IsIterator) {
726 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
727 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
728 ig.Emit (OpCodes.Stloc, scope_instance);
732 if (CaptureContext.Host.IsIterator) {
733 ig.Emit (OpCodes.Ldarg_0);
734 ig.Emit (OpCodes.Ldarg_1);
736 ig.Emit (OpCodes.Ldloc, scope_instance);
737 ig.Emit (OpCodes.Ldarg_0);
739 ig.Emit (OpCodes.Stfld, THIS);
743 // Copy the parameter values, if any
745 int extra = ec.IsStatic ? 0 : 1;
746 if (CaptureContext.Host.IsIterator)
748 if (HostsParameters){
749 Hashtable captured_parameters = CaptureContext.captured_parameters;
751 foreach (DictionaryEntry de in captured_parameters){
752 CapturedParameter cp = (CapturedParameter) de.Value;
754 EmitScopeInstance (ig);
755 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
756 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
760 if (ParentScope != null){
761 if (!ParentScope.inited)
762 ParentScope.EmitInitScope (ec);
764 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
766 // Only emit initialization in our capturecontext world
768 if (ParentScope.CaptureContext == CaptureContext){
769 EmitScopeInstance (ig);
770 ParentScope.EmitScopeInstance (ig);
771 ig.Emit (OpCodes.Stfld, ParentLink);
773 EmitScopeInstance (ig);
774 ig.Emit (OpCodes.Ldarg_0);
775 ig.Emit (OpCodes.Stfld, ParentLink);
782 public void EmitScopeInstance (ILGenerator ig)
784 if (CaptureContext.Host.IsIterator)
785 ig.Emit (OpCodes.Ldarg_0);
787 if (scope_instance == null){
789 // This is needed if someone overwrites the Emit method
790 // of Statement and manually calls Block.Emit without
791 // this snippet first:
793 // ec.EmitScopeInitFromBlock (The_Block);
794 // The_Block.Emit (ec);
798 "The scope_instance has not been emitted, this typically means\n" +
799 "that inside the compiler someone is calling Block.Emit without\n" +
800 "first calling EmitScopeInitFromBlock for the block. See compiler" +
801 "source code for an explanation");
802 throw new Exception ("Internal compiler error");
805 ig.Emit (OpCodes.Ldloc, scope_instance);
809 public static void CheckCycles (string msg, ScopeInfo s)
811 ArrayList l = new ArrayList ();
814 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
816 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
817 throw new Exception ();
823 static void DoPath (StringBuilder sb, ScopeInfo start)
825 CheckCycles ("print", start);
827 if (start.ParentScope != null){
828 DoPath (sb, start.ParentScope);
831 sb.Append ((start.id).ToString ());
834 public override string ToString ()
836 StringBuilder sb = new StringBuilder ();
839 if (CaptureContext != null){
840 sb.Append (CaptureContext.ToString ());
847 return sb.ToString ();
852 // CaptureContext objects are created on demand if a method has
853 // anonymous methods and kept on the ToplevelBlock.
855 // If they exist, all ToplevelBlocks in the containing block are
856 // linked together (children pointing to their parents).
858 public class CaptureContext {
859 public static int count;
864 // Points to the toplevel block that owns this CaptureContext
866 ToplevelBlock toplevel_owner;
869 // All the scopes we capture
871 Hashtable scopes = new Hashtable ();
874 // All the root scopes
876 ArrayList roots = new ArrayList ();
878 bool have_captured_vars = false;
879 bool referenced_this = false;
884 Hashtable captured_fields = new Hashtable ();
885 Hashtable captured_variables = new Hashtable ();
886 public Hashtable captured_parameters = new Hashtable ();
887 public AnonymousContainer Host;
889 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
890 AnonymousContainer host)
893 this.toplevel_owner = toplevel_owner;
900 void DoPath (StringBuilder sb, CaptureContext cc)
902 if (cc.ParentCaptureContext != null){
903 DoPath (sb, cc.ParentCaptureContext);
906 sb.Append (cc.cc_id.ToString ());
909 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
911 toplevel_owner = new_toplevel;
914 for (CaptureContext cc = ParentCaptureContext; cc != null;
915 cc = cc.ParentCaptureContext) {
920 public override string ToString ()
922 StringBuilder sb = new StringBuilder ();
926 return sb.ToString ();
929 public ToplevelBlock ParentToplevel {
931 return toplevel_owner.Container;
935 public CaptureContext ParentCaptureContext {
937 ToplevelBlock parent = ParentToplevel;
939 return (parent == null) ? null : parent.CaptureContext;
943 ScopeInfo GetScopeForBlock (Block block)
945 ScopeInfo si = (ScopeInfo) scopes [block.ID];
948 si = new ScopeInfo (this, block);
949 scopes [block.ID] = si;
953 public void AddLocal (AnonymousContainer am, LocalInfo li)
955 if (li.Block.Toplevel != toplevel_owner){
956 ParentCaptureContext.AddLocal (am, li);
959 ScopeInfo scope = GetScopeForBlock (li.Block);
965 Host.RegisterScope (scope);
970 am.RegisterScope (scope);
972 if (captured_variables [li] != null)
975 have_captured_vars = true;
976 captured_variables [li] = li;
981 // Retursn the CaptureContext for the block that defines the parameter `name'
983 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
985 ToplevelBlock container = current.Container;
986 if (container != null){
987 CaptureContext cc = _ContextForParameter (container, name);
991 if (current.IsParameterReference (name))
992 return current.ToplevelBlockCaptureContext;
996 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
998 CaptureContext cc = _ContextForParameter (current, name);
1000 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1005 // Records the captured parameter at the appropriate CaptureContext
1007 public void AddParameter (EmitContext ec, AnonymousContainer am,
1008 string name, Type t, int idx)
1010 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1012 cc.AddParameterToContext (am, name, t, idx);
1016 // Records the parameters in the context
1018 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1020 if (captured_parameters == null)
1021 captured_parameters = new Hashtable ();
1022 if (captured_parameters [name] == null)
1023 captured_parameters [name] = new CapturedParameter (t, idx);
1025 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1026 scope.HostsParameters = true;
1027 am.RegisterScope (scope);
1031 // Captured fields are only recorded on the topmost CaptureContext, because that
1032 // one is the one linked to the owner of instance fields
1034 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1036 if (fe.FieldInfo.IsStatic)
1037 throw new Exception ("Attempt to register a static field as a captured field");
1038 CaptureContext parent = ParentCaptureContext;
1039 if (parent != null) {
1040 parent.AddField (ec, am, fe);
1044 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1045 am.RegisterScope (scope);
1048 public void CaptureThis (AnonymousContainer am)
1051 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1052 CaptureContext parent = ParentCaptureContext;
1053 if (parent != null) {
1054 parent.CaptureThis (am);
1057 referenced_this = true;
1059 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1060 am.RegisterScope (scope);
1063 public bool HaveCapturedVariables {
1065 return have_captured_vars;
1069 public bool HaveCapturedFields {
1071 CaptureContext parent = ParentCaptureContext;
1073 return parent.HaveCapturedFields;
1074 return captured_fields.Count > 0;
1078 public bool IsCaptured (LocalInfo local)
1080 foreach (ScopeInfo si in scopes.Values){
1081 if (si.IsCaptured (local))
1088 // Returns whether the parameter is captured
1090 public bool IsParameterCaptured (string name)
1092 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1095 if (captured_parameters != null)
1096 return captured_parameters [name] != null;
1100 public void EmitAnonymousHelperClasses (EmitContext ec)
1102 if (roots.Count != 0){
1103 foreach (ScopeInfo root in roots){
1105 // FIXME: We really should do this in a per-ScopeInfo
1106 // basis, instead of having the NeedThis applied to
1107 // all of the roots.
1109 root.NeedThis = HaveCapturedFields || referenced_this;
1111 root.EmitScopeType (ec);
1116 public void CloseAnonymousHelperClasses ()
1118 if (roots.Count != 0)
1119 foreach (ScopeInfo root in roots)
1123 public void EmitInitScope (EmitContext ec)
1125 EmitAnonymousHelperClasses (ec);
1126 if (roots.Count != 0)
1127 foreach (ScopeInfo root in roots)
1128 root.EmitInitScope (ec); }
1131 // This is called externally when we start emitting code for a block
1132 // if the block has a ScopeInfo associated, emit the init code
1134 public void EmitScopeInitFromBlock (EmitContext ec, Block b)
1136 ScopeInfo si = (ScopeInfo) scopes [b.ID];
1140 si.EmitInitScope (ec);
1144 // Emits the opcodes necessary to load the instance of the captured
1147 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1148 AnonymousContainer am)
1150 ILGenerator ig = ec.ig;
1153 if (li.Block.Toplevel == toplevel_owner){
1154 si = (ScopeInfo) scopes [li.Block.ID];
1155 si.EmitScopeInstance (ig);
1160 ig.Emit (OpCodes.Ldarg_0);
1162 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1166 while (si.ScopeBlock.ID != li.Block.ID){
1167 if (si.ParentLink != null)
1168 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1169 si = si.ParentScope;
1172 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1173 while (si.ScopeBlock.ID != li.Block.ID){
1174 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1175 si = si.ParentScope;
1178 throw new Exception (
1179 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1180 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1187 // Internal routine that loads the instance to reach parameter `name'
1189 void EmitParameterInstance (EmitContext ec, string name)
1191 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1193 cc.EmitParameterInstance (ec, name);
1197 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1198 if (par_info != null){
1200 // FIXME: implementing this.
1203 ILGenerator ig = ec.ig;
1207 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1208 si = (ScopeInfo) scopes [toplevel_owner.ID];
1209 si.EmitScopeInstance (ig);
1211 si = ec.CurrentAnonymousMethod.Scope;
1212 ig.Emit (OpCodes.Ldarg_0);
1216 while (si.ParentLink != null) {
1217 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1218 si = si.ParentScope;
1224 // Emits the code necessary to load the parameter named `name' within
1225 // an anonymous method.
1227 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1229 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1231 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1235 EmitParameterInstance (ec, name);
1236 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1237 if (par_info != null){
1239 // FIXME: implementing this.
1242 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1245 ec.ig.Emit (OpCodes.Dup);
1246 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1252 // Implements the assignment of `source' to the paramenter named `name' within
1253 // an anonymous method.
1255 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1257 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1259 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1262 ILGenerator ig = ec.ig;
1263 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1265 EmitParameterInstance (ec, name);
1266 if (prepare_for_load)
1267 ig.Emit (OpCodes.Dup);
1270 ig.Emit (OpCodes.Dup);
1271 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1274 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1280 // Emits the address for the parameter named `name' within
1281 // an anonymous method.
1283 public void EmitAddressOfParameter (EmitContext ec, string name)
1285 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1287 cc.EmitAddressOfParameter (ec, name);
1290 EmitParameterInstance (ec, name);
1291 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1292 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1296 // The following methods are only invoked on the host for the
1297 // anonymous method.
1299 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1301 ILGenerator ig = target.ig;
1302 ScopeInfo si = am.Scope;
1304 AnonymousContainer container = am.ContainerAnonymousMethod;
1306 if ((si == null) || ((container != null) && (si == container.Scope))) {
1307 ig.Emit (OpCodes.Ldarg_0);
1311 si.EmitInitScope (target);
1312 si.EmitScopeInstance (ig);
1315 public void RegisterCaptureContext ()
1317 toplevel_owner.RegisterCaptureContext (this);
1321 // Returs true if `probe' is an ancestor of `scope' in the
1324 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1326 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1327 if (probe.ScopeBlock == b)
1334 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1335 // of `scope' found in `scope_list'.
1337 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1339 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1341 object ancestors = null;
1343 for (int i = 0; i < scope_list.Length; i++){
1344 // Ignore the same scope
1345 if (scope_list [i] == scope)
1348 if (IsAncestor (scope_list [i], scope)){
1349 if (ancestors == null){
1350 ancestors = scope_list [i];
1354 if (ancestors is ScopeInfo){
1355 object old = ancestors;
1356 ancestors = new ArrayList (4);
1357 ((ArrayList)ancestors).Add (old);
1360 ((ArrayList)ancestors).Add (scope_list [i]);
1367 // Returns the immediate parent of `scope' from all the captured
1368 // scopes found in `scope_list', or null if this is a toplevel scope.
1370 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1372 object ancestors = GetAncestorScopes (scope, scope_list);
1373 if (ancestors == null)
1376 // Single match, thats the parent.
1377 if (ancestors is ScopeInfo)
1378 return (ScopeInfo) ancestors;
1380 ArrayList candidates = (ArrayList) ancestors;
1381 ScopeInfo parent = (ScopeInfo) candidates [0];
1382 for (int i = 1; i < candidates.Count; i++){
1383 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1384 parent = (ScopeInfo) candidates [i];
1390 // Links all the scopes
1393 public void LinkScopes ()
1399 if (ParentCaptureContext != null)
1400 ParentCaptureContext.LinkScopes ();
1402 int scope_count = scopes.Keys.Count;
1403 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1404 scopes.Values.CopyTo (scope_list, 0);
1406 for (int i = 0; i < scope_count; i++){
1407 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1409 if (parent == null){
1410 roots.Add (scope_list [i]);
1414 scope_list [i].ParentScope = parent;
1415 parent.AddChild (scope_list [i]);
1419 // Link the roots to their parent containers if any.
1421 if (ParentCaptureContext != null && roots.Count != 0){
1422 ScopeInfo one_root = (ScopeInfo) roots [0];
1425 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1426 if (!IsAncestor (a_parent_root, one_root))
1431 // Found, link all the roots to this root
1432 foreach (ScopeInfo root in roots){
1433 root.ParentScope = a_parent_root;
1434 a_parent_root.AddChild (root);
1440 // This is to catch a condition in which it is
1441 // not possible to determine the containing ScopeInfo
1442 // from an encapsulating CaptureContext
1444 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");