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
211 // The value return by the Compatible call, this ensure that
212 // the code works even if invoked more than once (Resolve called
213 // more than once, due to the way Convert.ImplicitConversion works
215 Expression anonymous_delegate;
217 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
218 ToplevelBlock block, Location l)
219 : base (parameters, container, block, l)
224 public override bool IsIterator {
225 get { return false; }
228 public override void Emit (EmitContext ec)
230 // nothing, as we only exist to not do anything.
234 // Creates the host for the anonymous method
236 protected override bool CreateMethodHost (EmitContext ec)
238 ComputeMethodHost ();
241 // Crude hack follows: we replace the TypeBuilder during the
242 // definition to get the method hosted in the right class
244 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
245 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
247 if (current_type == null)
248 throw new Exception ("The current_type is null");
250 if (type_host == null)
251 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
253 if (current_type != type_host)
254 method_modifiers = Modifiers.INTERNAL;
256 if (current_type == type_host && ec.IsStatic){
257 method_modifiers |= Modifiers.STATIC;
261 method = new Method (
262 (TypeContainer) ec.TypeContainer,
263 new TypeExpression (invoke_mb.ReturnType, loc),
264 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
266 method.Block = Block;
269 // Swap the TypeBuilder while we define the method, then restore
271 if (current_type != null)
272 ec.TypeContainer.TypeBuilder = type_host;
273 bool res = method.Define ();
274 if (current_type != null)
275 ec.TypeContainer.TypeBuilder = current_type;
279 void Error_ParameterMismatch (Type t)
281 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
282 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
285 public bool ImplicitStandardConversionExists (Type delegate_type)
287 if (Parameters == null)
290 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
291 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
293 if (Parameters.Count != invoke_pd.Count)
296 for (int i = 0; i < Parameters.Count; ++i) {
297 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
304 // Returns true if this anonymous method can be implicitly
305 // converted to the delegate type `delegate_type'
307 public Expression Compatible (EmitContext ec, Type delegate_type)
309 if (anonymous_delegate != null)
310 return anonymous_delegate;
313 // At this point its the first time we know the return type that is
314 // needed for the anonymous method. We create the method here.
317 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
318 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
320 if (Parameters == null) {
322 // We provide a set of inaccessible parameters
324 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
326 for (int i = 0; i < invoke_pd.Count; i++){
327 fixedpars [i] = new Parameter (
328 invoke_pd.ParameterType (i),
329 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
332 Parameters = new Parameters (fixedpars);
334 if (Parameters.Count != invoke_pd.Count) {
335 Report.SymbolRelatedToPreviousError (delegate_type);
336 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
337 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
338 Error_ParameterMismatch (delegate_type);
342 for (int i = 0; i < Parameters.Count; ++i) {
343 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
344 if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
345 if (p_mod == Parameter.Modifier.NONE)
346 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
347 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
349 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
350 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
351 Error_ParameterMismatch (delegate_type);
355 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
356 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
358 TypeManager.CSharpName (Parameters.ParameterType (i)),
359 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
360 Error_ParameterMismatch (delegate_type);
367 // Second: the return type of the delegate must be compatible with
368 // the anonymous type. Instead of doing a pass to examine the block
369 // we satisfy the rule by setting the return type on the EmitContext
370 // to be the delegate type return type.
373 //MethodBuilder builder = method_data.MethodBuilder;
374 //ILGenerator ig = builder.GetILGenerator ();
376 aec = new EmitContext (ec.ResolveContext,
377 ec.TypeContainer, ec.DeclContainer, loc, null,
378 invoke_mb.ReturnType,
379 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
380 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
381 (ec.IsStatic ? Modifiers.STATIC : 0),
382 /* No constructor */ false);
384 aec.CurrentAnonymousMethod = this;
385 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
386 ContainingBlock = ec.CurrentBlock;
388 if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable)){
389 anonymous_delegate = new AnonymousDelegate (
390 this, delegate_type, loc).Resolve (ec);
391 return anonymous_delegate;
396 public override Expression DoResolve (EmitContext ec)
398 if (!ec.IsAnonymousMethodAllowed) {
399 Report.Error (1706, loc, "Anonymous methods are not allowed in the attribute declaration");
403 if (Parameters != null && !Parameters.Resolve (ec)) {
407 return base.DoResolve (ec);
411 public override string ExprClassName {
413 return "anonymous method";
417 public MethodBuilder GetMethodBuilder ()
419 return method.MethodBuilder;
422 public override string GetSignatureForError ()
424 string s = TypeManager.CSharpSignature (invoke_mb);
425 return s.Substring (0, s.IndexOf (".Invoke("));
428 public bool EmitMethod (EmitContext ec)
430 if (!CreateMethodHost (ec))
433 MethodBuilder builder = GetMethodBuilder ();
434 ILGenerator ig = builder.GetILGenerator ();
437 Parameters.ApplyAttributes (builder);
440 // Adjust based on the computed state of the
441 // method from CreateMethodHost
443 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
445 aec.EmitMeta (Block);
446 aec.EmitResolvedTopBlock (Block, unreachable);
450 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
452 TypeBuilder container = ec.TypeContainer.TypeBuilder;
453 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
455 scope.ScopeTypeBuilder = container.DefineNestedType (name,
456 TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit | TypeAttributes.NestedPrivate,
457 TypeManager.object_type);
459 Type [] constructor_types = Type.EmptyTypes;
460 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
461 MethodAttributes.Public | MethodAttributes.HideBySig |
462 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
463 CallingConventions.HasThis, constructor_types);
465 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
467 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
468 cig.Emit (OpCodes.Ldarg_0);
469 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
470 cig.Emit (OpCodes.Ret);
473 public static void Error_AddressOfCapturedVar (string name, Location loc)
475 Report.Error (1686, loc,
476 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
482 // This will emit the code for the delegate, as well delegate creation on the host
484 public class AnonymousDelegate : DelegateCreation {
487 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
494 public override Expression DoResolve (EmitContext ec)
496 eclass = ExprClass.Value;
501 public override void Emit (EmitContext ec)
503 if (!am.EmitMethod (ec))
507 // Now emit the delegate creation.
509 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
510 delegate_instance_expression = new AnonymousInstance (am);
512 Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
513 BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, loc);
514 constructor_method = ((MethodGroupExpr) ml).Methods [0];
515 delegate_method = am.GetMethodBuilder ();
519 class AnonymousInstance : Expression {
522 public AnonymousInstance (AnonymousMethod am)
525 eclass = ExprClass.Value;
528 public override Expression DoResolve (EmitContext ec)
533 public override void Emit (EmitContext ec)
535 am.aec.EmitMethodHostInstance (ec, am);
540 class CapturedParameter {
542 public FieldBuilder FieldBuilder;
545 public CapturedParameter (Type type, int idx)
553 // Here we cluster all the variables captured on a given scope, we also
554 // keep some extra information that might be required on each scope.
556 public class ScopeInfo {
557 public CaptureContext CaptureContext;
558 public ScopeInfo ParentScope;
559 public Block ScopeBlock;
560 public bool NeedThis = false;
561 public bool HostsParameters = false;
563 // For tracking the number of scopes created.
568 ArrayList locals = new ArrayList ();
569 ArrayList children = new ArrayList ();
572 // The types and fields generated
574 public TypeBuilder ScopeTypeBuilder;
575 public ConstructorBuilder ScopeConstructor;
576 public FieldBuilder THIS;
577 public FieldBuilder ParentLink;
580 // Points to the object of type `ScopeTypeBuilder' that
581 // holds the data for the scope
583 LocalBuilder scope_instance;
585 public ScopeInfo (CaptureContext cc, Block b)
591 cc.RegisterCaptureContext ();
594 public void AddLocal (LocalInfo li)
596 if (locals.Contains (li))
602 public bool IsCaptured (LocalInfo li)
604 return locals.Contains (li);
607 internal void AddChild (ScopeInfo si)
609 if (children.Contains (si))
613 // If any of the current children should be a children of `si', move them there
615 ArrayList move_queue = null;
616 foreach (ScopeInfo child in children){
617 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
618 if (move_queue == null)
619 move_queue = new ArrayList ();
620 move_queue.Add (child);
621 child.ParentScope = si;
628 if (move_queue != null){
629 foreach (ScopeInfo child in move_queue){
630 children.Remove (child);
635 static int indent = 0;
639 for (int i = 0; i < indent; i++)
645 //Console.WriteLine (Environment.StackTrace);
647 Console.WriteLine ("START");
650 Console.WriteLine ("NeedThis=" + NeedThis);
651 foreach (LocalInfo li in locals){
653 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
656 foreach (ScopeInfo si in children)
660 Console.WriteLine ("END");
663 private string MakeFieldName (string local_name)
665 return "<" + id + ":" + local_name + ">";
668 public void EmitScopeType (EmitContext ec)
672 if (ScopeTypeBuilder != null)
675 TypeBuilder container = ec.TypeContainer.TypeBuilder;
677 CaptureContext.Host.CreateScopeType (ec, this);
680 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
682 if (ParentScope != null){
683 if (ParentScope.ScopeTypeBuilder == null){
684 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
687 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
688 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
689 FieldAttributes.Assembly);
692 if (NeedThis && ParentScope != null)
693 throw new Exception ("I was not expecting THIS && having a parent");
695 foreach (LocalInfo info in locals)
696 info.FieldBuilder = ScopeTypeBuilder.DefineField (
697 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
699 if (HostsParameters){
700 Hashtable captured_parameters = CaptureContext.captured_parameters;
702 foreach (DictionaryEntry de in captured_parameters){
703 string name = (string) de.Key;
704 CapturedParameter cp = (CapturedParameter) de.Value;
707 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
708 cp.FieldBuilder = fb;
712 foreach (ScopeInfo si in children){
713 si.EmitScopeType (ec);
717 public void CloseTypes ()
719 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
720 foreach (ScopeInfo si in children)
725 // Emits the initialization code for the scope
727 public void EmitInitScope (EmitContext ec)
729 ILGenerator ig = ec.ig;
734 if (ScopeConstructor == null)
735 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
737 if (!CaptureContext.Host.IsIterator) {
738 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
739 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
740 ig.Emit (OpCodes.Stloc, scope_instance);
744 if (CaptureContext.Host.IsIterator) {
745 ig.Emit (OpCodes.Ldarg_0);
746 ig.Emit (OpCodes.Ldarg_1);
748 ig.Emit (OpCodes.Ldloc, scope_instance);
749 ig.Emit (OpCodes.Ldarg_0);
751 ig.Emit (OpCodes.Stfld, THIS);
755 // Copy the parameter values, if any
757 int extra = ec.IsStatic ? 0 : 1;
758 if (CaptureContext.Host.IsIterator)
760 if (HostsParameters){
761 Hashtable captured_parameters = CaptureContext.captured_parameters;
763 foreach (DictionaryEntry de in captured_parameters){
764 CapturedParameter cp = (CapturedParameter) de.Value;
766 EmitScopeInstance (ig);
767 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
768 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
772 if (ParentScope != null){
773 if (!ParentScope.inited)
774 ParentScope.EmitInitScope (ec);
776 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
778 // Only emit initialization in our capturecontext world
780 if (ParentScope.CaptureContext == CaptureContext){
781 EmitScopeInstance (ig);
782 ParentScope.EmitScopeInstance (ig);
783 ig.Emit (OpCodes.Stfld, ParentLink);
785 EmitScopeInstance (ig);
786 ig.Emit (OpCodes.Ldarg_0);
787 ig.Emit (OpCodes.Stfld, ParentLink);
794 public void EmitScopeInstance (ILGenerator ig)
796 if (CaptureContext.Host.IsIterator)
797 ig.Emit (OpCodes.Ldarg_0);
799 if (scope_instance == null){
801 // This is needed if someone overwrites the Emit method
802 // of Statement and manually calls Block.Emit without
803 // this snippet first:
805 // ec.EmitScopeInitFromBlock (The_Block);
806 // The_Block.Emit (ec);
810 "The scope_instance has not been emitted, this typically means\n" +
811 "that inside the compiler someone is calling Block.Emit without\n" +
812 "first calling EmitScopeInitFromBlock for the block. See compiler" +
813 "source code for an explanation");
814 throw new Exception ("Internal compiler error");
817 ig.Emit (OpCodes.Ldloc, scope_instance);
821 public static void CheckCycles (string msg, ScopeInfo s)
823 ArrayList l = new ArrayList ();
826 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
828 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
829 throw new Exception ();
835 static void DoPath (StringBuilder sb, ScopeInfo start)
837 CheckCycles ("print", start);
839 if (start.ParentScope != null){
840 DoPath (sb, start.ParentScope);
843 sb.Append ((start.id).ToString ());
846 public override string ToString ()
848 StringBuilder sb = new StringBuilder ();
851 if (CaptureContext != null){
852 sb.Append (CaptureContext.ToString ());
859 return sb.ToString ();
864 // CaptureContext objects are created on demand if a method has
865 // anonymous methods and kept on the ToplevelBlock.
867 // If they exist, all ToplevelBlocks in the containing block are
868 // linked together (children pointing to their parents).
870 public class CaptureContext {
871 public static int count;
876 // Points to the toplevel block that owns this CaptureContext
878 ToplevelBlock toplevel_owner;
881 // All the scopes we capture
883 Hashtable scopes = new Hashtable ();
886 // All the root scopes
888 ArrayList roots = new ArrayList ();
890 bool have_captured_vars = false;
891 bool referenced_this = false;
896 Hashtable captured_fields = new Hashtable ();
897 Hashtable captured_variables = new Hashtable ();
898 public Hashtable captured_parameters = new Hashtable ();
899 public AnonymousContainer Host;
901 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
902 AnonymousContainer host)
905 this.toplevel_owner = toplevel_owner;
912 void DoPath (StringBuilder sb, CaptureContext cc)
914 if (cc.ParentCaptureContext != null){
915 DoPath (sb, cc.ParentCaptureContext);
918 sb.Append (cc.cc_id.ToString ());
921 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
923 toplevel_owner = new_toplevel;
926 for (CaptureContext cc = ParentCaptureContext; cc != null;
927 cc = cc.ParentCaptureContext) {
932 public override string ToString ()
934 StringBuilder sb = new StringBuilder ();
938 return sb.ToString ();
941 public ToplevelBlock ParentToplevel {
943 return toplevel_owner.Container;
947 public CaptureContext ParentCaptureContext {
949 ToplevelBlock parent = ParentToplevel;
951 return (parent == null) ? null : parent.CaptureContext;
955 ScopeInfo GetScopeForBlock (Block block)
957 ScopeInfo si = (ScopeInfo) scopes [block.ID];
960 si = new ScopeInfo (this, block);
961 scopes [block.ID] = si;
965 public void AddLocal (AnonymousContainer am, LocalInfo li)
967 if (li.Block.Toplevel != toplevel_owner){
968 ParentCaptureContext.AddLocal (am, li);
971 ScopeInfo scope = GetScopeForBlock (li.Block);
977 Host.RegisterScope (scope);
982 am.RegisterScope (scope);
984 if (captured_variables [li] != null)
987 have_captured_vars = true;
988 captured_variables [li] = li;
993 // Retursn the CaptureContext for the block that defines the parameter `name'
995 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
997 ToplevelBlock container = current.Container;
998 if (container != null){
999 CaptureContext cc = _ContextForParameter (container, name);
1003 if (current.IsParameterReference (name))
1004 return current.ToplevelBlockCaptureContext;
1008 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1010 CaptureContext cc = _ContextForParameter (current, name);
1012 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1017 // Records the captured parameter at the appropriate CaptureContext
1019 public void AddParameter (EmitContext ec, AnonymousContainer am,
1020 string name, Type t, int idx)
1022 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1024 cc.AddParameterToContext (am, name, t, idx);
1028 // Records the parameters in the context
1030 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1032 if (captured_parameters == null)
1033 captured_parameters = new Hashtable ();
1034 if (captured_parameters [name] == null)
1035 captured_parameters [name] = new CapturedParameter (t, idx);
1037 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1038 scope.HostsParameters = true;
1039 am.RegisterScope (scope);
1043 // Captured fields are only recorded on the topmost CaptureContext, because that
1044 // one is the one linked to the owner of instance fields
1046 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1048 if (fe.FieldInfo.IsStatic)
1049 throw new Exception ("Attempt to register a static field as a captured field");
1050 CaptureContext parent = ParentCaptureContext;
1051 if (parent != null) {
1052 parent.AddField (ec, am, fe);
1056 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1057 am.RegisterScope (scope);
1060 public void CaptureThis (AnonymousContainer am)
1063 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1064 CaptureContext parent = ParentCaptureContext;
1065 if (parent != null) {
1066 parent.CaptureThis (am);
1069 referenced_this = true;
1071 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1072 am.RegisterScope (scope);
1075 public bool HaveCapturedVariables {
1077 return have_captured_vars;
1081 public bool HaveCapturedFields {
1083 CaptureContext parent = ParentCaptureContext;
1085 return parent.HaveCapturedFields;
1086 return captured_fields.Count > 0;
1090 public bool IsCaptured (LocalInfo local)
1092 foreach (ScopeInfo si in scopes.Values){
1093 if (si.IsCaptured (local))
1100 // Returns whether the parameter is captured
1102 public bool IsParameterCaptured (string name)
1104 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1107 if (captured_parameters != null)
1108 return captured_parameters [name] != null;
1112 public void EmitAnonymousHelperClasses (EmitContext ec)
1114 if (roots.Count != 0){
1115 foreach (ScopeInfo root in roots){
1117 // FIXME: We really should do this in a per-ScopeInfo
1118 // basis, instead of having the NeedThis applied to
1119 // all of the roots.
1121 root.NeedThis = HaveCapturedFields || referenced_this;
1123 root.EmitScopeType (ec);
1128 public void CloseAnonymousHelperClasses ()
1130 if (roots.Count != 0)
1131 foreach (ScopeInfo root in roots)
1135 public void EmitInitScope (EmitContext ec)
1137 EmitAnonymousHelperClasses (ec);
1138 if (roots.Count != 0)
1139 foreach (ScopeInfo root in roots)
1140 root.EmitInitScope (ec); }
1143 // This is called externally when we start emitting code for a block
1144 // if the block has a ScopeInfo associated, emit the init code
1146 public void EmitScopeInitFromBlock (EmitContext ec, Block b)
1148 ScopeInfo si = (ScopeInfo) scopes [b.ID];
1152 si.EmitInitScope (ec);
1156 // Emits the opcodes necessary to load the instance of the captured
1159 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1160 AnonymousContainer am)
1162 ILGenerator ig = ec.ig;
1165 if (li.Block.Toplevel == toplevel_owner){
1166 si = (ScopeInfo) scopes [li.Block.ID];
1167 si.EmitScopeInstance (ig);
1172 ig.Emit (OpCodes.Ldarg_0);
1174 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1178 while (si.ScopeBlock.ID != li.Block.ID){
1179 if (si.ParentLink != null)
1180 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1181 si = si.ParentScope;
1184 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1185 while (si.ScopeBlock.ID != li.Block.ID){
1186 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1187 si = si.ParentScope;
1190 throw new Exception (
1191 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1192 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1199 // Internal routine that loads the instance to reach parameter `name'
1201 void EmitParameterInstance (EmitContext ec, string name)
1203 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1205 cc.EmitParameterInstance (ec, name);
1209 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1210 if (par_info != null){
1212 // FIXME: implementing this.
1215 ILGenerator ig = ec.ig;
1219 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1220 si = (ScopeInfo) scopes [toplevel_owner.ID];
1221 si.EmitScopeInstance (ig);
1223 si = ec.CurrentAnonymousMethod.Scope;
1224 ig.Emit (OpCodes.Ldarg_0);
1228 while (si.ParentLink != null) {
1229 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1230 si = si.ParentScope;
1236 // Emits the code necessary to load the parameter named `name' within
1237 // an anonymous method.
1239 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1241 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1243 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1247 EmitParameterInstance (ec, name);
1248 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1249 if (par_info != null){
1251 // FIXME: implementing this.
1254 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1257 ec.ig.Emit (OpCodes.Dup);
1258 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1264 // Implements the assignment of `source' to the paramenter named `name' within
1265 // an anonymous method.
1267 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1269 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1271 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1274 ILGenerator ig = ec.ig;
1275 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1277 EmitParameterInstance (ec, name);
1278 if (prepare_for_load)
1279 ig.Emit (OpCodes.Dup);
1282 ig.Emit (OpCodes.Dup);
1283 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1286 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1292 // Emits the address for the parameter named `name' within
1293 // an anonymous method.
1295 public void EmitAddressOfParameter (EmitContext ec, string name)
1297 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1299 cc.EmitAddressOfParameter (ec, name);
1302 EmitParameterInstance (ec, name);
1303 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1304 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1308 // The following methods are only invoked on the host for the
1309 // anonymous method.
1311 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1313 ILGenerator ig = target.ig;
1314 ScopeInfo si = am.Scope;
1316 AnonymousContainer container = am.ContainerAnonymousMethod;
1318 if ((si == null) || ((container != null) && (si == container.Scope))) {
1319 ig.Emit (OpCodes.Ldarg_0);
1323 si.EmitInitScope (target);
1324 si.EmitScopeInstance (ig);
1327 public void RegisterCaptureContext ()
1329 toplevel_owner.RegisterCaptureContext (this);
1333 // Returs true if `probe' is an ancestor of `scope' in the
1336 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1338 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1339 if (probe.ScopeBlock == b)
1346 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1347 // of `scope' found in `scope_list'.
1349 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1351 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1353 object ancestors = null;
1355 for (int i = 0; i < scope_list.Length; i++){
1356 // Ignore the same scope
1357 if (scope_list [i] == scope)
1360 if (IsAncestor (scope_list [i], scope)){
1361 if (ancestors == null){
1362 ancestors = scope_list [i];
1366 if (ancestors is ScopeInfo){
1367 object old = ancestors;
1368 ancestors = new ArrayList (4);
1369 ((ArrayList)ancestors).Add (old);
1372 ((ArrayList)ancestors).Add (scope_list [i]);
1379 // Returns the immediate parent of `scope' from all the captured
1380 // scopes found in `scope_list', or null if this is a toplevel scope.
1382 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1384 object ancestors = GetAncestorScopes (scope, scope_list);
1385 if (ancestors == null)
1388 // Single match, thats the parent.
1389 if (ancestors is ScopeInfo)
1390 return (ScopeInfo) ancestors;
1392 ArrayList candidates = (ArrayList) ancestors;
1393 ScopeInfo parent = (ScopeInfo) candidates [0];
1394 for (int i = 1; i < candidates.Count; i++){
1395 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1396 parent = (ScopeInfo) candidates [i];
1402 // Links all the scopes
1405 public void LinkScopes ()
1411 if (ParentCaptureContext != null)
1412 ParentCaptureContext.LinkScopes ();
1414 int scope_count = scopes.Keys.Count;
1415 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1416 scopes.Values.CopyTo (scope_list, 0);
1418 for (int i = 0; i < scope_count; i++){
1419 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1421 if (parent == null){
1422 roots.Add (scope_list [i]);
1426 scope_list [i].ParentScope = parent;
1427 parent.AddChild (scope_list [i]);
1431 // Link the roots to their parent containers if any.
1433 if (ParentCaptureContext != null && roots.Count != 0){
1434 ScopeInfo one_root = (ScopeInfo) roots [0];
1437 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1438 if (!IsAncestor (a_parent_root, one_root))
1443 // Found, link all the roots to this root
1444 foreach (ScopeInfo root in roots){
1445 root.ParentScope = a_parent_root;
1446 a_parent_root.AddChild (root);
1452 // This is to catch a condition in which it is
1453 // not possible to determine the containing ScopeInfo
1454 // from an encapsulating CaptureContext
1456 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");