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 // The modifiers applied to the method, we aggregate them
56 protected int method_modifiers = Modifiers.PRIVATE;
59 // During the resolve stage of the anonymous method body,
60 // we discover the actual scope where we are hosted, or
61 // null to host the method in the same class
63 public ScopeInfo Scope;
66 // Points to our container anonymous method if its present
68 public AnonymousContainer ContainerAnonymousMethod;
70 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
71 ToplevelBlock block, Location l)
73 Parameters = parameters;
78 // The order is important: this setups the CaptureContext tree hierarchy.
80 if (container == null) {
81 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
84 container.SetHaveAnonymousMethods (l, this);
85 block.SetHaveAnonymousMethods (l, this);
88 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
90 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
94 public override Expression DoResolve (EmitContext ec)
97 // Set class type, set type
100 eclass = ExprClass.Value;
103 // This hack means `The type is not accessible
104 // anywhere', we depend on special conversion
107 type = TypeManager.anonymous_method_type;
112 protected abstract bool CreateMethodHost (EmitContext ec);
114 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
116 public abstract bool IsIterator {
121 public class AnonymousMethod : AnonymousContainer
123 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
124 ToplevelBlock block, Location l)
125 : base (parameters, container, block, l)
129 public override bool IsIterator {
130 get { return false; }
133 public override void Emit (EmitContext ec)
135 // nothing, as we only exist to not do anything.
139 // Creates the host for the anonymous method
141 protected override bool CreateMethodHost (EmitContext ec)
144 // Crude hack follows: we replace the TypeBuilder during the
145 // definition to get the method hosted in the right class
148 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
149 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
150 ? current_type : Scope.ScopeTypeBuilder;
152 if (current_type == null)
153 throw new Exception ("The current_type is null");
155 if (type_host == null)
156 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
158 if (current_type != type_host)
159 method_modifiers = Modifiers.INTERNAL;
161 if (current_type == type_host && ec.IsStatic){
162 method_modifiers |= Modifiers.STATIC;
166 method = new Method (
167 (TypeContainer) ec.TypeContainer,
168 new TypeExpression (invoke_mb.ReturnType, loc),
169 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
171 method.Block = Block;
174 // Swap the TypeBuilder while we define the method, then restore
176 if (current_type != null)
177 ec.TypeContainer.TypeBuilder = type_host;
178 bool res = method.Define ();
179 if (current_type != null)
180 ec.TypeContainer.TypeBuilder = current_type;
184 void Error_ParameterMismatch (Type t)
186 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
187 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
191 // Returns true if this anonymous method can be implicitly
192 // converted to the delegate type `delegate_type'
194 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
197 // At this point its the first time we know the return type that is
198 // needed for the anonymous method. We create the method here.
201 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
202 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
204 if (Parameters == null){
206 // We provide a set of inaccessible parameters
208 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
210 for (int i = 0; i < invoke_pd.Count; i++){
211 fixedpars [i] = new Parameter (
212 invoke_pd.ParameterType (i),
213 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
216 Parameters = new Parameters (fixedpars);
220 // First, parameter types of `delegate_type' must be compatible
221 // with the anonymous method.
223 Parameters.Resolve (ec);
226 if (amp.Count != invoke_pd.Count){
228 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
229 TypeManager.CSharpName (delegate_type), amp.Count.ToString ());
230 Error_ParameterMismatch (delegate_type);
235 for (int i = 0; i < amp.Count; i++){
236 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
239 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
240 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
241 (i+1).ToString (), Parameter.GetModifierSignature (amp_mod));
242 Error_ParameterMismatch (delegate_type);
246 if (amp_mod != invoke_pd.ParameterModifier (i)){
247 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
248 (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
249 Error_ParameterMismatch (delegate_type);
253 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
254 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
256 TypeManager.CSharpName (amp.ParameterType (i)),
257 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
258 Error_ParameterMismatch (delegate_type);
265 // If we are only probing, return ourselves
271 // Second: the return type of the delegate must be compatible with
272 // the anonymous type. Instead of doing a pass to examine the block
273 // we satisfy the rule by setting the return type on the EmitContext
274 // to be the delegate type return type.
277 //MethodBuilder builder = method_data.MethodBuilder;
278 //ILGenerator ig = builder.GetILGenerator ();
281 aec = new EmitContext (
282 ec.TypeContainer, ec.DeclSpace, loc, null,
283 invoke_mb.ReturnType,
284 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
285 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
286 (ec.IsStatic ? Modifiers.STATIC : 0),
287 /* No constructor */ false);
289 aec.CurrentAnonymousMethod = this;
290 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
291 ContainingBlock = ec.CurrentBlock;
293 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
294 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
299 public override string ExprClassName {
301 return "anonymous method";
305 public MethodBuilder GetMethodBuilder ()
307 return method.MethodData.MethodBuilder;
310 public override string GetSignatureForError ()
312 string s = TypeManager.CSharpSignature (invoke_mb);
313 return s.Substring (0, s.IndexOf (".Invoke("));
316 public bool EmitMethod (EmitContext ec)
318 if (!CreateMethodHost (ec))
321 MethodBuilder builder = GetMethodBuilder ();
322 ILGenerator ig = builder.GetILGenerator ();
325 Parameters.ApplyAttributes (aec, builder);
328 // Adjust based on the computed state of the
329 // method from CreateMethodHost
331 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
333 aec.EmitMeta (Block);
334 aec.EmitResolvedTopBlock (Block, unreachable);
338 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
340 TypeBuilder container = ec.TypeContainer.TypeBuilder;
341 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
343 scope.ScopeTypeBuilder = container.DefineNestedType (
344 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
345 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
347 Type [] constructor_types = TypeManager.NoTypes;
348 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
349 MethodAttributes.Public | MethodAttributes.HideBySig |
350 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
351 CallingConventions.HasThis, constructor_types);
353 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
355 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
356 cig.Emit (OpCodes.Ldarg_0);
357 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
358 cig.Emit (OpCodes.Ret);
361 public static void Error_AddressOfCapturedVar (string name, Location loc)
363 Report.Error (1686, loc,
364 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
370 // This will emit the code for the delegate, as well delegate creation on the host
372 public class AnonymousDelegate : DelegateCreation {
375 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
382 public override Expression DoResolve (EmitContext ec)
384 eclass = ExprClass.Value;
389 public override void Emit (EmitContext ec)
391 if (!am.EmitMethod (ec))
395 // Now emit the delegate creation.
397 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
398 delegate_instance_expression = new AnonymousInstance (am);
400 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
401 constructor_method = ((MethodGroupExpr) ml).Methods [0];
402 delegate_method = am.GetMethodBuilder ();
406 class AnonymousInstance : Expression {
409 public AnonymousInstance (AnonymousMethod am)
412 eclass = ExprClass.Value;
415 public override Expression DoResolve (EmitContext ec)
420 public override void Emit (EmitContext ec)
422 am.aec.EmitMethodHostInstance (ec, am);
427 class CapturedParameter {
429 public FieldBuilder FieldBuilder;
432 public CapturedParameter (Type type, int idx)
440 // Here we cluster all the variables captured on a given scope, we also
441 // keep some extra information that might be required on each scope.
443 public class ScopeInfo {
444 public CaptureContext CaptureContext;
445 public ScopeInfo ParentScope;
446 public Block ScopeBlock;
447 public bool NeedThis = false;
448 public bool HostsParameters = false;
450 // For tracking the number of scopes created.
455 ArrayList locals = new ArrayList ();
456 ArrayList children = new ArrayList ();
459 // The types and fields generated
461 public TypeBuilder ScopeTypeBuilder;
462 public ConstructorBuilder ScopeConstructor;
463 public FieldBuilder THIS;
464 public FieldBuilder ParentLink;
467 // Points to the object of type `ScopeTypeBuilder' that
468 // holds the data for the scope
470 LocalBuilder scope_instance;
472 public ScopeInfo (CaptureContext cc, Block b)
481 public void AddLocal (LocalInfo li)
483 if (locals.Contains (li))
489 public bool IsCaptured (LocalInfo li)
491 return locals.Contains (li);
494 internal void AddChild (ScopeInfo si)
496 if (children.Contains (si))
500 // If any of the current children should be a children of `si', move them there
502 ArrayList move_queue = null;
503 foreach (ScopeInfo child in children){
504 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
505 if (move_queue == null)
506 move_queue = new ArrayList ();
507 move_queue.Add (child);
508 child.ParentScope = si;
515 if (move_queue != null){
516 foreach (ScopeInfo child in move_queue){
517 children.Remove (child);
522 static int indent = 0;
526 for (int i = 0; i < indent; i++)
532 //Console.WriteLine (Environment.StackTrace);
534 Console.WriteLine ("START");
537 Console.WriteLine ("NeedThis=" + NeedThis);
538 foreach (LocalInfo li in locals){
540 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
543 foreach (ScopeInfo si in children)
547 Console.WriteLine ("END");
550 public string MakeHelperName ()
552 return String.Format ("<>AnonHelp<{0}>", id);
555 private string MakeFieldName (string local_name)
557 return "<" + id + ":" + local_name + ">";
560 public void EmitScopeType (EmitContext ec)
564 if (ScopeTypeBuilder != null)
567 TypeBuilder container = ec.TypeContainer.TypeBuilder;
569 CaptureContext.Host.CreateScopeType (ec, this);
572 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
574 if (ParentScope != null){
575 if (ParentScope.ScopeTypeBuilder == null){
576 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
579 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
580 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
581 FieldAttributes.Assembly);
584 if (NeedThis && ParentScope != null)
585 throw new Exception ("I was not expecting THIS && having a parent");
587 foreach (LocalInfo info in locals)
588 info.FieldBuilder = ScopeTypeBuilder.DefineField (
589 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
591 if (HostsParameters){
592 Hashtable captured_parameters = CaptureContext.captured_parameters;
594 foreach (DictionaryEntry de in captured_parameters){
595 string name = (string) de.Key;
596 CapturedParameter cp = (CapturedParameter) de.Value;
599 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
600 cp.FieldBuilder = fb;
604 foreach (ScopeInfo si in children){
605 si.EmitScopeType (ec);
609 public void CloseTypes ()
611 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
612 foreach (ScopeInfo si in children)
617 // Emits the initialization code for the scope
619 public void EmitInitScope (EmitContext ec)
621 ILGenerator ig = ec.ig;
626 if (ScopeConstructor == null)
627 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
629 if (!CaptureContext.Host.IsIterator) {
630 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
631 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
632 ig.Emit (OpCodes.Stloc, scope_instance);
636 if (CaptureContext.Host.IsIterator) {
637 ig.Emit (OpCodes.Ldarg_0);
638 ig.Emit (OpCodes.Ldarg_1);
640 ig.Emit (OpCodes.Ldloc, scope_instance);
641 ig.Emit (OpCodes.Ldarg_0);
643 ig.Emit (OpCodes.Stfld, THIS);
647 // Copy the parameter values, if any
649 int extra = ec.IsStatic ? 0 : 1;
650 if (CaptureContext.Host.IsIterator)
652 if (HostsParameters){
653 Hashtable captured_parameters = CaptureContext.captured_parameters;
655 foreach (DictionaryEntry de in captured_parameters){
656 CapturedParameter cp = (CapturedParameter) de.Value;
658 EmitScopeInstance (ig);
659 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
660 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
664 if (ParentScope != null){
665 if (!ParentScope.inited)
666 ParentScope.EmitInitScope (ec);
668 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
670 // Only emit initialization in our capturecontext world
672 if (ParentScope.CaptureContext == CaptureContext){
673 EmitScopeInstance (ig);
674 ParentScope.EmitScopeInstance (ig);
675 ig.Emit (OpCodes.Stfld, ParentLink);
677 EmitScopeInstance (ig);
678 ig.Emit (OpCodes.Ldarg_0);
679 ig.Emit (OpCodes.Stfld, ParentLink);
686 public void EmitScopeInstance (ILGenerator ig)
688 if (CaptureContext.Host.IsIterator)
689 ig.Emit (OpCodes.Ldarg_0);
691 ig.Emit (OpCodes.Ldloc, scope_instance);
694 static void DoPath (StringBuilder sb, ScopeInfo start)
696 if (start.ParentScope != null){
697 DoPath (sb, start.ParentScope);
700 sb.Append ((start.id).ToString ());
703 public override string ToString ()
705 StringBuilder sb = new StringBuilder ();
708 if (CaptureContext != null){
709 sb.Append (CaptureContext.ToString ());
716 return sb.ToString ();
721 // CaptureContext objects are created on demand if a method has
722 // anonymous methods and kept on the ToplevelBlock.
724 // If they exist, all ToplevelBlocks in the containing block are
725 // linked together (children pointing to their parents).
727 public class CaptureContext {
728 public static int count;
733 // Points to the toplevel block that owns this CaptureContext
735 ToplevelBlock toplevel_owner;
736 Hashtable scopes = new Hashtable ();
737 bool have_captured_vars = false;
738 bool referenced_this = false;
739 ScopeInfo topmost = null;
744 Hashtable captured_fields = new Hashtable ();
745 Hashtable captured_variables = new Hashtable ();
746 public Hashtable captured_parameters = new Hashtable ();
747 public AnonymousContainer Host;
749 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
750 AnonymousContainer host)
753 this.toplevel_owner = toplevel_owner;
760 void DoPath (StringBuilder sb, CaptureContext cc)
762 if (cc.ParentCaptureContext != null){
763 DoPath (sb, cc.ParentCaptureContext);
766 sb.Append (cc.cc_id.ToString ());
769 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
771 toplevel_owner = new_toplevel;
774 for (CaptureContext cc = ParentCaptureContext; cc != null;
775 cc = cc.ParentCaptureContext) {
780 public override string ToString ()
782 StringBuilder sb = new StringBuilder ();
786 return sb.ToString ();
789 public ToplevelBlock ParentToplevel {
791 return toplevel_owner.Container;
795 public CaptureContext ParentCaptureContext {
797 ToplevelBlock parent = ParentToplevel;
799 return (parent == null) ? null : parent.CaptureContext;
803 // Returns the deepest of two scopes
804 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
816 // If they Scopes are on the same CaptureContext, we do the double
817 // checks just so if there is an invariant change in the future,
818 // we get the exception at the end
820 for (p = a; p != null; p = p.ParentScope)
824 for (p = b; p != null; p = p.ParentScope)
828 CaptureContext ca = a.CaptureContext;
829 CaptureContext cb = b.CaptureContext;
831 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
835 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
838 throw new Exception ("Should never be reached");
841 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
843 am.Scope = Deepest (am.Scope, scope);
846 void LinkScope (ScopeInfo scope, int id)
848 ScopeInfo parent = (ScopeInfo) scopes [id];
849 scope.ParentScope = parent;
850 parent.AddChild (scope);
852 if (scope == topmost)
856 public void AddLocal (AnonymousContainer am, LocalInfo li)
858 if (li.Block.Toplevel != toplevel_owner){
859 ParentCaptureContext.AddLocal (am, li);
862 int block_id = li.Block.ID;
864 if (scopes [block_id] == null){
865 scope = new ScopeInfo (this, li.Block);
866 scopes [block_id] = scope;
868 scope = (ScopeInfo) scopes [block_id];
870 if (topmost == null){
875 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
876 if (scopes [b.ID] != null){
877 LinkScope (scope, b.ID);
882 if (scope.ParentScope == null && ParentCaptureContext != null){
883 CaptureContext pcc = ParentCaptureContext;
885 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
886 if (pcc.scopes [b.ID] != null){
887 pcc.LinkScope (scope, b.ID);
898 AdjustMethodScope (Host, topmost);
903 AdjustMethodScope (am, scope);
905 if (captured_variables [li] != null)
908 have_captured_vars = true;
909 captured_variables [li] = li;
914 // Retursn the CaptureContext for the block that defines the parameter `name'
916 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
918 ToplevelBlock container = current.Container;
919 if (container != null){
920 CaptureContext cc = _ContextForParameter (container, name);
924 if (current.IsParameterReference (name))
925 return current.ToplevelBlockCaptureContext;
929 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
931 CaptureContext cc = _ContextForParameter (current, name);
933 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
938 // Records the captured parameter at the appropriate CaptureContext
940 public void AddParameter (EmitContext ec, AnonymousContainer am,
941 string name, Type t, int idx)
943 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
945 cc.AddParameterToContext (am, name, t, idx);
949 // Records the parameters in the context
951 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
953 if (captured_parameters == null)
954 captured_parameters = new Hashtable ();
955 if (captured_parameters [name] == null)
956 captured_parameters [name] = new CapturedParameter (t, idx);
958 if (topmost == null){
960 // Create one ScopeInfo, if there are none.
962 topmost = new ScopeInfo (this, toplevel_owner);
963 scopes [toplevel_owner.ID] = topmost;
966 // If the topmost ScopeInfo is not at the topblock level, insert
967 // a new ScopeInfo there.
969 // FIXME: This code probably should be evolved to be like the code
972 if (topmost.ScopeBlock != toplevel_owner){
973 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
974 ScopeInfo old_top = topmost;
975 scopes [toplevel_owner.ID] = topmost;
976 topmost.ParentScope = par_si;
978 topmost.AddChild (old_top);
982 topmost.HostsParameters = true;
983 AdjustMethodScope (am, topmost);
987 // Captured fields are only recorded on the topmost CaptureContext, because that
988 // one is the one linked to the owner of instance fields
990 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
992 if (fe.FieldInfo.IsStatic)
993 throw new Exception ("Attempt to register a static field as a captured field");
994 CaptureContext parent = ParentCaptureContext;
995 if (parent != null) {
996 parent.AddField (ec, am, fe);
1000 if (topmost == null){
1002 // Create one ScopeInfo, if there are none.
1004 topmost = new ScopeInfo (this, toplevel_owner);
1005 scopes [toplevel_owner.ID] = topmost;
1008 AdjustMethodScope (am, topmost);
1011 public void CaptureThis (AnonymousContainer am)
1013 CaptureContext parent = ParentCaptureContext;
1014 if (parent != null) {
1015 parent.CaptureThis (am);
1018 referenced_this = true;
1020 if (topmost == null){
1022 // Create one ScopeInfo, if there are none.
1024 topmost = new ScopeInfo (this, toplevel_owner);
1025 scopes [toplevel_owner.ID] = topmost;
1027 AdjustMethodScope (am, topmost);
1031 public bool HaveCapturedVariables {
1033 return have_captured_vars;
1037 public bool HaveCapturedFields {
1039 CaptureContext parent = ParentCaptureContext;
1041 return parent.HaveCapturedFields;
1042 return captured_fields.Count > 0;
1046 public bool IsCaptured (LocalInfo local)
1048 foreach (ScopeInfo si in scopes.Values){
1049 if (si.IsCaptured (local))
1056 // Returns whether the parameter is captured
1058 public bool IsParameterCaptured (string name)
1060 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1063 if (captured_parameters != null)
1064 return captured_parameters [name] != null;
1068 public void EmitAnonymousHelperClasses (EmitContext ec)
1070 if (topmost != null){
1071 topmost.NeedThis = HaveCapturedFields || referenced_this;
1072 topmost.EmitScopeType (ec);
1076 public void CloseAnonymousHelperClasses ()
1078 if (topmost != null)
1079 topmost.CloseTypes ();
1082 public void EmitInitScope (EmitContext ec)
1084 EmitAnonymousHelperClasses (ec);
1085 if (topmost != null)
1086 topmost.EmitInitScope (ec);
1089 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1093 si = (ScopeInfo) scopes [b.ID];
1095 throw new Exception ("Si is null for block " + b.ID);
1096 si.EmitInitScope (ec);
1102 // Emits the opcodes necessary to load the instance of the captured
1105 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1106 AnonymousContainer am)
1108 ILGenerator ig = ec.ig;
1111 if (li.Block.Toplevel == toplevel_owner){
1112 si = GetScopeFromBlock (ec, li.Block);
1113 si.EmitScopeInstance (ig);
1118 ig.Emit (OpCodes.Ldarg_0);
1120 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1124 while (si.ScopeBlock.ID != li.Block.ID){
1125 if (si.ParentLink != null)
1126 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1127 si = si.ParentScope;
1130 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1131 while (si.ScopeBlock.ID != li.Block.ID){
1132 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1133 si = si.ParentScope;
1136 throw new Exception (
1137 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1138 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1145 // Internal routine that loads the instance to reach parameter `name'
1147 void EmitParameterInstance (EmitContext ec, string name)
1149 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1151 cc.EmitParameterInstance (ec, name);
1155 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1156 if (par_info != null){
1158 // FIXME: implementing this.
1161 ILGenerator ig = ec.ig;
1165 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1166 si = GetScopeFromBlock (ec, toplevel_owner);
1167 si.EmitScopeInstance (ig);
1169 si = ec.CurrentAnonymousMethod.Scope;
1170 ig.Emit (OpCodes.Ldarg_0);
1174 while (si.ParentLink != null) {
1175 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1176 si = si.ParentScope;
1182 // Emits the code necessary to load the parameter named `name' within
1183 // an anonymous method.
1185 public void EmitParameter (EmitContext ec, string name)
1187 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1189 cc.EmitParameter (ec, name);
1192 EmitParameterInstance (ec, name);
1193 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1194 if (par_info != null){
1196 // FIXME: implementing this.
1199 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1203 // Implements the assignment of `source' to the paramenter named `name' within
1204 // an anonymous method.
1206 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1208 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1210 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1213 ILGenerator ig = ec.ig;
1214 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1216 EmitParameterInstance (ec, name);
1218 ig.Emit (OpCodes.Dup);
1220 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1222 ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1227 // Emits the address for the parameter named `name' within
1228 // an anonymous method.
1230 public void EmitAddressOfParameter (EmitContext ec, string name)
1232 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1234 cc.EmitAddressOfParameter (ec, name);
1237 EmitParameterInstance (ec, name);
1238 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1239 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1243 // The following methods are only invoked on the host for the
1244 // anonymous method.
1246 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1248 ILGenerator ig = target.ig;
1249 ScopeInfo si = am.Scope;
1251 AnonymousContainer container = am.ContainerAnonymousMethod;
1253 if ((si == null) || ((container != null) && (si == container.Scope))) {
1254 ig.Emit (OpCodes.Ldarg_0);
1258 si.EmitInitScope (target);
1259 si.EmitScopeInstance (ig);
1262 ArrayList all_scopes = new ArrayList ();
1264 public void AddScope (ScopeInfo si)
1266 all_scopes.Add (si);
1267 toplevel_owner.RegisterCaptureContext (this);
1271 // Links any scopes that were not linked previously
1273 public void AdjustScopes ()
1275 foreach (ScopeInfo scope in all_scopes){
1276 if (scope.ParentScope != null)
1279 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1280 if (scopes [b.ID] != null){
1281 LinkScope (scope, b.ID);
1286 if (scope.ParentScope == null && ParentCaptureContext != null){
1287 CaptureContext pcc = ParentCaptureContext;
1289 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1290 if (pcc.scopes [b.ID] != null){
1291 pcc.LinkScope (scope, b.ID);