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 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 if (container == null) {
83 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
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 protected abstract bool CreateMethodHost (EmitContext ec);
116 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
118 public abstract Iterator Iterator {
122 public abstract bool IsIterator {
127 public class AnonymousMethod : AnonymousContainer
129 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
130 ToplevelBlock block, Location l)
131 : base (parameters, container, block, l)
135 public override Iterator Iterator {
139 public override bool IsIterator {
140 get { return false; }
143 public override void Emit (EmitContext ec)
145 // nothing, as we only exist to not do anything.
149 // Creates the host for the anonymous method
151 protected override bool CreateMethodHost (EmitContext ec)
154 // Crude hack follows: we replace the TypeBuilder during the
155 // definition to get the method hosted in the right class
158 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
159 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
160 ? current_type : Scope.ScopeTypeBuilder;
162 if (current_type == null)
163 throw new Exception ("The current_type is null");
165 if (type_host == null)
166 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
168 if (current_type != type_host)
169 method_modifiers = Modifiers.INTERNAL;
171 if (current_type == type_host && ec.IsStatic){
172 method_modifiers |= Modifiers.STATIC;
176 string name = "<#AnonymousMethod>" + anonymous_method_count++;
177 MemberName member_name;
179 GenericMethod generic_method = null;
180 if (TypeParameters != null) {
181 TypeArguments args = new TypeArguments (loc);
182 foreach (string t in TypeParameters)
183 args.Add (new SimpleName (t, loc));
185 member_name = new MemberName (name, args, loc);
187 generic_method = new GenericMethod (
188 ec.DeclSpace.NamespaceEntry,
189 (TypeContainer) ec.TypeContainer, member_name,
190 new TypeExpression (invoke_mb.ReturnType, loc),
193 generic_method.SetParameterInfo (null);
195 member_name = new MemberName (name, loc);
197 method = new Method (
198 (TypeContainer) ec.TypeContainer, generic_method,
199 new TypeExpression (invoke_mb.ReturnType, loc),
200 method_modifiers, false, member_name, Parameters, null);
201 method.Block = Block;
204 // Swap the TypeBuilder while we define the method, then restore
206 if (current_type != null)
207 ec.TypeContainer.TypeBuilder = type_host;
208 bool res = method.Define ();
209 if (current_type != null)
210 ec.TypeContainer.TypeBuilder = current_type;
215 void Error_ParameterMismatch (Type t)
217 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
218 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
222 // Returns true if this anonymous method can be implicitly
223 // converted to the delegate type `delegate_type'
225 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
228 // At this point its the first time we know the return type that is
229 // needed for the anonymous method. We create the method here.
232 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec, delegate_type, loc);
233 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
234 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
236 if (delegate_type.IsGenericType) {
237 Type def = delegate_type.GetGenericTypeDefinition ();
239 if (def != delegate_type) {
240 Type[] tparam = TypeManager.GetTypeArguments (def);
242 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
243 TypeParameters = new string [tparam.Length];
244 for (int i = 0; i < tparam.Length; i++)
245 TypeParameters [i] = tparam [i].Name;
249 if (Parameters == null){
251 // We provide a set of inaccessible parameters
253 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
255 for (int i = 0; i < invoke_pd.Count; i++){
256 fixedpars [i] = new Parameter (
257 invoke_pd.ParameterType (i),
258 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
261 Parameters = new Parameters (fixedpars);
265 // First, parameter types of `delegate_type' must be compatible
266 // with the anonymous method.
268 Parameters.Resolve (ec);
271 if (amp.Count != invoke_pd.Count){
273 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
274 TypeManager.CSharpName (delegate_type), amp.Count.ToString ());
275 Error_ParameterMismatch (delegate_type);
280 for (int i = 0; i < amp.Count; i++){
281 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
284 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
285 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
286 (i+1).ToString (), Parameter.GetModifierSignature (amp_mod));
287 Error_ParameterMismatch (delegate_type);
291 if (amp_mod != invoke_pd.ParameterModifier (i)){
292 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
293 (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
294 Error_ParameterMismatch (delegate_type);
298 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
299 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
301 TypeManager.CSharpName (amp.ParameterType (i)),
302 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
303 Error_ParameterMismatch (delegate_type);
310 // If we are only probing, return ourselves
316 // Second: the return type of the delegate must be compatible with
317 // the anonymous type. Instead of doing a pass to examine the block
318 // we satisfy the rule by setting the return type on the EmitContext
319 // to be the delegate type return type.
322 //MethodBuilder builder = method_data.MethodBuilder;
323 //ILGenerator ig = builder.GetILGenerator ();
326 aec = new EmitContext (
327 ec.TypeContainer, ec.DeclSpace, loc, null,
328 invoke_mb.ReturnType,
329 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
330 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
331 (ec.IsStatic ? Modifiers.STATIC : 0),
332 /* No constructor */ false);
334 aec.CurrentAnonymousMethod = this;
335 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
336 ContainingBlock = ec.CurrentBlock;
338 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
339 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
344 public override string ExprClassName {
346 return "anonymous method";
350 public MethodInfo GetMethodBuilder ()
352 MethodInfo builder = method.MethodData.MethodBuilder;
353 if (TypeArguments != null)
354 return builder.MakeGenericMethod (TypeArguments);
359 public override string GetSignatureForError ()
361 string s = TypeManager.CSharpSignature (invoke_mb);
362 return s.Substring (0, s.IndexOf (".Invoke("));
365 public bool EmitMethod (EmitContext ec)
367 if (!CreateMethodHost (ec))
370 MethodBuilder builder = method.MethodData.MethodBuilder;
371 ILGenerator ig = builder.GetILGenerator ();
374 Parameters.ApplyAttributes (aec, builder);
377 // Adjust based on the computed state of the
378 // method from CreateMethodHost
380 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
382 aec.EmitMeta (Block);
383 aec.EmitResolvedTopBlock (Block, unreachable);
387 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
389 TypeBuilder container = ec.TypeContainer.TypeBuilder;
390 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
392 scope.ScopeTypeBuilder = container.DefineNestedType (
393 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
394 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
396 Type [] constructor_types = TypeManager.NoTypes;
397 ConstructorBuilder ctor = scope.ScopeTypeBuilder.DefineConstructor (
398 MethodAttributes.Public | MethodAttributes.HideBySig |
399 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
400 CallingConventions.HasThis, constructor_types);
401 TypeManager.RegisterMethod (ctor, Parameters.EmptyReadOnlyParameters);
403 ILGenerator cig = ctor.GetILGenerator ();
404 cig.Emit (OpCodes.Ldarg_0);
405 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
406 cig.Emit (OpCodes.Ret);
408 if (ec.TypeContainer.IsGeneric) {
409 TypeParameter[] tparam = ec.TypeContainer.TypeParameters;
410 string[] names = new string [tparam.Length];
411 Type[] types = new Type [tparam.Length];
413 for (int i = 0; i < names.Length; i++) {
414 names [i] = tparam [i].Name;
415 types [i] = tparam [i].Type;
418 scope.ScopeTypeBuilder.DefineGenericParameters (names);
419 scope.ScopeTypeBuilder.GetGenericTypeDefinition ();
421 scope.ScopeType = scope.ScopeTypeBuilder.MakeGenericType (types);
423 scope.ScopeType = scope.ScopeTypeBuilder;
426 if (ec.TypeContainer.IsGeneric)
427 scope.ScopeConstructor = TypeBuilder.GetConstructor (
428 scope.ScopeType, ctor);
430 scope.ScopeConstructor = ctor;
433 public static void Error_AddressOfCapturedVar (string name, Location loc)
435 Report.Error (1686, loc,
436 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
442 // This will emit the code for the delegate, as well delegate creation on the host
444 public class AnonymousDelegate : DelegateCreation {
447 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
454 public override Expression DoResolve (EmitContext ec)
456 eclass = ExprClass.Value;
461 public override void Emit (EmitContext ec)
463 if (!am.EmitMethod (ec))
467 // Now emit the delegate creation.
469 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
470 delegate_instance_expression = new AnonymousInstance (am);
472 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
473 constructor_method = ((MethodGroupExpr) ml).Methods [0];
474 delegate_method = am.GetMethodBuilder ();
478 class AnonymousInstance : Expression {
481 public AnonymousInstance (AnonymousMethod am)
484 eclass = ExprClass.Value;
487 public override Expression DoResolve (EmitContext ec)
492 public override void Emit (EmitContext ec)
494 am.aec.EmitMethodHostInstance (ec, am);
499 class CapturedParameter {
501 public FieldBuilder FieldBuilder;
504 public CapturedParameter (Type type, int idx)
512 // Here we cluster all the variables captured on a given scope, we also
513 // keep some extra information that might be required on each scope.
515 public class ScopeInfo {
516 public CaptureContext CaptureContext;
517 public ScopeInfo ParentScope;
518 public Block ScopeBlock;
519 public bool NeedThis = false;
520 public bool HostsParameters = false;
522 // For tracking the number of scopes created.
527 ArrayList locals = new ArrayList ();
528 ArrayList children = new ArrayList ();
531 // The types and fields generated
533 public TypeBuilder ScopeTypeBuilder;
534 public Type ScopeType;
535 public ConstructorInfo ScopeConstructor;
536 public FieldBuilder THIS;
537 public FieldBuilder ParentLink;
540 // Points to the object of type `ScopeTypeBuilder' that
541 // holds the data for the scope
543 LocalBuilder scope_instance;
545 public ScopeInfo (CaptureContext cc, Block b)
554 public void AddLocal (LocalInfo li)
556 if (locals.Contains (li))
562 public bool IsCaptured (LocalInfo li)
564 return locals.Contains (li);
567 internal void AddChild (ScopeInfo si)
569 if (children.Contains (si))
573 // If any of the current children should be a children of `si', move them there
575 ArrayList move_queue = null;
576 foreach (ScopeInfo child in children){
577 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
578 if (move_queue == null)
579 move_queue = new ArrayList ();
580 move_queue.Add (child);
581 child.ParentScope = si;
588 if (move_queue != null){
589 foreach (ScopeInfo child in move_queue){
590 children.Remove (child);
595 static int indent = 0;
599 for (int i = 0; i < indent; i++)
605 //Console.WriteLine (Environment.StackTrace);
607 Console.WriteLine ("START");
610 Console.WriteLine ("NeedThis=" + NeedThis);
611 foreach (LocalInfo li in locals){
613 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
616 foreach (ScopeInfo si in children)
620 Console.WriteLine ("END");
623 public string MakeHelperName ()
625 return String.Format ("<>AnonHelp<{0}>", id);
628 private string MakeFieldName (string local_name)
630 return "<" + id + ":" + local_name + ">";
633 public void EmitScopeType (EmitContext ec)
637 if (ScopeTypeBuilder != null)
640 TypeBuilder container = ec.TypeContainer.TypeBuilder;
642 CaptureContext.Host.CreateScopeType (ec, this);
645 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
647 if (ParentScope != null){
648 if (ParentScope.ScopeTypeBuilder == null){
649 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
652 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
653 ParentLink = ScopeTypeBuilder.DefineField (
654 "<>parent", ParentScope.ScopeType, FieldAttributes.Assembly);
657 if (NeedThis && ParentScope != null)
658 throw new Exception ("I was not expecting THIS && having a parent");
660 foreach (LocalInfo info in locals)
661 info.FieldBuilder = ScopeTypeBuilder.DefineField (
662 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
664 if (HostsParameters){
665 Hashtable captured_parameters = CaptureContext.captured_parameters;
667 foreach (DictionaryEntry de in captured_parameters){
668 string name = (string) de.Key;
669 CapturedParameter cp = (CapturedParameter) de.Value;
672 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
673 cp.FieldBuilder = fb;
677 foreach (ScopeInfo si in children){
678 si.EmitScopeType (ec);
682 public void CloseTypes ()
684 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
685 foreach (ScopeInfo si in children)
690 // Emits the initialization code for the scope
692 public void EmitInitScope (EmitContext ec)
694 ILGenerator ig = ec.ig;
699 if (ScopeConstructor == null)
700 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
702 if (!CaptureContext.Host.IsIterator) {
703 scope_instance = ig.DeclareLocal (ScopeType);
704 ig.Emit (OpCodes.Newobj, ScopeConstructor);
705 ig.Emit (OpCodes.Stloc, scope_instance);
709 if (CaptureContext.Host.IsIterator) {
710 ig.Emit (OpCodes.Ldarg_0);
711 ig.Emit (OpCodes.Ldarg_1);
713 ig.Emit (OpCodes.Ldloc, scope_instance);
714 ig.Emit (OpCodes.Ldarg_0);
716 ig.Emit (OpCodes.Stfld, THIS);
720 // Copy the parameter values, if any
722 int extra = ec.IsStatic ? 0 : 1;
723 if (CaptureContext.Host.IsIterator)
725 if (HostsParameters){
726 Hashtable captured_parameters = CaptureContext.captured_parameters;
728 foreach (DictionaryEntry de in captured_parameters){
729 CapturedParameter cp = (CapturedParameter) de.Value;
731 EmitScopeInstance (ig);
732 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
733 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
737 if (ParentScope != null){
738 if (!ParentScope.inited)
739 ParentScope.EmitInitScope (ec);
741 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
743 // Only emit initialization in our capturecontext world
745 if (ParentScope.CaptureContext == CaptureContext){
746 EmitScopeInstance (ig);
747 ParentScope.EmitScopeInstance (ig);
748 ig.Emit (OpCodes.Stfld, ParentLink);
750 EmitScopeInstance (ig);
751 ig.Emit (OpCodes.Ldarg_0);
752 ig.Emit (OpCodes.Stfld, ParentLink);
759 public void EmitScopeInstance (ILGenerator ig)
761 if (CaptureContext.Host.IsIterator)
762 ig.Emit (OpCodes.Ldarg_0);
764 ig.Emit (OpCodes.Ldloc, scope_instance);
767 public static void CheckCycles (string msg, ScopeInfo s)
769 ArrayList l = new ArrayList ();
772 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
774 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
775 throw new Exception ();
781 static void DoPath (StringBuilder sb, ScopeInfo start)
783 CheckCycles ("print", start);
785 if (start.ParentScope != null){
786 DoPath (sb, start.ParentScope);
789 sb.Append ((start.id).ToString ());
792 public override string ToString ()
794 StringBuilder sb = new StringBuilder ();
797 if (CaptureContext != null){
798 sb.Append (CaptureContext.ToString ());
805 return sb.ToString ();
810 // CaptureContext objects are created on demand if a method has
811 // anonymous methods and kept on the ToplevelBlock.
813 // If they exist, all ToplevelBlocks in the containing block are
814 // linked together (children pointing to their parents).
816 public class CaptureContext {
817 public static int count;
822 // Points to the toplevel block that owns this CaptureContext
824 ToplevelBlock toplevel_owner;
825 Hashtable scopes = new Hashtable ();
826 bool have_captured_vars = false;
827 bool referenced_this = false;
828 ScopeInfo topmost = null;
833 Hashtable captured_fields = new Hashtable ();
834 Hashtable captured_variables = new Hashtable ();
835 public Hashtable captured_parameters = new Hashtable ();
836 public AnonymousContainer Host;
838 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
839 AnonymousContainer host)
842 this.toplevel_owner = toplevel_owner;
849 void DoPath (StringBuilder sb, CaptureContext cc)
851 if (cc.ParentCaptureContext != null){
852 DoPath (sb, cc.ParentCaptureContext);
855 sb.Append (cc.cc_id.ToString ());
858 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
860 toplevel_owner = new_toplevel;
863 for (CaptureContext cc = ParentCaptureContext; cc != null;
864 cc = cc.ParentCaptureContext) {
869 public override string ToString ()
871 StringBuilder sb = new StringBuilder ();
875 return sb.ToString ();
878 public ToplevelBlock ParentToplevel {
880 return toplevel_owner.Container;
884 public CaptureContext ParentCaptureContext {
886 ToplevelBlock parent = ParentToplevel;
888 return (parent == null) ? null : parent.CaptureContext;
892 // Returns the deepest of two scopes
893 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
905 // If they Scopes are on the same CaptureContext, we do the double
906 // checks just so if there is an invariant change in the future,
907 // we get the exception at the end
909 for (p = a; p != null; p = p.ParentScope)
913 for (p = b; p != null; p = p.ParentScope)
917 CaptureContext ca = a.CaptureContext;
918 CaptureContext cb = b.CaptureContext;
920 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
924 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
927 throw new Exception ("Should never be reached");
930 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
932 am.Scope = Deepest (am.Scope, scope);
935 void LinkScope (ScopeInfo scope, int id)
937 ScopeInfo parent = (ScopeInfo) scopes [id];
941 scope.ParentScope = parent;
942 parent.AddChild (scope);
944 if (scope == topmost)
948 public void AddLocal (AnonymousContainer am, LocalInfo li)
950 if (li.Block.Toplevel != toplevel_owner){
951 ParentCaptureContext.AddLocal (am, li);
954 int block_id = li.Block.ID;
956 if (scopes [block_id] == null){
957 scope = new ScopeInfo (this, li.Block);
958 scopes [block_id] = scope;
960 scope = (ScopeInfo) scopes [block_id];
962 if (topmost == null){
967 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
968 if (scopes [b.ID] != null){
969 LinkScope (scope, b.ID);
974 if (scope.ParentScope == null && ParentCaptureContext != null){
975 CaptureContext pcc = ParentCaptureContext;
977 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
978 if (pcc.scopes [b.ID] != null){
979 pcc.LinkScope (scope, b.ID);
990 AdjustMethodScope (Host, topmost);
995 AdjustMethodScope (am, scope);
997 if (captured_variables [li] != null)
1000 have_captured_vars = true;
1001 captured_variables [li] = li;
1002 scope.AddLocal (li);
1006 // Retursn the CaptureContext for the block that defines the parameter `name'
1008 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
1010 ToplevelBlock container = current.Container;
1011 if (container != null){
1012 CaptureContext cc = _ContextForParameter (container, name);
1016 if (current.IsParameterReference (name))
1017 return current.ToplevelBlockCaptureContext;
1021 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1023 CaptureContext cc = _ContextForParameter (current, name);
1025 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1030 // Records the captured parameter at the appropriate CaptureContext
1032 public void AddParameter (EmitContext ec, AnonymousContainer am,
1033 string name, Type t, int idx)
1035 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1037 cc.AddParameterToContext (am, name, t, idx);
1041 // Records the parameters in the context
1043 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1045 if (captured_parameters == null)
1046 captured_parameters = new Hashtable ();
1047 if (captured_parameters [name] == null)
1048 captured_parameters [name] = new CapturedParameter (t, idx);
1050 if (topmost == null){
1052 // Create one ScopeInfo, if there are none.
1054 topmost = new ScopeInfo (this, toplevel_owner);
1055 scopes [toplevel_owner.ID] = topmost;
1058 // If the topmost ScopeInfo is not at the topblock level, insert
1059 // a new ScopeInfo there.
1061 // FIXME: This code probably should be evolved to be like the code
1064 if (topmost.ScopeBlock != toplevel_owner){
1065 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
1066 ScopeInfo old_top = topmost;
1067 scopes [toplevel_owner.ID] = topmost;
1068 topmost.ParentScope = par_si;
1070 topmost.AddChild (old_top);
1074 topmost.HostsParameters = true;
1075 AdjustMethodScope (am, topmost);
1079 // Captured fields are only recorded on the topmost CaptureContext, because that
1080 // one is the one linked to the owner of instance fields
1082 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1084 if (fe.FieldInfo.IsStatic)
1085 throw new Exception ("Attempt to register a static field as a captured field");
1086 CaptureContext parent = ParentCaptureContext;
1087 if (parent != null) {
1088 parent.AddField (ec, am, fe);
1092 if (topmost == null){
1094 // Create one ScopeInfo, if there are none.
1096 topmost = new ScopeInfo (this, toplevel_owner);
1097 scopes [toplevel_owner.ID] = topmost;
1100 AdjustMethodScope (am, topmost);
1103 public void CaptureThis (AnonymousContainer am)
1105 CaptureContext parent = ParentCaptureContext;
1106 if (parent != null) {
1107 parent.CaptureThis (am);
1110 referenced_this = true;
1112 if (topmost == null){
1114 // Create one ScopeInfo, if there are none.
1116 topmost = new ScopeInfo (this, toplevel_owner);
1117 scopes [toplevel_owner.ID] = topmost;
1119 AdjustMethodScope (am, topmost);
1123 public bool HaveCapturedVariables {
1125 return have_captured_vars;
1129 public bool HaveCapturedFields {
1131 CaptureContext parent = ParentCaptureContext;
1133 return parent.HaveCapturedFields;
1134 return captured_fields.Count > 0;
1138 public bool IsCaptured (LocalInfo local)
1140 foreach (ScopeInfo si in scopes.Values){
1141 if (si.IsCaptured (local))
1148 // Returns whether the parameter is captured
1150 public bool IsParameterCaptured (string name)
1152 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1155 if (captured_parameters != null)
1156 return captured_parameters [name] != null;
1160 public void EmitAnonymousHelperClasses (EmitContext ec)
1162 if (topmost != null){
1163 topmost.NeedThis = HaveCapturedFields || referenced_this;
1164 topmost.EmitScopeType (ec);
1168 public void CloseAnonymousHelperClasses ()
1170 if (topmost != null)
1171 topmost.CloseTypes ();
1174 public void EmitInitScope (EmitContext ec)
1176 EmitAnonymousHelperClasses (ec);
1177 if (topmost != null)
1178 topmost.EmitInitScope (ec);
1181 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1185 si = (ScopeInfo) scopes [b.ID];
1187 throw new Exception ("Si is null for block " + b.ID);
1188 si.EmitInitScope (ec);
1194 // Emits the opcodes necessary to load the instance of the captured
1197 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1198 AnonymousContainer am)
1200 ILGenerator ig = ec.ig;
1203 if (li.Block.Toplevel == toplevel_owner){
1204 si = GetScopeFromBlock (ec, li.Block);
1205 si.EmitScopeInstance (ig);
1210 ig.Emit (OpCodes.Ldarg_0);
1212 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1216 while (si.ScopeBlock.ID != li.Block.ID){
1217 if (si.ParentLink != null)
1218 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1219 si = si.ParentScope;
1222 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1223 while (si.ScopeBlock.ID != li.Block.ID){
1224 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1225 si = si.ParentScope;
1228 throw new Exception (
1229 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1230 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1237 // Internal routine that loads the instance to reach parameter `name'
1239 void EmitParameterInstance (EmitContext ec, string name)
1241 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1243 cc.EmitParameterInstance (ec, name);
1247 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1248 if (par_info != null){
1250 // FIXME: implementing this.
1253 ILGenerator ig = ec.ig;
1257 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1258 si = GetScopeFromBlock (ec, toplevel_owner);
1259 si.EmitScopeInstance (ig);
1261 si = ec.CurrentAnonymousMethod.Scope;
1262 ig.Emit (OpCodes.Ldarg_0);
1266 while (si.ParentLink != null) {
1267 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1268 si = si.ParentScope;
1274 // Emits the code necessary to load the parameter named `name' within
1275 // an anonymous method.
1277 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1279 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1281 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1285 EmitParameterInstance (ec, name);
1286 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1287 if (par_info != null){
1289 // FIXME: implementing this.
1292 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1295 ec.ig.Emit (OpCodes.Dup);
1296 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1302 // Implements the assignment of `source' to the paramenter named `name' within
1303 // an anonymous method.
1305 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1307 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1309 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1312 ILGenerator ig = ec.ig;
1313 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1315 EmitParameterInstance (ec, name);
1316 if (prepare_for_load)
1317 ig.Emit (OpCodes.Dup);
1320 ig.Emit (OpCodes.Dup);
1321 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1324 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1330 // Emits the address for the parameter named `name' within
1331 // an anonymous method.
1333 public void EmitAddressOfParameter (EmitContext ec, string name)
1335 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1337 cc.EmitAddressOfParameter (ec, name);
1340 EmitParameterInstance (ec, name);
1341 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1342 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1346 // The following methods are only invoked on the host for the
1347 // anonymous method.
1349 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1351 ILGenerator ig = target.ig;
1352 ScopeInfo si = am.Scope;
1354 AnonymousContainer container = am.ContainerAnonymousMethod;
1356 if ((si == null) || ((container != null) && (si == container.Scope))) {
1357 ig.Emit (OpCodes.Ldarg_0);
1361 si.EmitInitScope (target);
1362 si.EmitScopeInstance (ig);
1365 ArrayList all_scopes = new ArrayList ();
1367 public void AddScope (ScopeInfo si)
1369 all_scopes.Add (si);
1370 toplevel_owner.RegisterCaptureContext (this);
1374 // Links any scopes that were not linked previously
1376 public void AdjustScopes ()
1378 foreach (ScopeInfo scope in all_scopes){
1379 if (scope.ParentScope != null)
1382 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1383 if (scopes [b.ID] != null){
1384 LinkScope (scope, b.ID);
1389 if (scope.ParentScope == null && ParentCaptureContext != null){
1390 CaptureContext pcc = ParentCaptureContext;
1392 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1393 if (pcc.scopes [b.ID] != null){
1394 pcc.LinkScope (scope, b.ID);