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 InternalParameters amp;
51 public string[] TypeParameters;
52 public Type[] TypeArguments;
53 protected bool unreachable;
56 // The modifiers applied to the method, we aggregate them
58 protected int method_modifiers = Modifiers.PRIVATE;
61 // During the resolve stage of the anonymous method body,
62 // we discover the actual scope where we are hosted, or
63 // null to host the method in the same class
65 public ScopeInfo Scope;
68 // Points to our container anonymous method if its present
70 public AnonymousContainer ContainerAnonymousMethod;
72 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
73 ToplevelBlock block, Location l)
75 Parameters = parameters;
80 // The order is important: this setups the CaptureContext tree hierarchy.
82 container.SetHaveAnonymousMethods (l, this);
83 block.SetHaveAnonymousMethods (l, this);
86 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
89 Parameters = parameters;
90 Block = new ToplevelBlock (container, Parameters, l);
94 // The order is important: this setups the CaptureContext tree hierarchy.
96 container.SetHaveAnonymousMethods (loc, this);
97 Block.SetHaveAnonymousMethods (loc, this);
100 public override Expression DoResolve (EmitContext ec)
103 // Set class type, set type
106 eclass = ExprClass.Value;
109 // This hack means `The type is not accessible
110 // anywhere', we depend on special conversion
113 type = TypeManager.anonymous_method_type;
118 protected abstract bool CreateMethodHost (EmitContext ec);
120 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
122 public abstract Iterator Iterator {
126 public abstract bool IsIterator {
131 public class AnonymousMethod : AnonymousContainer
133 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
134 ToplevelBlock block, Location l)
135 : base (parameters, container, block, l)
139 public override Iterator Iterator {
143 public override bool IsIterator {
144 get { return false; }
147 public override void Emit (EmitContext ec)
149 // nothing, as we only exist to not do anything.
153 // Creates the host for the anonymous method
155 protected override bool CreateMethodHost (EmitContext ec)
158 // Crude hack follows: we replace the TypeBuilder during the
159 // definition to get the method hosted in the right class
162 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
163 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
164 ? current_type : Scope.ScopeTypeBuilder;
166 if (current_type == null)
167 throw new Exception ("The current_type is null");
169 if (type_host == null)
170 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
172 if (current_type == type_host && ec.IsStatic){
174 method_modifiers |= Modifiers.STATIC;
179 string name = "<#AnonymousMethod>" + anonymous_method_count++;
180 MemberName member_name;
182 GenericMethod generic_method = null;
183 if (TypeParameters != null) {
184 TypeArguments args = new TypeArguments (loc);
185 foreach (string t in TypeParameters)
186 args.Add (new SimpleName (t, loc));
188 member_name = new MemberName (name, args);
190 generic_method = new GenericMethod (
191 ec.DeclSpace.NamespaceEntry,
192 (TypeContainer) ec.TypeContainer,
195 generic_method.SetParameterInfo (null);
197 member_name = new MemberName (name);
199 method = new Method (
200 (TypeContainer) ec.TypeContainer, generic_method,
201 new TypeExpression (invoke_mb.ReturnType, loc),
202 method_modifiers, false, member_name,
203 Parameters, null, loc);
204 method.Block = Block;
207 // Swap the TypeBuilder while we define the method, then restore
209 if (current_type != null)
210 ec.TypeContainer.TypeBuilder = type_host;
211 bool res = method.Define ();
212 if (current_type != null)
213 ec.TypeContainer.TypeBuilder = current_type;
218 void Error_ParameterMismatch (Type t)
220 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
221 "{0}' since there is a parameter mismatch", t);
225 // Returns true if this anonymous method can be implicitly
226 // converted to the delegate type `delegate_type'
228 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
231 // At this point its the first time we know the return type that is
232 // needed for the anonymous method. We create the method here.
235 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec, delegate_type, loc);
236 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
237 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
239 if (delegate_type.IsGenericInstance) {
240 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
242 Type def = delegate_type.GetGenericTypeDefinition ();
243 Type[] tparam = TypeManager.GetTypeArguments (def);
244 TypeParameters = new string [tparam.Length];
245 for (int i = 0; i < tparam.Length; i++)
246 TypeParameters [i] = tparam [i].Name;
249 if (Parameters == null){
253 // We provide a set of inaccessible parameters
256 for (i = 0; i < invoke_pd.Count; i++){
257 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
260 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
261 Parameter [] fixedpars = new Parameter [n];
263 for (i = j = 0; i < invoke_pd.Count; i++){
264 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
266 fixedpars [j] = new Parameter (
267 new TypeExpression (invoke_pd.ParameterType (i), loc),
268 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
272 Parameter variable = null;
273 if (params_idx != -1){
274 variable = new Parameter (
275 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
276 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
279 Parameters = new Parameters (fixedpars, variable);
283 // First, parameter types of `delegate_type' must be compatible
284 // with the anonymous method.
286 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
288 if (amp.Count != invoke_pd.Count){
290 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
291 TypeManager.CSharpName (delegate_type), amp.Count);
292 Error_ParameterMismatch (delegate_type);
297 for (int i = 0; i < amp.Count; i++){
298 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
301 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
302 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
303 i+1, amp.ModifierDesc (i));
304 Error_ParameterMismatch (delegate_type);
308 if (amp_mod != invoke_pd.ParameterModifier (i)){
309 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
310 i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
311 Error_ParameterMismatch (delegate_type);
315 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
316 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
318 TypeManager.CSharpName (amp.ParameterType (i)),
319 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
320 Error_ParameterMismatch (delegate_type);
327 // If we are only probing, return ourselves
333 // Second: the return type of the delegate must be compatible with
334 // the anonymous type. Instead of doing a pass to examine the block
335 // we satisfy the rule by setting the return type on the EmitContext
336 // to be the delegate type return type.
339 //MethodBuilder builder = method_data.MethodBuilder;
340 //ILGenerator ig = builder.GetILGenerator ();
343 aec = new EmitContext (
344 ec.TypeContainer, ec.DeclSpace, loc, null,
345 invoke_mb.ReturnType,
346 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
347 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
348 (ec.IsStatic ? Modifiers.STATIC : 0),
349 /* No constructor */ false);
351 aec.CurrentAnonymousMethod = this;
352 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
353 ContainingBlock = ec.CurrentBlock;
355 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
356 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
361 public MethodInfo GetMethodBuilder ()
363 MethodInfo builder = method.MethodData.MethodBuilder;
364 if (TypeArguments != null)
365 return builder.BindGenericParameters (TypeArguments);
370 public override string GetSignatureForError ()
372 string s = TypeManager.CSharpSignature (invoke_mb);
373 return s.Substring (0, s.IndexOf (".Invoke("));
376 public bool EmitMethod (EmitContext ec)
378 if (!CreateMethodHost (ec))
381 MethodBuilder builder = method.MethodData.MethodBuilder;
382 ILGenerator ig = builder.GetILGenerator ();
385 Parameters.LabelParameters (aec, builder);
388 // Adjust based on the computed state of the
389 // method from CreateMethodHost
391 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
393 aec.EmitMeta (Block, amp);
394 aec.EmitResolvedTopBlock (Block, unreachable);
398 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
400 TypeBuilder container = ec.TypeContainer.TypeBuilder;
401 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
403 scope.ScopeTypeBuilder = container.DefineNestedType (
404 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
405 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
407 Type [] constructor_types = TypeManager.NoTypes;
408 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
409 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
410 MethodAttributes.Public | MethodAttributes.HideBySig |
411 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
412 CallingConventions.HasThis, constructor_types);
413 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
414 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
416 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
417 cig.Emit (OpCodes.Ldarg_0);
418 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
419 cig.Emit (OpCodes.Ret);
422 public static void Error_AddressOfCapturedVar (string name, Location loc)
424 Report.Error (1686, loc,
425 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
431 // This will emit the code for the delegate, as well delegate creation on the host
433 public class AnonymousDelegate : DelegateCreation {
436 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
443 public override Expression DoResolve (EmitContext ec)
445 eclass = ExprClass.Value;
450 public override void Emit (EmitContext ec)
452 if (!am.EmitMethod (ec))
456 // Now emit the delegate creation.
458 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
459 delegate_instance_expression = new AnonymousInstance (am);
461 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
462 constructor_method = ((MethodGroupExpr) ml).Methods [0];
463 delegate_method = am.GetMethodBuilder ();
467 class AnonymousInstance : Expression {
470 public AnonymousInstance (AnonymousMethod am)
473 eclass = ExprClass.Value;
476 public override Expression DoResolve (EmitContext ec)
481 public override void Emit (EmitContext ec)
483 am.aec.EmitMethodHostInstance (ec, am);
488 class CapturedParameter {
490 public FieldBuilder FieldBuilder;
493 public CapturedParameter (Type type, int idx)
501 // Here we cluster all the variables captured on a given scope, we also
502 // keep some extra information that might be required on each scope.
504 public class ScopeInfo {
505 public CaptureContext CaptureContext;
506 public ScopeInfo ParentScope;
507 public Block ScopeBlock;
508 public bool NeedThis = false;
509 public bool HostsParameters = false;
511 // For tracking the number of scopes created.
516 ArrayList locals = new ArrayList ();
517 ArrayList children = new ArrayList ();
520 // The types and fields generated
522 public TypeBuilder ScopeTypeBuilder;
523 public ConstructorBuilder ScopeConstructor;
524 public FieldBuilder THIS;
525 public FieldBuilder ParentLink;
528 // Points to the object of type `ScopeTypeBuilder' that
529 // holds the data for the scope
531 LocalBuilder scope_instance;
533 public ScopeInfo (CaptureContext cc, Block b)
542 public void AddLocal (LocalInfo li)
544 if (locals.Contains (li))
550 public bool IsCaptured (LocalInfo li)
552 return locals.Contains (li);
555 internal void AddChild (ScopeInfo si)
557 if (children.Contains (si))
561 // If any of the current children should be a children of `si', move them there
563 ArrayList move_queue = null;
564 foreach (ScopeInfo child in children){
565 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
566 if (move_queue == null)
567 move_queue = new ArrayList ();
568 move_queue.Add (child);
569 child.ParentScope = si;
576 if (move_queue != null){
577 foreach (ScopeInfo child in move_queue){
578 children.Remove (child);
583 static int indent = 0;
587 for (int i = 0; i < indent; i++)
593 //Console.WriteLine (Environment.StackTrace);
595 Console.WriteLine ("START");
598 Console.WriteLine ("NeedThis=" + NeedThis);
599 foreach (LocalInfo li in locals){
601 Console.WriteLine ("var {0}", li.Name);
604 foreach (ScopeInfo si in children)
608 Console.WriteLine ("END");
611 public string MakeHelperName ()
613 return String.Format ("<>AnonHelp<{0}>", id);
616 public void EmitScopeType (EmitContext ec)
620 if (ScopeTypeBuilder != null)
623 TypeBuilder container = ec.TypeContainer.TypeBuilder;
625 CaptureContext.Host.CreateScopeType (ec, this);
628 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
630 if (ParentScope != null){
631 if (ParentScope.ScopeTypeBuilder == null){
632 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
635 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
636 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
637 FieldAttributes.Assembly);
640 if (NeedThis && ParentScope != null)
641 throw new Exception ("I was not expecting THIS && having a parent");
643 foreach (LocalInfo info in locals)
644 info.FieldBuilder = ScopeTypeBuilder.DefineField (
645 info.Name, info.VariableType, FieldAttributes.Assembly);
647 if (HostsParameters){
648 Hashtable captured_parameters = CaptureContext.captured_parameters;
650 foreach (DictionaryEntry de in captured_parameters){
651 string name = (string) de.Key;
652 CapturedParameter cp = (CapturedParameter) de.Value;
655 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
656 cp.FieldBuilder = fb;
660 foreach (ScopeInfo si in children){
661 si.EmitScopeType (ec);
665 public void CloseTypes ()
667 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
668 foreach (ScopeInfo si in children)
673 // Emits the initialization code for the scope
675 public void EmitInitScope (EmitContext ec)
677 ILGenerator ig = ec.ig;
682 if (ScopeConstructor == null)
683 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
685 if (!CaptureContext.Host.IsIterator) {
686 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
687 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
688 ig.Emit (OpCodes.Stloc, scope_instance);
692 if (CaptureContext.Host.IsIterator) {
693 ig.Emit (OpCodes.Ldarg_0);
694 ig.Emit (OpCodes.Ldarg_1);
696 ig.Emit (OpCodes.Ldloc, scope_instance);
697 ig.Emit (OpCodes.Ldarg_0);
699 ig.Emit (OpCodes.Stfld, THIS);
703 // Copy the parameter values, if any
705 int extra = ec.IsStatic ? 0 : 1;
706 if (CaptureContext.Host.IsIterator)
708 if (HostsParameters){
709 Hashtable captured_parameters = CaptureContext.captured_parameters;
711 foreach (DictionaryEntry de in captured_parameters){
712 CapturedParameter cp = (CapturedParameter) de.Value;
714 EmitScopeInstance (ig);
715 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
716 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
720 if (ParentScope != null){
721 if (!ParentScope.inited)
722 ParentScope.EmitInitScope (ec);
724 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
726 // Only emit initialization in our capturecontext world
728 if (ParentScope.CaptureContext == CaptureContext){
729 EmitScopeInstance (ig);
730 ParentScope.EmitScopeInstance (ig);
731 ig.Emit (OpCodes.Stfld, ParentLink);
733 EmitScopeInstance (ig);
734 ig.Emit (OpCodes.Ldarg_0);
735 ig.Emit (OpCodes.Stfld, ParentLink);
742 public void EmitScopeInstance (ILGenerator ig)
744 if (CaptureContext.Host.IsIterator)
745 ig.Emit (OpCodes.Ldarg_0);
747 ig.Emit (OpCodes.Ldloc, scope_instance);
750 static void DoPath (StringBuilder sb, ScopeInfo start)
752 if (start.ParentScope != null){
753 DoPath (sb, start.ParentScope);
756 sb.Append ((start.id).ToString ());
759 public override string ToString ()
761 StringBuilder sb = new StringBuilder ();
764 if (CaptureContext != null){
765 sb.Append (CaptureContext.ToString ());
772 return sb.ToString ();
777 // CaptureContext objects are created on demand if a method has
778 // anonymous methods and kept on the ToplevelBlock.
780 // If they exist, all ToplevelBlocks in the containing block are
781 // linked together (children pointing to their parents).
783 public class CaptureContext {
784 public static int count;
789 // Points to the toplevel block that owns this CaptureContext
791 ToplevelBlock toplevel_owner;
792 Hashtable scopes = new Hashtable ();
793 bool have_captured_vars = false;
794 bool referenced_this = false;
795 ScopeInfo topmost = null;
800 Hashtable captured_fields = new Hashtable ();
801 Hashtable captured_variables = new Hashtable ();
802 public Hashtable captured_parameters = new Hashtable ();
803 public AnonymousContainer Host;
805 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
806 AnonymousContainer host)
809 this.toplevel_owner = toplevel_owner;
816 void DoPath (StringBuilder sb, CaptureContext cc)
818 if (cc.ParentCaptureContext != null){
819 DoPath (sb, cc.ParentCaptureContext);
822 sb.Append (cc.cc_id.ToString ());
825 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
827 toplevel_owner = new_toplevel;
830 for (CaptureContext cc = ParentCaptureContext; cc != null;
831 cc = cc.ParentCaptureContext) {
836 public override string ToString ()
838 StringBuilder sb = new StringBuilder ();
842 return sb.ToString ();
845 public ToplevelBlock ParentToplevel {
847 return toplevel_owner.Container;
851 public CaptureContext ParentCaptureContext {
853 ToplevelBlock parent = ParentToplevel;
855 return (parent == null) ? null : parent.CaptureContext;
859 // Returns the deepest of two scopes
860 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
872 // If they Scopes are on the same CaptureContext, we do the double
873 // checks just so if there is an invariant change in the future,
874 // we get the exception at the end
876 for (p = a; p != null; p = p.ParentScope)
880 for (p = b; p != null; p = p.ParentScope)
884 CaptureContext ca = a.CaptureContext;
885 CaptureContext cb = b.CaptureContext;
887 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
891 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
894 throw new Exception ("Should never be reached");
897 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
899 am.Scope = Deepest (am.Scope, scope);
902 void LinkScope (ScopeInfo scope, int id)
904 ScopeInfo parent = (ScopeInfo) scopes [id];
905 scope.ParentScope = parent;
906 parent.AddChild (scope);
908 if (scope == topmost)
912 public void AddLocal (AnonymousContainer am, LocalInfo li)
914 if (li.Block.Toplevel != toplevel_owner){
915 ParentCaptureContext.AddLocal (am, li);
918 int block_id = li.Block.ID;
920 if (scopes [block_id] == null){
921 scope = new ScopeInfo (this, li.Block);
922 scopes [block_id] = scope;
924 scope = (ScopeInfo) scopes [block_id];
926 if (topmost == null){
931 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
932 if (scopes [b.ID] != null){
933 LinkScope (scope, b.ID);
938 if (scope.ParentScope == null && ParentCaptureContext != null){
939 CaptureContext pcc = ParentCaptureContext;
941 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
942 if (pcc.scopes [b.ID] != null){
943 pcc.LinkScope (scope, b.ID);
954 AdjustMethodScope (Host, topmost);
959 AdjustMethodScope (am, scope);
961 if (captured_variables [li] != null)
964 have_captured_vars = true;
965 captured_variables [li] = li;
970 // Retursn the CaptureContext for the block that defines the parameter `name'
972 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
974 ToplevelBlock container = current.Container;
975 if (container != null){
976 CaptureContext cc = _ContextForParameter (container, name);
980 if (current.IsParameterReference (name))
981 return current.ToplevelBlockCaptureContext;
985 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
987 CaptureContext cc = _ContextForParameter (current, name);
989 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
994 // Records the captured parameter at the appropriate CaptureContext
996 public void AddParameter (EmitContext ec, AnonymousContainer am,
997 string name, Type t, int idx)
999 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1001 cc.AddParameterToContext (am, name, t, idx);
1005 // Records the parameters in the context
1007 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1009 if (captured_parameters == null)
1010 captured_parameters = new Hashtable ();
1011 if (captured_parameters [name] != null)
1013 captured_parameters [name] = new CapturedParameter (t, idx);
1015 if (topmost == null){
1017 // Create one ScopeInfo, if there are none.
1019 topmost = new ScopeInfo (this, toplevel_owner);
1020 scopes [toplevel_owner.ID] = topmost;
1023 // If the topmost ScopeInfo is not at the topblock level, insert
1024 // a new ScopeInfo there.
1026 // FIXME: This code probably should be evolved to be like the code
1029 if (topmost.ScopeBlock != toplevel_owner){
1030 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
1031 ScopeInfo old_top = topmost;
1032 scopes [toplevel_owner.ID] = topmost;
1033 topmost.ParentScope = par_si;
1035 topmost.AddChild (old_top);
1039 topmost.HostsParameters = true;
1040 AdjustMethodScope (am, topmost);
1044 // Captured fields are only recorded on the topmost CaptureContext, because that
1045 // one is the one linked to the owner of instance fields
1047 public void AddField (FieldExpr fe)
1049 if (fe.FieldInfo.IsStatic)
1050 throw new Exception ("Attempt to register a static field as a captured field");
1052 CaptureContext parent = ParentCaptureContext;
1054 parent.AddField (fe);
1056 captured_fields [fe] = fe;
1059 public void CaptureThis ()
1061 CaptureContext parent = ParentCaptureContext;
1063 parent.CaptureThis ();
1064 referenced_this = true;
1067 public bool HaveCapturedVariables {
1069 return have_captured_vars;
1073 public bool HaveCapturedFields {
1075 CaptureContext parent = ParentCaptureContext;
1077 return parent.HaveCapturedFields;
1078 return captured_fields.Count > 0;
1082 public bool IsCaptured (LocalInfo local)
1084 foreach (ScopeInfo si in scopes.Values){
1085 if (si.IsCaptured (local))
1092 // Returns whether the parameter is captured
1094 public bool IsParameterCaptured (string name)
1096 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1099 if (captured_parameters != null)
1100 return captured_parameters [name] != null;
1104 public void EmitAnonymousHelperClasses (EmitContext ec)
1106 if (topmost != null){
1107 topmost.NeedThis = HaveCapturedFields || referenced_this;
1108 topmost.EmitScopeType (ec);
1112 public void CloseAnonymousHelperClasses ()
1114 if (topmost != null)
1115 topmost.CloseTypes ();
1118 public void EmitInitScope (EmitContext ec)
1120 EmitAnonymousHelperClasses (ec);
1121 if (topmost != null)
1122 topmost.EmitInitScope (ec);
1125 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1129 si = (ScopeInfo) scopes [b.ID];
1131 throw new Exception ("Si is null for block " + b.ID);
1132 si.EmitInitScope (ec);
1138 // Emits the opcodes necessary to load the instance of the captured
1141 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1142 AnonymousContainer am)
1144 ILGenerator ig = ec.ig;
1147 if (li.Block.Toplevel == toplevel_owner){
1148 si = GetScopeFromBlock (ec, li.Block);
1149 si.EmitScopeInstance (ig);
1154 ig.Emit (OpCodes.Ldarg_0);
1156 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1160 while (si.ScopeBlock.ID != li.Block.ID){
1161 if (si.ParentLink != null)
1162 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1163 si = si.ParentScope;
1166 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1167 while (si.ScopeBlock.ID != li.Block.ID){
1168 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1169 si = si.ParentScope;
1172 throw new Exception (
1173 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1174 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1181 // Internal routine that loads the instance to reach parameter `name'
1183 void EmitParameterInstance (EmitContext ec, string name)
1185 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1187 cc.EmitParameterInstance (ec, name);
1191 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1192 if (par_info != null){
1194 // FIXME: implementing this.
1197 ILGenerator ig = ec.ig;
1201 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1202 si = GetScopeFromBlock (ec, toplevel_owner);
1203 si.EmitScopeInstance (ig);
1205 si = ec.CurrentAnonymousMethod.Scope;
1206 ig.Emit (OpCodes.Ldarg_0);
1210 while (si.ParentLink != null) {
1211 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1212 si = si.ParentScope;
1218 // Emits the code necessary to load the parameter named `name' within
1219 // an anonymous method.
1221 public void EmitParameter (EmitContext ec, string name)
1223 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1225 cc.EmitParameter (ec, name);
1228 EmitParameterInstance (ec, name);
1229 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1230 if (par_info != null){
1232 // FIXME: implementing this.
1235 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1239 // Implements the assignment of `source' to the paramenter named `name' within
1240 // an anonymous method.
1242 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1244 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1246 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1249 ILGenerator ig = ec.ig;
1250 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1252 EmitParameterInstance (ec, name);
1255 ig.Emit (OpCodes.Dup);
1256 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1260 // Emits the address for the parameter named `name' within
1261 // an anonymous method.
1263 public void EmitAddressOfParameter (EmitContext ec, string name)
1265 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1267 cc.EmitAddressOfParameter (ec, name);
1270 EmitParameterInstance (ec, name);
1271 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1272 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1276 // The following methods are only invoked on the host for the
1277 // anonymous method.
1279 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1281 ILGenerator ig = target.ig;
1282 ScopeInfo si = am.Scope;
1285 ig.Emit (OpCodes.Ldarg_0);
1289 si.EmitInitScope (target);
1290 si.EmitScopeInstance (ig);
1293 ArrayList all_scopes = new ArrayList ();
1295 public void AddScope (ScopeInfo si)
1297 all_scopes.Add (si);
1298 toplevel_owner.RegisterCaptureContext (this);
1302 // Links any scopes that were not linked previously
1304 public void AdjustScopes ()
1306 foreach (ScopeInfo scope in all_scopes){
1307 if (scope.ParentScope != null)
1310 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1311 if (scopes [b.ID] != null){
1312 LinkScope (scope, b.ID);
1317 if (scope.ParentScope == null && ParentCaptureContext != null){
1318 CaptureContext pcc = ParentCaptureContext;
1320 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1321 if (pcc.scopes [b.ID] != null){
1322 pcc.LinkScope (scope, b.ID);