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 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 container.SetHaveAnonymousMethods (l, this);
81 block.SetHaveAnonymousMethods (l, this);
84 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
87 Parameters = parameters;
88 Block = new ToplevelBlock (container, Parameters, l);
92 // The order is important: this setups the CaptureContext tree hierarchy.
94 container.SetHaveAnonymousMethods (loc, this);
95 Block.SetHaveAnonymousMethods (loc, this);
98 public override Expression DoResolve (EmitContext ec)
101 // Set class type, set type
104 eclass = ExprClass.Value;
107 // This hack means `The type is not accessible
108 // anywhere', we depend on special conversion
111 type = TypeManager.anonymous_method_type;
116 protected abstract bool CreateMethodHost (EmitContext ec);
118 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
120 public abstract bool IsIterator {
125 public class AnonymousMethod : AnonymousContainer
127 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
128 ToplevelBlock block, Location l)
129 : base (parameters, container, block, l)
133 public override bool IsIterator {
134 get { return false; }
137 public override void Emit (EmitContext ec)
139 // nothing, as we only exist to not do anything.
143 // Creates the host for the anonymous method
145 protected override bool CreateMethodHost (EmitContext ec)
148 // Crude hack follows: we replace the TypeBuilder during the
149 // definition to get the method hosted in the right class
152 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
153 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
154 ? current_type : Scope.ScopeTypeBuilder;
156 if (current_type == null)
157 throw new Exception ("The current_type is null");
159 if (type_host == null)
160 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
162 if (current_type != type_host)
163 method_modifiers = Modifiers.INTERNAL;
165 if (current_type == type_host && ec.IsStatic){
167 method_modifiers |= Modifiers.STATIC;
172 method = new Method (
173 (TypeContainer) ec.TypeContainer,
174 new TypeExpression (invoke_mb.ReturnType, loc),
175 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
177 method.Block = Block;
180 // Swap the TypeBuilder while we define the method, then restore
182 if (current_type != null)
183 ec.TypeContainer.TypeBuilder = type_host;
184 bool res = method.Define ();
185 if (current_type != null)
186 ec.TypeContainer.TypeBuilder = current_type;
190 void Error_ParameterMismatch (Type t)
192 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
193 "{0}' since there is a parameter mismatch", t);
197 // Returns true if this anonymous method can be implicitly
198 // converted to the delegate type `delegate_type'
200 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
203 // At this point its the first time we know the return type that is
204 // needed for the anonymous method. We create the method here.
207 invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
208 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
210 if (Parameters == null){
214 // We provide a set of inaccessible parameters
217 for (i = 0; i < invoke_pd.Count; i++){
218 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
221 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
222 Parameter [] fixedpars = new Parameter [n];
224 for (i = j = 0; i < invoke_pd.Count; i++){
225 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
227 fixedpars [j] = new Parameter (
228 new TypeExpression (invoke_pd.ParameterType (i), loc),
229 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
233 Parameter variable = null;
234 if (params_idx != -1){
235 variable = new Parameter (
236 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
237 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
240 Parameters = new Parameters (fixedpars, variable);
244 // First, parameter types of `delegate_type' must be compatible
245 // with the anonymous method.
247 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
249 if (amp.Count != invoke_pd.Count){
251 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
252 TypeManager.CSharpName (delegate_type), amp.Count);
253 Error_ParameterMismatch (delegate_type);
258 for (int i = 0; i < amp.Count; i++){
259 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
262 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
263 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
264 i+1, amp.ModifierDesc (i));
265 Error_ParameterMismatch (delegate_type);
269 if (amp_mod != invoke_pd.ParameterModifier (i)){
270 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
271 i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
272 Error_ParameterMismatch (delegate_type);
276 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
277 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
279 TypeManager.CSharpName (amp.ParameterType (i)),
280 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
281 Error_ParameterMismatch (delegate_type);
288 // If we are only probing, return ourselves
294 // Second: the return type of the delegate must be compatible with
295 // the anonymous type. Instead of doing a pass to examine the block
296 // we satisfy the rule by setting the return type on the EmitContext
297 // to be the delegate type return type.
300 //MethodBuilder builder = method_data.MethodBuilder;
301 //ILGenerator ig = builder.GetILGenerator ();
304 aec = new EmitContext (
305 ec.TypeContainer, ec.DeclSpace, loc, null,
306 invoke_mb.ReturnType,
307 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
308 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
309 (ec.IsStatic ? Modifiers.STATIC : 0),
310 /* No constructor */ false);
312 aec.CurrentAnonymousMethod = this;
313 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
314 ContainingBlock = ec.CurrentBlock;
316 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
317 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
322 public override string ExprClassName {
324 return "anonymous method";
328 public MethodBuilder GetMethodBuilder ()
330 return method.MethodData.MethodBuilder;
333 public override string GetSignatureForError ()
335 string s = TypeManager.CSharpSignature (invoke_mb);
336 return s.Substring (0, s.IndexOf (".Invoke("));
339 public bool EmitMethod (EmitContext ec)
341 if (!CreateMethodHost (ec))
344 MethodBuilder builder = GetMethodBuilder ();
345 ILGenerator ig = builder.GetILGenerator ();
348 Parameters.LabelParameters (aec, builder);
351 // Adjust based on the computed state of the
352 // method from CreateMethodHost
354 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
356 aec.EmitMeta (Block, amp);
357 aec.EmitResolvedTopBlock (Block, unreachable);
361 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
363 TypeBuilder container = ec.TypeContainer.TypeBuilder;
364 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
366 scope.ScopeTypeBuilder = container.DefineNestedType (
367 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
368 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
370 Type [] constructor_types = TypeManager.NoTypes;
371 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
372 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
373 MethodAttributes.Public | MethodAttributes.HideBySig |
374 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
375 CallingConventions.HasThis, constructor_types);
376 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
377 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
379 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
380 cig.Emit (OpCodes.Ldarg_0);
381 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
382 cig.Emit (OpCodes.Ret);
385 public static void Error_AddressOfCapturedVar (string name, Location loc)
387 Report.Error (1686, loc,
388 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
394 // This will emit the code for the delegate, as well delegate creation on the host
396 public class AnonymousDelegate : DelegateCreation {
399 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
406 public override Expression DoResolve (EmitContext ec)
408 eclass = ExprClass.Value;
413 public override void Emit (EmitContext ec)
415 if (!am.EmitMethod (ec))
419 // Now emit the delegate creation.
421 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
422 delegate_instance_expression = new AnonymousInstance (am);
424 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
425 constructor_method = ((MethodGroupExpr) ml).Methods [0];
426 delegate_method = am.GetMethodBuilder ();
430 class AnonymousInstance : Expression {
433 public AnonymousInstance (AnonymousMethod am)
436 eclass = ExprClass.Value;
439 public override Expression DoResolve (EmitContext ec)
444 public override void Emit (EmitContext ec)
446 am.aec.EmitMethodHostInstance (ec, am);
451 class CapturedParameter {
453 public FieldBuilder FieldBuilder;
456 public CapturedParameter (Type type, int idx)
464 // Here we cluster all the variables captured on a given scope, we also
465 // keep some extra information that might be required on each scope.
467 public class ScopeInfo {
468 public CaptureContext CaptureContext;
469 public ScopeInfo ParentScope;
470 public Block ScopeBlock;
471 public bool NeedThis = false;
472 public bool HostsParameters = false;
474 // For tracking the number of scopes created.
479 ArrayList locals = new ArrayList ();
480 ArrayList children = new ArrayList ();
483 // The types and fields generated
485 public TypeBuilder ScopeTypeBuilder;
486 public ConstructorBuilder ScopeConstructor;
487 public FieldBuilder THIS;
488 public FieldBuilder ParentLink;
491 // Points to the object of type `ScopeTypeBuilder' that
492 // holds the data for the scope
494 LocalBuilder scope_instance;
496 public ScopeInfo (CaptureContext cc, Block b)
505 public void AddLocal (LocalInfo li)
507 if (locals.Contains (li))
513 public bool IsCaptured (LocalInfo li)
515 return locals.Contains (li);
518 internal void AddChild (ScopeInfo si)
520 if (children.Contains (si))
524 // If any of the current children should be a children of `si', move them there
526 ArrayList move_queue = null;
527 foreach (ScopeInfo child in children){
528 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
529 if (move_queue == null)
530 move_queue = new ArrayList ();
531 move_queue.Add (child);
532 child.ParentScope = si;
539 if (move_queue != null){
540 foreach (ScopeInfo child in move_queue){
541 children.Remove (child);
546 static int indent = 0;
550 for (int i = 0; i < indent; i++)
556 //Console.WriteLine (Environment.StackTrace);
558 Console.WriteLine ("START");
561 Console.WriteLine ("NeedThis=" + NeedThis);
562 foreach (LocalInfo li in locals){
564 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
567 foreach (ScopeInfo si in children)
571 Console.WriteLine ("END");
574 public string MakeHelperName ()
576 return String.Format ("<>AnonHelp<{0}>", id);
579 private string MakeFieldName (string local_name)
581 return "<" + id + ":" + local_name + ">";
584 public void EmitScopeType (EmitContext ec)
588 if (ScopeTypeBuilder != null)
591 TypeBuilder container = ec.TypeContainer.TypeBuilder;
593 CaptureContext.Host.CreateScopeType (ec, this);
596 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
598 if (ParentScope != null){
599 if (ParentScope.ScopeTypeBuilder == null){
600 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
603 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
604 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
605 FieldAttributes.Assembly);
608 if (NeedThis && ParentScope != null)
609 throw new Exception ("I was not expecting THIS && having a parent");
611 foreach (LocalInfo info in locals)
612 info.FieldBuilder = ScopeTypeBuilder.DefineField (
613 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
615 if (HostsParameters){
616 Hashtable captured_parameters = CaptureContext.captured_parameters;
618 foreach (DictionaryEntry de in captured_parameters){
619 string name = (string) de.Key;
620 CapturedParameter cp = (CapturedParameter) de.Value;
623 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
624 cp.FieldBuilder = fb;
628 foreach (ScopeInfo si in children){
629 si.EmitScopeType (ec);
633 public void CloseTypes ()
635 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
636 foreach (ScopeInfo si in children)
641 // Emits the initialization code for the scope
643 public void EmitInitScope (EmitContext ec)
645 ILGenerator ig = ec.ig;
650 if (ScopeConstructor == null)
651 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
653 if (!CaptureContext.Host.IsIterator) {
654 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
655 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
656 ig.Emit (OpCodes.Stloc, scope_instance);
660 if (CaptureContext.Host.IsIterator) {
661 ig.Emit (OpCodes.Ldarg_0);
662 ig.Emit (OpCodes.Ldarg_1);
664 ig.Emit (OpCodes.Ldloc, scope_instance);
665 ig.Emit (OpCodes.Ldarg_0);
667 ig.Emit (OpCodes.Stfld, THIS);
671 // Copy the parameter values, if any
673 int extra = ec.IsStatic ? 0 : 1;
674 if (CaptureContext.Host.IsIterator)
676 if (HostsParameters){
677 Hashtable captured_parameters = CaptureContext.captured_parameters;
679 foreach (DictionaryEntry de in captured_parameters){
680 CapturedParameter cp = (CapturedParameter) de.Value;
682 EmitScopeInstance (ig);
683 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
684 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
688 if (ParentScope != null){
689 if (!ParentScope.inited)
690 ParentScope.EmitInitScope (ec);
692 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
694 // Only emit initialization in our capturecontext world
696 if (ParentScope.CaptureContext == CaptureContext){
697 EmitScopeInstance (ig);
698 ParentScope.EmitScopeInstance (ig);
699 ig.Emit (OpCodes.Stfld, ParentLink);
701 EmitScopeInstance (ig);
702 ig.Emit (OpCodes.Ldarg_0);
703 ig.Emit (OpCodes.Stfld, ParentLink);
710 public void EmitScopeInstance (ILGenerator ig)
712 if (CaptureContext.Host.IsIterator)
713 ig.Emit (OpCodes.Ldarg_0);
715 ig.Emit (OpCodes.Ldloc, scope_instance);
718 static void DoPath (StringBuilder sb, ScopeInfo start)
720 if (start.ParentScope != null){
721 DoPath (sb, start.ParentScope);
724 sb.Append ((start.id).ToString ());
727 public override string ToString ()
729 StringBuilder sb = new StringBuilder ();
732 if (CaptureContext != null){
733 sb.Append (CaptureContext.ToString ());
740 return sb.ToString ();
745 // CaptureContext objects are created on demand if a method has
746 // anonymous methods and kept on the ToplevelBlock.
748 // If they exist, all ToplevelBlocks in the containing block are
749 // linked together (children pointing to their parents).
751 public class CaptureContext {
752 public static int count;
757 // Points to the toplevel block that owns this CaptureContext
759 ToplevelBlock toplevel_owner;
760 Hashtable scopes = new Hashtable ();
761 bool have_captured_vars = false;
762 bool referenced_this = false;
763 ScopeInfo topmost = null;
768 Hashtable captured_fields = new Hashtable ();
769 Hashtable captured_variables = new Hashtable ();
770 public Hashtable captured_parameters = new Hashtable ();
771 public AnonymousContainer Host;
773 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
774 AnonymousContainer host)
777 this.toplevel_owner = toplevel_owner;
784 void DoPath (StringBuilder sb, CaptureContext cc)
786 if (cc.ParentCaptureContext != null){
787 DoPath (sb, cc.ParentCaptureContext);
790 sb.Append (cc.cc_id.ToString ());
793 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
795 toplevel_owner = new_toplevel;
798 for (CaptureContext cc = ParentCaptureContext; cc != null;
799 cc = cc.ParentCaptureContext) {
804 public override string ToString ()
806 StringBuilder sb = new StringBuilder ();
810 return sb.ToString ();
813 public ToplevelBlock ParentToplevel {
815 return toplevel_owner.Container;
819 public CaptureContext ParentCaptureContext {
821 ToplevelBlock parent = ParentToplevel;
823 return (parent == null) ? null : parent.CaptureContext;
827 // Returns the deepest of two scopes
828 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
840 // If they Scopes are on the same CaptureContext, we do the double
841 // checks just so if there is an invariant change in the future,
842 // we get the exception at the end
844 for (p = a; p != null; p = p.ParentScope)
848 for (p = b; p != null; p = p.ParentScope)
852 CaptureContext ca = a.CaptureContext;
853 CaptureContext cb = b.CaptureContext;
855 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
859 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
862 throw new Exception ("Should never be reached");
865 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
867 am.Scope = Deepest (am.Scope, scope);
870 void LinkScope (ScopeInfo scope, int id)
872 ScopeInfo parent = (ScopeInfo) scopes [id];
873 scope.ParentScope = parent;
874 parent.AddChild (scope);
876 if (scope == topmost)
880 public void AddLocal (AnonymousContainer am, LocalInfo li)
882 if (li.Block.Toplevel != toplevel_owner){
883 ParentCaptureContext.AddLocal (am, li);
886 int block_id = li.Block.ID;
888 if (scopes [block_id] == null){
889 scope = new ScopeInfo (this, li.Block);
890 scopes [block_id] = scope;
892 scope = (ScopeInfo) scopes [block_id];
894 if (topmost == null){
899 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
900 if (scopes [b.ID] != null){
901 LinkScope (scope, b.ID);
906 if (scope.ParentScope == null && ParentCaptureContext != null){
907 CaptureContext pcc = ParentCaptureContext;
909 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
910 if (pcc.scopes [b.ID] != null){
911 pcc.LinkScope (scope, b.ID);
922 AdjustMethodScope (Host, topmost);
927 AdjustMethodScope (am, scope);
929 if (captured_variables [li] != null)
932 have_captured_vars = true;
933 captured_variables [li] = li;
938 // Retursn the CaptureContext for the block that defines the parameter `name'
940 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
942 ToplevelBlock container = current.Container;
943 if (container != null){
944 CaptureContext cc = _ContextForParameter (container, name);
948 if (current.IsParameterReference (name))
949 return current.ToplevelBlockCaptureContext;
953 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
955 CaptureContext cc = _ContextForParameter (current, name);
957 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
962 // Records the captured parameter at the appropriate CaptureContext
964 public void AddParameter (EmitContext ec, AnonymousContainer am,
965 string name, Type t, int idx)
967 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
969 cc.AddParameterToContext (am, name, t, idx);
973 // Records the parameters in the context
975 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
977 if (captured_parameters == null)
978 captured_parameters = new Hashtable ();
979 if (captured_parameters [name] != null)
981 captured_parameters [name] = new CapturedParameter (t, idx);
983 if (topmost == null){
985 // Create one ScopeInfo, if there are none.
987 topmost = new ScopeInfo (this, toplevel_owner);
988 scopes [toplevel_owner.ID] = topmost;
991 // If the topmost ScopeInfo is not at the topblock level, insert
992 // a new ScopeInfo there.
994 // FIXME: This code probably should be evolved to be like the code
997 if (topmost.ScopeBlock != toplevel_owner){
998 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
999 ScopeInfo old_top = topmost;
1000 scopes [toplevel_owner.ID] = topmost;
1001 topmost.ParentScope = par_si;
1003 topmost.AddChild (old_top);
1007 topmost.HostsParameters = true;
1008 AdjustMethodScope (am, topmost);
1012 // Captured fields are only recorded on the topmost CaptureContext, because that
1013 // one is the one linked to the owner of instance fields
1015 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1017 if (fe.FieldInfo.IsStatic)
1018 throw new Exception ("Attempt to register a static field as a captured field");
1019 CaptureContext parent = ParentCaptureContext;
1020 if (parent != null) {
1021 parent.AddField (ec, am, fe);
1025 if (topmost == null){
1027 // Create one ScopeInfo, if there are none.
1029 topmost = new ScopeInfo (this, toplevel_owner);
1030 scopes [toplevel_owner.ID] = topmost;
1033 AdjustMethodScope (am, topmost);
1036 public void CaptureThis ()
1038 CaptureContext parent = ParentCaptureContext;
1040 parent.CaptureThis ();
1041 referenced_this = true;
1044 public bool HaveCapturedVariables {
1046 return have_captured_vars;
1050 public bool HaveCapturedFields {
1052 CaptureContext parent = ParentCaptureContext;
1054 return parent.HaveCapturedFields;
1055 return captured_fields.Count > 0;
1059 public bool IsCaptured (LocalInfo local)
1061 foreach (ScopeInfo si in scopes.Values){
1062 if (si.IsCaptured (local))
1069 // Returns whether the parameter is captured
1071 public bool IsParameterCaptured (string name)
1073 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1076 if (captured_parameters != null)
1077 return captured_parameters [name] != null;
1081 public void EmitAnonymousHelperClasses (EmitContext ec)
1083 if (topmost != null){
1084 topmost.NeedThis = HaveCapturedFields || referenced_this;
1085 topmost.EmitScopeType (ec);
1089 public void CloseAnonymousHelperClasses ()
1091 if (topmost != null)
1092 topmost.CloseTypes ();
1095 public void EmitInitScope (EmitContext ec)
1097 EmitAnonymousHelperClasses (ec);
1098 if (topmost != null)
1099 topmost.EmitInitScope (ec);
1102 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1106 si = (ScopeInfo) scopes [b.ID];
1108 throw new Exception ("Si is null for block " + b.ID);
1109 si.EmitInitScope (ec);
1115 // Emits the opcodes necessary to load the instance of the captured
1118 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1119 AnonymousContainer am)
1121 ILGenerator ig = ec.ig;
1124 if (li.Block.Toplevel == toplevel_owner){
1125 si = GetScopeFromBlock (ec, li.Block);
1126 si.EmitScopeInstance (ig);
1131 ig.Emit (OpCodes.Ldarg_0);
1133 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1137 while (si.ScopeBlock.ID != li.Block.ID){
1138 if (si.ParentLink != null)
1139 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1140 si = si.ParentScope;
1143 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1144 while (si.ScopeBlock.ID != li.Block.ID){
1145 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1146 si = si.ParentScope;
1149 throw new Exception (
1150 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1151 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1158 // Internal routine that loads the instance to reach parameter `name'
1160 void EmitParameterInstance (EmitContext ec, string name)
1162 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1164 cc.EmitParameterInstance (ec, name);
1168 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1169 if (par_info != null){
1171 // FIXME: implementing this.
1174 ILGenerator ig = ec.ig;
1178 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1179 si = GetScopeFromBlock (ec, toplevel_owner);
1180 si.EmitScopeInstance (ig);
1182 si = ec.CurrentAnonymousMethod.Scope;
1183 ig.Emit (OpCodes.Ldarg_0);
1187 while (si.ParentLink != null) {
1188 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1189 si = si.ParentScope;
1195 // Emits the code necessary to load the parameter named `name' within
1196 // an anonymous method.
1198 public void EmitParameter (EmitContext ec, string name)
1200 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1202 cc.EmitParameter (ec, name);
1205 EmitParameterInstance (ec, name);
1206 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1207 if (par_info != null){
1209 // FIXME: implementing this.
1212 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1216 // Implements the assignment of `source' to the paramenter named `name' within
1217 // an anonymous method.
1219 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1221 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1223 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1226 ILGenerator ig = ec.ig;
1227 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1229 EmitParameterInstance (ec, name);
1232 ig.Emit (OpCodes.Dup);
1233 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1237 // Emits the address for the parameter named `name' within
1238 // an anonymous method.
1240 public void EmitAddressOfParameter (EmitContext ec, string name)
1242 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1244 cc.EmitAddressOfParameter (ec, name);
1247 EmitParameterInstance (ec, name);
1248 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1249 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1253 // The following methods are only invoked on the host for the
1254 // anonymous method.
1256 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1258 ILGenerator ig = target.ig;
1259 ScopeInfo si = am.Scope;
1262 ig.Emit (OpCodes.Ldarg_0);
1266 si.EmitInitScope (target);
1267 si.EmitScopeInstance (ig);
1270 ArrayList all_scopes = new ArrayList ();
1272 public void AddScope (ScopeInfo si)
1274 all_scopes.Add (si);
1275 toplevel_owner.RegisterCaptureContext (this);
1279 // Links any scopes that were not linked previously
1281 public void AdjustScopes ()
1283 foreach (ScopeInfo scope in all_scopes){
1284 if (scope.ParentScope != null)
1287 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1288 if (scopes [b.ID] != null){
1289 LinkScope (scope, b.ID);
1294 if (scope.ParentScope == null && ParentCaptureContext != null){
1295 CaptureContext pcc = ParentCaptureContext;
1297 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1298 if (pcc.scopes [b.ID] != null){
1299 pcc.LinkScope (scope, b.ID);