2 // anonymous.cs: Support for anonymous methods
5 // Miguel de Icaza (miguel@ximain.com)
7 // (C) 2003, 2004 Novell, Inc.
9 // TODO: Ideally, we should have the helper classes emited as a hierarchy to map
10 // their nesting, and have the visibility set to private, instead of NestedAssembly
17 using System.Collections;
18 using System.Reflection;
19 using System.Reflection.Emit;
21 namespace Mono.CSharp {
23 public abstract class AnonymousContainer : Expression
25 // Used to generate unique method names.
26 protected static int anonymous_method_count;
28 // An array list of AnonymousMethodParameter or null
29 public Parameters Parameters;
32 // The block that makes up the body for the anonymous mehtod
34 public ToplevelBlock Block;
37 // The container block for this anonymous method.
39 public Block ContainingBlock;
42 // The implicit method we create
46 protected MethodInfo invoke_mb;
48 // The emit context for the anonymous method
49 public EmitContext aec;
50 public Parameters amp;
51 protected bool unreachable;
54 ScopeInfo method_scope;
55 bool computed_method_scope = false;
58 // The modifiers applied to the method, we aggregate them
60 protected int method_modifiers = Modifiers.PRIVATE;
63 // Track the scopes that this method has used. At the
64 // end this is used to determine the ScopeInfo that will
67 ArrayList scopes_used = new ArrayList ();
70 // Points to our container anonymous method if its present
72 public AnonymousContainer ContainerAnonymousMethod;
74 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
75 ToplevelBlock block, Location l)
77 Parameters = parameters;
82 // The order is important: this setups the CaptureContext tree hierarchy.
84 if (container == null) {
85 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
88 container.SetHaveAnonymousMethods (l, this);
89 block.SetHaveAnonymousMethods (l, this);
92 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
94 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
98 public override Expression DoResolve (EmitContext ec)
101 // Set class type, set type
104 eclass = ExprClass.Value;
107 // This hack means `The type is not accessible
108 // anywhere', we depend on special conversion
111 type = TypeManager.anonymous_method_type;
116 public void RegisterScope (ScopeInfo scope)
118 if (scopes_used.Contains (scope))
120 scopes_used.Add (scope);
123 // Returns the deepest of two scopes
124 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
136 // If they Scopes are on the same CaptureContext, we do the double
137 // checks just so if there is an invariant change in the future,
138 // we get the exception at the end
140 for (p = a; p != null; p = p.ParentScope)
144 for (p = b; p != null; p = p.ParentScope)
148 CaptureContext ca = a.CaptureContext;
149 CaptureContext cb = b.CaptureContext;
151 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
155 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
158 throw new Exception ("Should never be reached");
162 // Determines the proper host for a method considering the
163 // scopes it references
165 public void ComputeMethodHost ()
167 if (computed_method_scope)
171 int top = scopes_used.Count;
172 computed_method_scope = true;
177 method_scope = (ScopeInfo) scopes_used [0];
181 for (int i = 1; i < top; i++)
182 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
185 public ScopeInfo Scope {
187 if (computed_method_scope)
191 // This means that ComputeMethodHost is not being called, most
192 // likely by someone who overwrote the CreateMethodHost method
194 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
199 protected abstract bool CreateMethodHost (EmitContext ec);
201 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
203 public abstract bool IsIterator {
208 public class AnonymousMethod : AnonymousContainer
210 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
211 ToplevelBlock block, Location l)
212 : base (parameters, container, block, l)
216 public override bool IsIterator {
217 get { return false; }
220 public override void Emit (EmitContext ec)
222 // nothing, as we only exist to not do anything.
226 // Creates the host for the anonymous method
228 protected override bool CreateMethodHost (EmitContext ec)
230 ComputeMethodHost ();
233 // Crude hack follows: we replace the TypeBuilder during the
234 // definition to get the method hosted in the right class
236 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
237 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
239 if (current_type == null)
240 throw new Exception ("The current_type is null");
242 if (type_host == null)
243 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
245 if (current_type != type_host)
246 method_modifiers = Modifiers.INTERNAL;
248 if (current_type == type_host && ec.IsStatic){
249 method_modifiers |= Modifiers.STATIC;
253 method = new Method (
254 (TypeContainer) ec.TypeContainer,
255 new TypeExpression (invoke_mb.ReturnType, loc),
256 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
258 method.Block = Block;
261 // Swap the TypeBuilder while we define the method, then restore
263 if (current_type != null)
264 ec.TypeContainer.TypeBuilder = type_host;
265 bool res = method.Define ();
266 if (current_type != null)
267 ec.TypeContainer.TypeBuilder = current_type;
271 void Error_ParameterMismatch (Type t)
273 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
274 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
278 // Returns true if this anonymous method can be implicitly
279 // converted to the delegate type `delegate_type'
281 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
284 // At this point its the first time we know the return type that is
285 // needed for the anonymous method. We create the method here.
288 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
289 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
291 if (Parameters == null){
293 // We provide a set of inaccessible parameters
295 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
297 for (int i = 0; i < invoke_pd.Count; i++){
298 fixedpars [i] = new Parameter (
299 invoke_pd.ParameterType (i),
300 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
303 Parameters = new Parameters (fixedpars);
307 // First, parameter types of `delegate_type' must be compatible
308 // with the anonymous method.
310 Parameters.Resolve (ec);
313 if (amp.Count != invoke_pd.Count){
315 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
316 TypeManager.CSharpName (delegate_type), amp.Count.ToString ());
317 Error_ParameterMismatch (delegate_type);
322 for (int i = 0; i < amp.Count; i++){
323 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
326 if (amp_mod != invoke_pd.ParameterModifier (i)){
327 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
328 (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
329 Error_ParameterMismatch (delegate_type);
333 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
334 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
336 TypeManager.CSharpName (amp.ParameterType (i)),
337 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
338 Error_ParameterMismatch (delegate_type);
345 // If we are only probing, return ourselves
351 // Second: the return type of the delegate must be compatible with
352 // the anonymous type. Instead of doing a pass to examine the block
353 // we satisfy the rule by setting the return type on the EmitContext
354 // to be the delegate type return type.
357 //MethodBuilder builder = method_data.MethodBuilder;
358 //ILGenerator ig = builder.GetILGenerator ();
361 aec = new EmitContext (
362 ec.TypeContainer, ec.DeclSpace, loc, null,
363 invoke_mb.ReturnType,
364 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
365 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
366 (ec.IsStatic ? Modifiers.STATIC : 0),
367 /* No constructor */ false);
369 aec.CurrentAnonymousMethod = this;
370 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
371 ContainingBlock = ec.CurrentBlock;
373 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
374 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
379 public override string ExprClassName {
381 return "anonymous method";
385 public MethodBuilder GetMethodBuilder ()
387 return method.MethodData.MethodBuilder;
390 public override string GetSignatureForError ()
392 string s = TypeManager.CSharpSignature (invoke_mb);
393 return s.Substring (0, s.IndexOf (".Invoke("));
396 public bool EmitMethod (EmitContext ec)
398 if (!CreateMethodHost (ec))
401 MethodBuilder builder = GetMethodBuilder ();
402 ILGenerator ig = builder.GetILGenerator ();
405 Parameters.ApplyAttributes (aec, builder);
408 // Adjust based on the computed state of the
409 // method from CreateMethodHost
411 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
413 aec.EmitMeta (Block);
414 aec.EmitResolvedTopBlock (Block, unreachable);
418 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
420 TypeBuilder container = ec.TypeContainer.TypeBuilder;
421 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
423 scope.ScopeTypeBuilder = container.DefineNestedType (
424 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
425 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
427 Type [] constructor_types = Type.EmptyTypes;
428 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
429 MethodAttributes.Public | MethodAttributes.HideBySig |
430 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
431 CallingConventions.HasThis, constructor_types);
433 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
435 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
436 cig.Emit (OpCodes.Ldarg_0);
437 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
438 cig.Emit (OpCodes.Ret);
441 public static void Error_AddressOfCapturedVar (string name, Location loc)
443 Report.Error (1686, loc,
444 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
450 // This will emit the code for the delegate, as well delegate creation on the host
452 public class AnonymousDelegate : DelegateCreation {
455 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
462 public override Expression DoResolve (EmitContext ec)
464 eclass = ExprClass.Value;
469 public override void Emit (EmitContext ec)
471 if (!am.EmitMethod (ec))
475 // Now emit the delegate creation.
477 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
478 delegate_instance_expression = new AnonymousInstance (am);
480 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
481 constructor_method = ((MethodGroupExpr) ml).Methods [0];
482 delegate_method = am.GetMethodBuilder ();
486 class AnonymousInstance : Expression {
489 public AnonymousInstance (AnonymousMethod am)
492 eclass = ExprClass.Value;
495 public override Expression DoResolve (EmitContext ec)
500 public override void Emit (EmitContext ec)
502 am.aec.EmitMethodHostInstance (ec, am);
507 class CapturedParameter {
509 public FieldBuilder FieldBuilder;
512 public CapturedParameter (Type type, int idx)
520 // Here we cluster all the variables captured on a given scope, we also
521 // keep some extra information that might be required on each scope.
523 public class ScopeInfo {
524 public CaptureContext CaptureContext;
525 public ScopeInfo ParentScope;
526 public Block ScopeBlock;
527 public bool NeedThis = false;
528 public bool HostsParameters = false;
530 // For tracking the number of scopes created.
535 ArrayList locals = new ArrayList ();
536 ArrayList children = new ArrayList ();
539 // The types and fields generated
541 public TypeBuilder ScopeTypeBuilder;
542 public ConstructorBuilder ScopeConstructor;
543 public FieldBuilder THIS;
544 public FieldBuilder ParentLink;
547 // Points to the object of type `ScopeTypeBuilder' that
548 // holds the data for the scope
550 LocalBuilder scope_instance;
552 public ScopeInfo (CaptureContext cc, Block b)
558 cc.RegisterCaptureContext ();
561 public void AddLocal (LocalInfo li)
563 if (locals.Contains (li))
569 public bool IsCaptured (LocalInfo li)
571 return locals.Contains (li);
574 internal void AddChild (ScopeInfo si)
576 if (children.Contains (si))
580 // If any of the current children should be a children of `si', move them there
582 ArrayList move_queue = null;
583 foreach (ScopeInfo child in children){
584 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
585 if (move_queue == null)
586 move_queue = new ArrayList ();
587 move_queue.Add (child);
588 child.ParentScope = si;
595 if (move_queue != null){
596 foreach (ScopeInfo child in move_queue){
597 children.Remove (child);
602 static int indent = 0;
606 for (int i = 0; i < indent; i++)
612 //Console.WriteLine (Environment.StackTrace);
614 Console.WriteLine ("START");
617 Console.WriteLine ("NeedThis=" + NeedThis);
618 foreach (LocalInfo li in locals){
620 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
623 foreach (ScopeInfo si in children)
627 Console.WriteLine ("END");
630 public string MakeHelperName ()
632 return String.Format ("<>AnonHelp<{0}>", id);
635 private string MakeFieldName (string local_name)
637 return "<" + id + ":" + local_name + ">";
640 public void EmitScopeType (EmitContext ec)
644 if (ScopeTypeBuilder != null)
647 TypeBuilder container = ec.TypeContainer.TypeBuilder;
649 CaptureContext.Host.CreateScopeType (ec, this);
652 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
654 if (ParentScope != null){
655 if (ParentScope.ScopeTypeBuilder == null){
656 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
659 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
660 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
661 FieldAttributes.Assembly);
664 if (NeedThis && ParentScope != null)
665 throw new Exception ("I was not expecting THIS && having a parent");
667 foreach (LocalInfo info in locals)
668 info.FieldBuilder = ScopeTypeBuilder.DefineField (
669 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
671 if (HostsParameters){
672 Hashtable captured_parameters = CaptureContext.captured_parameters;
674 foreach (DictionaryEntry de in captured_parameters){
675 string name = (string) de.Key;
676 CapturedParameter cp = (CapturedParameter) de.Value;
679 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
680 cp.FieldBuilder = fb;
684 foreach (ScopeInfo si in children){
685 si.EmitScopeType (ec);
689 public void CloseTypes ()
691 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
692 foreach (ScopeInfo si in children)
697 // Emits the initialization code for the scope
699 public void EmitInitScope (EmitContext ec)
701 ILGenerator ig = ec.ig;
706 if (ScopeConstructor == null)
707 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
709 if (!CaptureContext.Host.IsIterator) {
710 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
711 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
712 ig.Emit (OpCodes.Stloc, scope_instance);
716 if (CaptureContext.Host.IsIterator) {
717 ig.Emit (OpCodes.Ldarg_0);
718 ig.Emit (OpCodes.Ldarg_1);
720 ig.Emit (OpCodes.Ldloc, scope_instance);
721 ig.Emit (OpCodes.Ldarg_0);
723 ig.Emit (OpCodes.Stfld, THIS);
727 // Copy the parameter values, if any
729 int extra = ec.IsStatic ? 0 : 1;
730 if (CaptureContext.Host.IsIterator)
732 if (HostsParameters){
733 Hashtable captured_parameters = CaptureContext.captured_parameters;
735 foreach (DictionaryEntry de in captured_parameters){
736 CapturedParameter cp = (CapturedParameter) de.Value;
738 EmitScopeInstance (ig);
739 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
740 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
744 if (ParentScope != null){
745 if (!ParentScope.inited)
746 ParentScope.EmitInitScope (ec);
748 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
750 // Only emit initialization in our capturecontext world
752 if (ParentScope.CaptureContext == CaptureContext){
753 EmitScopeInstance (ig);
754 ParentScope.EmitScopeInstance (ig);
755 ig.Emit (OpCodes.Stfld, ParentLink);
757 EmitScopeInstance (ig);
758 ig.Emit (OpCodes.Ldarg_0);
759 ig.Emit (OpCodes.Stfld, ParentLink);
766 public void EmitScopeInstance (ILGenerator ig)
768 if (CaptureContext.Host.IsIterator)
769 ig.Emit (OpCodes.Ldarg_0);
771 ig.Emit (OpCodes.Ldloc, scope_instance);
774 public static void CheckCycles (string msg, ScopeInfo s)
776 ArrayList l = new ArrayList ();
779 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
781 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
782 throw new Exception ();
788 static void DoPath (StringBuilder sb, ScopeInfo start)
790 CheckCycles ("print", start);
792 if (start.ParentScope != null){
793 DoPath (sb, start.ParentScope);
796 sb.Append ((start.id).ToString ());
799 public override string ToString ()
801 StringBuilder sb = new StringBuilder ();
804 if (CaptureContext != null){
805 sb.Append (CaptureContext.ToString ());
812 return sb.ToString ();
817 // CaptureContext objects are created on demand if a method has
818 // anonymous methods and kept on the ToplevelBlock.
820 // If they exist, all ToplevelBlocks in the containing block are
821 // linked together (children pointing to their parents).
823 public class CaptureContext {
824 public static int count;
829 // Points to the toplevel block that owns this CaptureContext
831 ToplevelBlock toplevel_owner;
834 // All the scopes we capture
836 Hashtable scopes = new Hashtable ();
839 // All the root scopes
841 ArrayList roots = new ArrayList ();
843 bool have_captured_vars = false;
844 bool referenced_this = false;
849 Hashtable captured_fields = new Hashtable ();
850 Hashtable captured_variables = new Hashtable ();
851 public Hashtable captured_parameters = new Hashtable ();
852 public AnonymousContainer Host;
854 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
855 AnonymousContainer host)
858 this.toplevel_owner = toplevel_owner;
865 void DoPath (StringBuilder sb, CaptureContext cc)
867 if (cc.ParentCaptureContext != null){
868 DoPath (sb, cc.ParentCaptureContext);
871 sb.Append (cc.cc_id.ToString ());
874 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
876 toplevel_owner = new_toplevel;
879 for (CaptureContext cc = ParentCaptureContext; cc != null;
880 cc = cc.ParentCaptureContext) {
885 public override string ToString ()
887 StringBuilder sb = new StringBuilder ();
891 return sb.ToString ();
894 public ToplevelBlock ParentToplevel {
896 return toplevel_owner.Container;
900 public CaptureContext ParentCaptureContext {
902 ToplevelBlock parent = ParentToplevel;
904 return (parent == null) ? null : parent.CaptureContext;
908 ScopeInfo GetScopeForBlock (Block block)
910 ScopeInfo si = (ScopeInfo) scopes [block.ID];
913 si = new ScopeInfo (this, block);
914 scopes [block.ID] = si;
918 public void AddLocal (AnonymousContainer am, LocalInfo li)
920 if (li.Block.Toplevel != toplevel_owner){
921 ParentCaptureContext.AddLocal (am, li);
924 ScopeInfo scope = GetScopeForBlock (li.Block);
930 Host.RegisterScope (scope);
935 am.RegisterScope (scope);
937 if (captured_variables [li] != null)
940 have_captured_vars = true;
941 captured_variables [li] = li;
946 // Retursn the CaptureContext for the block that defines the parameter `name'
948 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
950 ToplevelBlock container = current.Container;
951 if (container != null){
952 CaptureContext cc = _ContextForParameter (container, name);
956 if (current.IsParameterReference (name))
957 return current.ToplevelBlockCaptureContext;
961 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
963 CaptureContext cc = _ContextForParameter (current, name);
965 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
970 // Records the captured parameter at the appropriate CaptureContext
972 public void AddParameter (EmitContext ec, AnonymousContainer am,
973 string name, Type t, int idx)
975 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
977 cc.AddParameterToContext (am, name, t, idx);
981 // Records the parameters in the context
983 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
985 if (captured_parameters == null)
986 captured_parameters = new Hashtable ();
987 if (captured_parameters [name] == null)
988 captured_parameters [name] = new CapturedParameter (t, idx);
990 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
991 scope.HostsParameters = true;
992 am.RegisterScope (scope);
996 // Captured fields are only recorded on the topmost CaptureContext, because that
997 // one is the one linked to the owner of instance fields
999 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1001 if (fe.FieldInfo.IsStatic)
1002 throw new Exception ("Attempt to register a static field as a captured field");
1003 CaptureContext parent = ParentCaptureContext;
1004 if (parent != null) {
1005 parent.AddField (ec, am, fe);
1009 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1010 am.RegisterScope (scope);
1013 public void CaptureThis (AnonymousContainer am)
1016 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1017 CaptureContext parent = ParentCaptureContext;
1018 if (parent != null) {
1019 parent.CaptureThis (am);
1022 referenced_this = true;
1024 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1025 am.RegisterScope (scope);
1028 public bool HaveCapturedVariables {
1030 return have_captured_vars;
1034 public bool HaveCapturedFields {
1036 CaptureContext parent = ParentCaptureContext;
1038 return parent.HaveCapturedFields;
1039 return captured_fields.Count > 0;
1043 public bool IsCaptured (LocalInfo local)
1045 foreach (ScopeInfo si in scopes.Values){
1046 if (si.IsCaptured (local))
1053 // Returns whether the parameter is captured
1055 public bool IsParameterCaptured (string name)
1057 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1060 if (captured_parameters != null)
1061 return captured_parameters [name] != null;
1065 public void EmitAnonymousHelperClasses (EmitContext ec)
1067 if (roots.Count != 0){
1068 foreach (ScopeInfo root in roots){
1070 // FIXME: We really should do this in a per-ScopeInfo
1071 // basis, instead of having the NeedThis applied to
1072 // all of the roots.
1074 root.NeedThis = HaveCapturedFields || referenced_this;
1076 root.EmitScopeType (ec);
1081 public void CloseAnonymousHelperClasses ()
1083 if (roots.Count != 0)
1084 foreach (ScopeInfo root in roots)
1088 public void EmitInitScope (EmitContext ec)
1090 EmitAnonymousHelperClasses (ec);
1091 if (roots.Count != 0)
1092 foreach (ScopeInfo root in roots)
1093 root.EmitInitScope (ec);
1096 ScopeInfo EmitGetScopeFromBlock (EmitContext ec, Block b)
1100 si = (ScopeInfo) scopes [b.ID];
1102 throw new Exception ("Si is null for block " + b.ID);
1103 si.EmitInitScope (ec);
1109 // Emits the opcodes necessary to load the instance of the captured
1112 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1113 AnonymousContainer am)
1115 ILGenerator ig = ec.ig;
1118 if (li.Block.Toplevel == toplevel_owner){
1119 si = EmitGetScopeFromBlock (ec, li.Block);
1120 si.EmitScopeInstance (ig);
1125 ig.Emit (OpCodes.Ldarg_0);
1127 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1131 while (si.ScopeBlock.ID != li.Block.ID){
1132 if (si.ParentLink != null)
1133 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1134 si = si.ParentScope;
1137 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1138 while (si.ScopeBlock.ID != li.Block.ID){
1139 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1140 si = si.ParentScope;
1143 throw new Exception (
1144 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1145 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1152 // Internal routine that loads the instance to reach parameter `name'
1154 void EmitParameterInstance (EmitContext ec, string name)
1156 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1158 cc.EmitParameterInstance (ec, name);
1162 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1163 if (par_info != null){
1165 // FIXME: implementing this.
1168 ILGenerator ig = ec.ig;
1172 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1173 si = EmitGetScopeFromBlock (ec, toplevel_owner);
1174 si.EmitScopeInstance (ig);
1176 si = ec.CurrentAnonymousMethod.Scope;
1177 ig.Emit (OpCodes.Ldarg_0);
1181 while (si.ParentLink != null) {
1182 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1183 si = si.ParentScope;
1189 // Emits the code necessary to load the parameter named `name' within
1190 // an anonymous method.
1192 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1194 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1196 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1200 EmitParameterInstance (ec, name);
1201 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1202 if (par_info != null){
1204 // FIXME: implementing this.
1207 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1210 ec.ig.Emit (OpCodes.Dup);
1211 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1217 // Implements the assignment of `source' to the paramenter named `name' within
1218 // an anonymous method.
1220 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1222 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1224 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1227 ILGenerator ig = ec.ig;
1228 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1230 EmitParameterInstance (ec, name);
1231 if (prepare_for_load)
1232 ig.Emit (OpCodes.Dup);
1235 ig.Emit (OpCodes.Dup);
1236 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1239 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1245 // Emits the address for the parameter named `name' within
1246 // an anonymous method.
1248 public void EmitAddressOfParameter (EmitContext ec, string name)
1250 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1252 cc.EmitAddressOfParameter (ec, name);
1255 EmitParameterInstance (ec, name);
1256 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1257 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1261 // The following methods are only invoked on the host for the
1262 // anonymous method.
1264 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1266 ILGenerator ig = target.ig;
1267 ScopeInfo si = am.Scope;
1269 AnonymousContainer container = am.ContainerAnonymousMethod;
1271 if ((si == null) || ((container != null) && (si == container.Scope))) {
1272 ig.Emit (OpCodes.Ldarg_0);
1276 si.EmitInitScope (target);
1277 si.EmitScopeInstance (ig);
1280 public void RegisterCaptureContext ()
1282 toplevel_owner.RegisterCaptureContext (this);
1286 // Returs true if `probe' is an ancestor of `scope' in the
1289 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1291 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1292 if (probe.ScopeBlock == b)
1299 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1300 // of `scope' found in `scope_list'.
1302 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1304 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1306 object ancestors = null;
1308 for (int i = 0; i < scope_list.Length; i++){
1309 // Ignore the same scope
1310 if (scope_list [i] == scope)
1313 if (IsAncestor (scope_list [i], scope)){
1314 if (ancestors == null){
1315 ancestors = scope_list [i];
1319 if (ancestors is ScopeInfo){
1320 object old = ancestors;
1321 ancestors = new ArrayList (4);
1322 ((ArrayList)ancestors).Add (old);
1325 ((ArrayList)ancestors).Add (scope_list [i]);
1332 // Returns the immediate parent of `scope' from all the captured
1333 // scopes found in `scope_list', or null if this is a toplevel scope.
1335 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1337 object ancestors = GetAncestorScopes (scope, scope_list);
1338 if (ancestors == null)
1341 // Single match, thats the parent.
1342 if (ancestors is ScopeInfo)
1343 return (ScopeInfo) ancestors;
1345 ArrayList candidates = (ArrayList) ancestors;
1346 ScopeInfo parent = (ScopeInfo) candidates [0];
1347 for (int i = 1; i < candidates.Count; i++){
1348 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1349 parent = (ScopeInfo) candidates [i];
1355 // Links all the scopes
1358 public void LinkScopes ()
1364 if (ParentCaptureContext != null)
1365 ParentCaptureContext.LinkScopes ();
1367 int scope_count = scopes.Keys.Count;
1368 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1369 scopes.Values.CopyTo (scope_list, 0);
1371 for (int i = 0; i < scope_count; i++){
1372 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1374 if (parent == null){
1375 roots.Add (scope_list [i]);
1379 scope_list [i].ParentScope = parent;
1380 parent.AddChild (scope_list [i]);
1384 // Link the roots to their parent containers if any.
1386 if (ParentCaptureContext != null && roots.Count != 0){
1387 ScopeInfo one_root = (ScopeInfo) roots [0];
1390 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1391 if (!IsAncestor (a_parent_root, one_root))
1396 // Found, link all the roots to this root
1397 foreach (ScopeInfo root in roots){
1398 root.ParentScope = a_parent_root;
1399 a_parent_root.AddChild (root);
1405 // This is to catch a condition in which it is
1406 // not possible to determine the containing ScopeInfo
1407 // from an encapsulating CaptureContext
1409 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");