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 != invoke_pd.ParameterModifier (i)){
285 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
286 (i+1).ToString (), Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
287 Error_ParameterMismatch (delegate_type);
291 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
292 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
294 TypeManager.CSharpName (amp.ParameterType (i)),
295 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
296 Error_ParameterMismatch (delegate_type);
303 // If we are only probing, return ourselves
309 // Second: the return type of the delegate must be compatible with
310 // the anonymous type. Instead of doing a pass to examine the block
311 // we satisfy the rule by setting the return type on the EmitContext
312 // to be the delegate type return type.
315 //MethodBuilder builder = method_data.MethodBuilder;
316 //ILGenerator ig = builder.GetILGenerator ();
319 aec = new EmitContext (
320 ec.TypeContainer, ec.DeclSpace, loc, null,
321 invoke_mb.ReturnType,
322 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
323 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
324 (ec.IsStatic ? Modifiers.STATIC : 0),
325 /* No constructor */ false);
327 aec.CurrentAnonymousMethod = this;
328 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
329 ContainingBlock = ec.CurrentBlock;
331 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
332 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
337 public override string ExprClassName {
339 return "anonymous method";
343 public MethodInfo GetMethodBuilder ()
345 MethodInfo builder = method.MethodData.MethodBuilder;
346 if (TypeArguments != null)
347 return builder.MakeGenericMethod (TypeArguments);
352 public override string GetSignatureForError ()
354 string s = TypeManager.CSharpSignature (invoke_mb);
355 return s.Substring (0, s.IndexOf (".Invoke("));
358 public bool EmitMethod (EmitContext ec)
360 if (!CreateMethodHost (ec))
363 MethodBuilder builder = method.MethodData.MethodBuilder;
364 ILGenerator ig = builder.GetILGenerator ();
367 Parameters.ApplyAttributes (aec, builder);
370 // Adjust based on the computed state of the
371 // method from CreateMethodHost
373 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
375 aec.EmitMeta (Block);
376 aec.EmitResolvedTopBlock (Block, unreachable);
380 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
382 TypeBuilder container = ec.TypeContainer.TypeBuilder;
383 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
385 scope.ScopeTypeBuilder = container.DefineNestedType (
386 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
387 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
389 Type [] constructor_types = TypeManager.NoTypes;
390 ConstructorBuilder ctor = scope.ScopeTypeBuilder.DefineConstructor (
391 MethodAttributes.Public | MethodAttributes.HideBySig |
392 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
393 CallingConventions.HasThis, constructor_types);
394 TypeManager.RegisterMethod (ctor, Parameters.EmptyReadOnlyParameters);
396 ILGenerator cig = ctor.GetILGenerator ();
397 cig.Emit (OpCodes.Ldarg_0);
398 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
399 cig.Emit (OpCodes.Ret);
401 if (ec.TypeContainer.IsGeneric) {
402 TypeParameter[] tparam = ec.TypeContainer.TypeParameters;
403 string[] names = new string [tparam.Length];
404 Type[] types = new Type [tparam.Length];
406 for (int i = 0; i < names.Length; i++) {
407 names [i] = tparam [i].Name;
408 types [i] = tparam [i].Type;
411 scope.ScopeTypeBuilder.DefineGenericParameters (names);
412 scope.ScopeTypeBuilder.GetGenericTypeDefinition ();
414 scope.ScopeType = scope.ScopeTypeBuilder.MakeGenericType (types);
416 scope.ScopeType = scope.ScopeTypeBuilder;
419 if (ec.TypeContainer.IsGeneric)
420 scope.ScopeConstructor = TypeBuilder.GetConstructor (
421 scope.ScopeType, ctor);
423 scope.ScopeConstructor = ctor;
426 public static void Error_AddressOfCapturedVar (string name, Location loc)
428 Report.Error (1686, loc,
429 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
435 // This will emit the code for the delegate, as well delegate creation on the host
437 public class AnonymousDelegate : DelegateCreation {
440 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
447 public override Expression DoResolve (EmitContext ec)
449 eclass = ExprClass.Value;
454 public override void Emit (EmitContext ec)
456 if (!am.EmitMethod (ec))
460 // Now emit the delegate creation.
462 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
463 delegate_instance_expression = new AnonymousInstance (am);
465 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
466 constructor_method = ((MethodGroupExpr) ml).Methods [0];
467 delegate_method = am.GetMethodBuilder ();
471 class AnonymousInstance : Expression {
474 public AnonymousInstance (AnonymousMethod am)
477 eclass = ExprClass.Value;
480 public override Expression DoResolve (EmitContext ec)
485 public override void Emit (EmitContext ec)
487 am.aec.EmitMethodHostInstance (ec, am);
492 class CapturedParameter {
494 public FieldBuilder FieldBuilder;
497 public CapturedParameter (Type type, int idx)
505 // Here we cluster all the variables captured on a given scope, we also
506 // keep some extra information that might be required on each scope.
508 public class ScopeInfo {
509 public CaptureContext CaptureContext;
510 public ScopeInfo ParentScope;
511 public Block ScopeBlock;
512 public bool NeedThis = false;
513 public bool HostsParameters = false;
515 // For tracking the number of scopes created.
520 ArrayList locals = new ArrayList ();
521 ArrayList children = new ArrayList ();
524 // The types and fields generated
526 public TypeBuilder ScopeTypeBuilder;
527 public Type ScopeType;
528 public ConstructorInfo ScopeConstructor;
529 public FieldBuilder THIS;
530 public FieldBuilder ParentLink;
533 // Points to the object of type `ScopeTypeBuilder' that
534 // holds the data for the scope
536 LocalBuilder scope_instance;
538 public ScopeInfo (CaptureContext cc, Block b)
547 public void AddLocal (LocalInfo li)
549 if (locals.Contains (li))
555 public bool IsCaptured (LocalInfo li)
557 return locals.Contains (li);
560 internal void AddChild (ScopeInfo si)
562 if (children.Contains (si))
566 // If any of the current children should be a children of `si', move them there
568 ArrayList move_queue = null;
569 foreach (ScopeInfo child in children){
570 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
571 if (move_queue == null)
572 move_queue = new ArrayList ();
573 move_queue.Add (child);
574 child.ParentScope = si;
581 if (move_queue != null){
582 foreach (ScopeInfo child in move_queue){
583 children.Remove (child);
588 static int indent = 0;
592 for (int i = 0; i < indent; i++)
598 //Console.WriteLine (Environment.StackTrace);
600 Console.WriteLine ("START");
603 Console.WriteLine ("NeedThis=" + NeedThis);
604 foreach (LocalInfo li in locals){
606 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
609 foreach (ScopeInfo si in children)
613 Console.WriteLine ("END");
616 public string MakeHelperName ()
618 return String.Format ("<>AnonHelp<{0}>", id);
621 private string MakeFieldName (string local_name)
623 return "<" + id + ":" + local_name + ">";
626 public void EmitScopeType (EmitContext ec)
630 if (ScopeTypeBuilder != null)
634 if (ec.TypeContainer.CurrentType != null)
635 container = ec.TypeContainer.CurrentType;
637 container = ec.TypeContainer.TypeBuilder;
639 CaptureContext.Host.CreateScopeType (ec, this);
642 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
644 if (ParentScope != null){
645 if (ParentScope.ScopeTypeBuilder == null){
646 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
649 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
650 ParentLink = ScopeTypeBuilder.DefineField (
651 "<>parent", ParentScope.ScopeType, FieldAttributes.Assembly);
654 if (NeedThis && ParentScope != null)
655 throw new Exception ("I was not expecting THIS && having a parent");
657 foreach (LocalInfo info in locals)
658 info.FieldBuilder = ScopeTypeBuilder.DefineField (
659 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
661 if (HostsParameters){
662 Hashtable captured_parameters = CaptureContext.captured_parameters;
664 foreach (DictionaryEntry de in captured_parameters){
665 string name = (string) de.Key;
666 CapturedParameter cp = (CapturedParameter) de.Value;
669 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
670 cp.FieldBuilder = fb;
674 foreach (ScopeInfo si in children){
675 si.EmitScopeType (ec);
679 public void CloseTypes ()
681 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
682 foreach (ScopeInfo si in children)
687 // Emits the initialization code for the scope
689 public void EmitInitScope (EmitContext ec)
691 ILGenerator ig = ec.ig;
696 if (ScopeConstructor == null)
697 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
699 if (!CaptureContext.Host.IsIterator) {
700 scope_instance = ig.DeclareLocal (ScopeType);
701 ig.Emit (OpCodes.Newobj, ScopeConstructor);
702 ig.Emit (OpCodes.Stloc, scope_instance);
706 if (CaptureContext.Host.IsIterator) {
707 ig.Emit (OpCodes.Ldarg_0);
708 ig.Emit (OpCodes.Ldarg_1);
710 ig.Emit (OpCodes.Ldloc, scope_instance);
711 ig.Emit (OpCodes.Ldarg_0);
713 ig.Emit (OpCodes.Stfld, THIS);
717 // Copy the parameter values, if any
719 int extra = ec.IsStatic ? 0 : 1;
720 if (CaptureContext.Host.IsIterator)
722 if (HostsParameters){
723 Hashtable captured_parameters = CaptureContext.captured_parameters;
725 foreach (DictionaryEntry de in captured_parameters){
726 CapturedParameter cp = (CapturedParameter) de.Value;
728 EmitScopeInstance (ig);
729 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
730 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
734 if (ParentScope != null){
735 if (!ParentScope.inited)
736 ParentScope.EmitInitScope (ec);
738 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
740 // Only emit initialization in our capturecontext world
742 if (ParentScope.CaptureContext == CaptureContext){
743 EmitScopeInstance (ig);
744 ParentScope.EmitScopeInstance (ig);
745 ig.Emit (OpCodes.Stfld, ParentLink);
747 EmitScopeInstance (ig);
748 ig.Emit (OpCodes.Ldarg_0);
749 ig.Emit (OpCodes.Stfld, ParentLink);
756 public void EmitScopeInstance (ILGenerator ig)
758 if (CaptureContext.Host.IsIterator)
759 ig.Emit (OpCodes.Ldarg_0);
761 ig.Emit (OpCodes.Ldloc, scope_instance);
764 public static void CheckCycles (string msg, ScopeInfo s)
766 ArrayList l = new ArrayList ();
769 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
771 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
772 throw new Exception ();
778 static void DoPath (StringBuilder sb, ScopeInfo start)
780 CheckCycles ("print", start);
782 if (start.ParentScope != null){
783 DoPath (sb, start.ParentScope);
786 sb.Append ((start.id).ToString ());
789 public override string ToString ()
791 StringBuilder sb = new StringBuilder ();
794 if (CaptureContext != null){
795 sb.Append (CaptureContext.ToString ());
802 return sb.ToString ();
807 // CaptureContext objects are created on demand if a method has
808 // anonymous methods and kept on the ToplevelBlock.
810 // If they exist, all ToplevelBlocks in the containing block are
811 // linked together (children pointing to their parents).
813 public class CaptureContext {
814 public static int count;
819 // Points to the toplevel block that owns this CaptureContext
821 ToplevelBlock toplevel_owner;
822 Hashtable scopes = new Hashtable ();
823 bool have_captured_vars = false;
824 bool referenced_this = false;
825 ScopeInfo topmost = null;
830 Hashtable captured_fields = new Hashtable ();
831 Hashtable captured_variables = new Hashtable ();
832 public Hashtable captured_parameters = new Hashtable ();
833 public AnonymousContainer Host;
835 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
836 AnonymousContainer host)
839 this.toplevel_owner = toplevel_owner;
846 void DoPath (StringBuilder sb, CaptureContext cc)
848 if (cc.ParentCaptureContext != null){
849 DoPath (sb, cc.ParentCaptureContext);
852 sb.Append (cc.cc_id.ToString ());
855 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
857 toplevel_owner = new_toplevel;
860 for (CaptureContext cc = ParentCaptureContext; cc != null;
861 cc = cc.ParentCaptureContext) {
866 public override string ToString ()
868 StringBuilder sb = new StringBuilder ();
872 return sb.ToString ();
875 public ToplevelBlock ParentToplevel {
877 return toplevel_owner.Container;
881 public CaptureContext ParentCaptureContext {
883 ToplevelBlock parent = ParentToplevel;
885 return (parent == null) ? null : parent.CaptureContext;
889 // Returns the deepest of two scopes
890 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
902 // If they Scopes are on the same CaptureContext, we do the double
903 // checks just so if there is an invariant change in the future,
904 // we get the exception at the end
906 for (p = a; p != null; p = p.ParentScope)
910 for (p = b; p != null; p = p.ParentScope)
914 CaptureContext ca = a.CaptureContext;
915 CaptureContext cb = b.CaptureContext;
917 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
921 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
924 throw new Exception ("Should never be reached");
927 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
929 am.Scope = Deepest (am.Scope, scope);
932 void LinkScope (ScopeInfo scope, int id)
934 ScopeInfo parent = (ScopeInfo) scopes [id];
938 scope.ParentScope = parent;
939 parent.AddChild (scope);
941 if (scope == topmost)
945 public void AddLocal (AnonymousContainer am, LocalInfo li)
947 if (li.Block.Toplevel != toplevel_owner){
948 ParentCaptureContext.AddLocal (am, li);
951 int block_id = li.Block.ID;
953 if (scopes [block_id] == null){
954 scope = new ScopeInfo (this, li.Block);
955 scopes [block_id] = scope;
957 scope = (ScopeInfo) scopes [block_id];
959 if (topmost == null){
964 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
965 if (scopes [b.ID] != null){
966 LinkScope (scope, b.ID);
971 if (scope.ParentScope == null && ParentCaptureContext != null){
972 CaptureContext pcc = ParentCaptureContext;
974 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
975 if (pcc.scopes [b.ID] != null){
976 pcc.LinkScope (scope, b.ID);
987 AdjustMethodScope (Host, topmost);
992 AdjustMethodScope (am, scope);
994 if (captured_variables [li] != null)
997 have_captured_vars = true;
998 captured_variables [li] = li;
1003 // Retursn the CaptureContext for the block that defines the parameter `name'
1005 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
1007 ToplevelBlock container = current.Container;
1008 if (container != null){
1009 CaptureContext cc = _ContextForParameter (container, name);
1013 if (current.IsParameterReference (name))
1014 return current.ToplevelBlockCaptureContext;
1018 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1020 CaptureContext cc = _ContextForParameter (current, name);
1022 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1027 // Records the captured parameter at the appropriate CaptureContext
1029 public void AddParameter (EmitContext ec, AnonymousContainer am,
1030 string name, Type t, int idx)
1032 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1034 cc.AddParameterToContext (am, name, t, idx);
1038 // Records the parameters in the context
1040 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1042 if (captured_parameters == null)
1043 captured_parameters = new Hashtable ();
1044 if (captured_parameters [name] == null)
1045 captured_parameters [name] = new CapturedParameter (t, idx);
1047 if (topmost == null){
1049 // Create one ScopeInfo, if there are none.
1051 topmost = new ScopeInfo (this, toplevel_owner);
1052 scopes [toplevel_owner.ID] = topmost;
1055 // If the topmost ScopeInfo is not at the topblock level, insert
1056 // a new ScopeInfo there.
1058 // FIXME: This code probably should be evolved to be like the code
1061 if (topmost.ScopeBlock != toplevel_owner){
1062 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
1063 ScopeInfo old_top = topmost;
1064 scopes [toplevel_owner.ID] = topmost;
1065 topmost.ParentScope = par_si;
1067 topmost.AddChild (old_top);
1071 topmost.HostsParameters = true;
1072 AdjustMethodScope (am, topmost);
1076 // Captured fields are only recorded on the topmost CaptureContext, because that
1077 // one is the one linked to the owner of instance fields
1079 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1081 if (fe.FieldInfo.IsStatic)
1082 throw new Exception ("Attempt to register a static field as a captured field");
1083 CaptureContext parent = ParentCaptureContext;
1084 if (parent != null) {
1085 parent.AddField (ec, am, fe);
1089 if (topmost == null){
1091 // Create one ScopeInfo, if there are none.
1093 topmost = new ScopeInfo (this, toplevel_owner);
1094 scopes [toplevel_owner.ID] = topmost;
1097 AdjustMethodScope (am, topmost);
1100 public void CaptureThis (AnonymousContainer am)
1102 CaptureContext parent = ParentCaptureContext;
1103 if (parent != null) {
1104 parent.CaptureThis (am);
1107 referenced_this = true;
1109 if (topmost == null){
1111 // Create one ScopeInfo, if there are none.
1113 topmost = new ScopeInfo (this, toplevel_owner);
1114 scopes [toplevel_owner.ID] = topmost;
1116 AdjustMethodScope (am, topmost);
1120 public bool HaveCapturedVariables {
1122 return have_captured_vars;
1126 public bool HaveCapturedFields {
1128 CaptureContext parent = ParentCaptureContext;
1130 return parent.HaveCapturedFields;
1131 return captured_fields.Count > 0;
1135 public bool IsCaptured (LocalInfo local)
1137 foreach (ScopeInfo si in scopes.Values){
1138 if (si.IsCaptured (local))
1145 // Returns whether the parameter is captured
1147 public bool IsParameterCaptured (string name)
1149 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1152 if (captured_parameters != null)
1153 return captured_parameters [name] != null;
1157 public void EmitAnonymousHelperClasses (EmitContext ec)
1159 if (topmost != null){
1160 topmost.NeedThis = HaveCapturedFields || referenced_this;
1161 topmost.EmitScopeType (ec);
1165 public void CloseAnonymousHelperClasses ()
1167 if (topmost != null)
1168 topmost.CloseTypes ();
1171 public void EmitInitScope (EmitContext ec)
1173 EmitAnonymousHelperClasses (ec);
1174 if (topmost != null)
1175 topmost.EmitInitScope (ec);
1178 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1182 si = (ScopeInfo) scopes [b.ID];
1184 throw new Exception ("Si is null for block " + b.ID);
1185 si.EmitInitScope (ec);
1191 // Emits the opcodes necessary to load the instance of the captured
1194 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1195 AnonymousContainer am)
1197 ILGenerator ig = ec.ig;
1200 if (li.Block.Toplevel == toplevel_owner){
1201 si = GetScopeFromBlock (ec, li.Block);
1202 si.EmitScopeInstance (ig);
1207 ig.Emit (OpCodes.Ldarg_0);
1209 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1213 while (si.ScopeBlock.ID != li.Block.ID){
1214 if (si.ParentLink != null)
1215 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1216 si = si.ParentScope;
1219 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1220 while (si.ScopeBlock.ID != li.Block.ID){
1221 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1222 si = si.ParentScope;
1225 throw new Exception (
1226 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1227 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1234 // Internal routine that loads the instance to reach parameter `name'
1236 void EmitParameterInstance (EmitContext ec, string name)
1238 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1240 cc.EmitParameterInstance (ec, name);
1244 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1245 if (par_info != null){
1247 // FIXME: implementing this.
1250 ILGenerator ig = ec.ig;
1254 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1255 si = GetScopeFromBlock (ec, toplevel_owner);
1256 si.EmitScopeInstance (ig);
1258 si = ec.CurrentAnonymousMethod.Scope;
1259 ig.Emit (OpCodes.Ldarg_0);
1263 while (si.ParentLink != null) {
1264 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1265 si = si.ParentScope;
1271 // Emits the code necessary to load the parameter named `name' within
1272 // an anonymous method.
1274 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1276 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1278 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1282 EmitParameterInstance (ec, name);
1283 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1284 if (par_info != null){
1286 // FIXME: implementing this.
1289 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1292 ec.ig.Emit (OpCodes.Dup);
1293 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1299 // Implements the assignment of `source' to the paramenter named `name' within
1300 // an anonymous method.
1302 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1304 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1306 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1309 ILGenerator ig = ec.ig;
1310 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1312 EmitParameterInstance (ec, name);
1313 if (prepare_for_load)
1314 ig.Emit (OpCodes.Dup);
1317 ig.Emit (OpCodes.Dup);
1318 temp = new LocalTemporary (ec, par_info.FieldBuilder.FieldType);
1321 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1327 // Emits the address for the parameter named `name' within
1328 // an anonymous method.
1330 public void EmitAddressOfParameter (EmitContext ec, string name)
1332 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1334 cc.EmitAddressOfParameter (ec, name);
1337 EmitParameterInstance (ec, name);
1338 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1339 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1343 // The following methods are only invoked on the host for the
1344 // anonymous method.
1346 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1348 ILGenerator ig = target.ig;
1349 ScopeInfo si = am.Scope;
1351 AnonymousContainer container = am.ContainerAnonymousMethod;
1353 if ((si == null) || ((container != null) && (si == container.Scope))) {
1354 ig.Emit (OpCodes.Ldarg_0);
1358 si.EmitInitScope (target);
1359 si.EmitScopeInstance (ig);
1362 ArrayList all_scopes = new ArrayList ();
1364 public void AddScope (ScopeInfo si)
1366 all_scopes.Add (si);
1367 toplevel_owner.RegisterCaptureContext (this);
1371 // Links any scopes that were not linked previously
1373 public void AdjustScopes ()
1375 foreach (ScopeInfo scope in all_scopes){
1376 if (scope.ParentScope != null)
1379 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1380 if (scopes [b.ID] != null){
1381 LinkScope (scope, b.ID);
1386 if (scope.ParentScope == null && ParentCaptureContext != null){
1387 CaptureContext pcc = ParentCaptureContext;
1389 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1390 if (pcc.scopes [b.ID] != null){
1391 pcc.LinkScope (scope, b.ID);