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 MethodBuilder GetMethodBuilder ()
324 return method.MethodData.MethodBuilder;
327 public override string GetSignatureForError ()
329 string s = TypeManager.CSharpSignature (invoke_mb);
330 return s.Substring (0, s.IndexOf (".Invoke("));
333 public bool EmitMethod (EmitContext ec)
335 if (!CreateMethodHost (ec))
338 MethodBuilder builder = GetMethodBuilder ();
339 ILGenerator ig = builder.GetILGenerator ();
342 Parameters.LabelParameters (aec, builder);
345 // Adjust based on the computed state of the
346 // method from CreateMethodHost
348 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
350 aec.EmitMeta (Block, amp);
351 aec.EmitResolvedTopBlock (Block, unreachable);
355 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
357 TypeBuilder container = ec.TypeContainer.TypeBuilder;
358 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
360 scope.ScopeTypeBuilder = container.DefineNestedType (
361 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
362 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
364 Type [] constructor_types = TypeManager.NoTypes;
365 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
366 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
367 MethodAttributes.Public | MethodAttributes.HideBySig |
368 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
369 CallingConventions.HasThis, constructor_types);
370 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
371 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
373 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
374 cig.Emit (OpCodes.Ldarg_0);
375 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
376 cig.Emit (OpCodes.Ret);
379 public static void Error_AddressOfCapturedVar (string name, Location loc)
381 Report.Error (1686, loc,
382 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
388 // This will emit the code for the delegate, as well delegate creation on the host
390 public class AnonymousDelegate : DelegateCreation {
393 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
400 public override Expression DoResolve (EmitContext ec)
402 eclass = ExprClass.Value;
407 public override void Emit (EmitContext ec)
409 if (!am.EmitMethod (ec))
413 // Now emit the delegate creation.
415 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
416 delegate_instance_expression = new AnonymousInstance (am);
418 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
419 constructor_method = ((MethodGroupExpr) ml).Methods [0];
420 delegate_method = am.GetMethodBuilder ();
424 class AnonymousInstance : Expression {
427 public AnonymousInstance (AnonymousMethod am)
430 eclass = ExprClass.Value;
433 public override Expression DoResolve (EmitContext ec)
438 public override void Emit (EmitContext ec)
440 am.aec.EmitMethodHostInstance (ec, am);
445 class CapturedParameter {
447 public FieldBuilder FieldBuilder;
450 public CapturedParameter (Type type, int idx)
458 // Here we cluster all the variables captured on a given scope, we also
459 // keep some extra information that might be required on each scope.
461 public class ScopeInfo {
462 public CaptureContext CaptureContext;
463 public ScopeInfo ParentScope;
464 public Block ScopeBlock;
465 public bool NeedThis = false;
466 public bool HostsParameters = false;
468 // For tracking the number of scopes created.
473 ArrayList locals = new ArrayList ();
474 ArrayList children = new ArrayList ();
477 // The types and fields generated
479 public TypeBuilder ScopeTypeBuilder;
480 public ConstructorBuilder ScopeConstructor;
481 public FieldBuilder THIS;
482 public FieldBuilder ParentLink;
485 // Points to the object of type `ScopeTypeBuilder' that
486 // holds the data for the scope
488 LocalBuilder scope_instance;
490 public ScopeInfo (CaptureContext cc, Block b)
499 public void AddLocal (LocalInfo li)
501 if (locals.Contains (li))
507 public bool IsCaptured (LocalInfo li)
509 return locals.Contains (li);
512 internal void AddChild (ScopeInfo si)
514 if (children.Contains (si))
518 // If any of the current children should be a children of `si', move them there
520 ArrayList move_queue = null;
521 foreach (ScopeInfo child in children){
522 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
523 if (move_queue == null)
524 move_queue = new ArrayList ();
525 move_queue.Add (child);
526 child.ParentScope = si;
533 if (move_queue != null){
534 foreach (ScopeInfo child in move_queue){
535 children.Remove (child);
540 static int indent = 0;
544 for (int i = 0; i < indent; i++)
550 //Console.WriteLine (Environment.StackTrace);
552 Console.WriteLine ("START");
555 Console.WriteLine ("NeedThis=" + NeedThis);
556 foreach (LocalInfo li in locals){
558 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
561 foreach (ScopeInfo si in children)
565 Console.WriteLine ("END");
568 public string MakeHelperName ()
570 return String.Format ("<>AnonHelp<{0}>", id);
573 private string MakeFieldName (string local_name)
575 return "<" + id + ":" + local_name + ">";
578 public void EmitScopeType (EmitContext ec)
582 if (ScopeTypeBuilder != null)
585 TypeBuilder container = ec.TypeContainer.TypeBuilder;
587 CaptureContext.Host.CreateScopeType (ec, this);
590 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
592 if (ParentScope != null){
593 if (ParentScope.ScopeTypeBuilder == null){
594 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
597 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
598 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
599 FieldAttributes.Assembly);
602 if (NeedThis && ParentScope != null)
603 throw new Exception ("I was not expecting THIS && having a parent");
605 foreach (LocalInfo info in locals)
606 info.FieldBuilder = ScopeTypeBuilder.DefineField (
607 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
609 if (HostsParameters){
610 Hashtable captured_parameters = CaptureContext.captured_parameters;
612 foreach (DictionaryEntry de in captured_parameters){
613 string name = (string) de.Key;
614 CapturedParameter cp = (CapturedParameter) de.Value;
617 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
618 cp.FieldBuilder = fb;
622 foreach (ScopeInfo si in children){
623 si.EmitScopeType (ec);
627 public void CloseTypes ()
629 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
630 foreach (ScopeInfo si in children)
635 // Emits the initialization code for the scope
637 public void EmitInitScope (EmitContext ec)
639 ILGenerator ig = ec.ig;
644 if (ScopeConstructor == null)
645 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
647 if (!CaptureContext.Host.IsIterator) {
648 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
649 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
650 ig.Emit (OpCodes.Stloc, scope_instance);
654 if (CaptureContext.Host.IsIterator) {
655 ig.Emit (OpCodes.Ldarg_0);
656 ig.Emit (OpCodes.Ldarg_1);
658 ig.Emit (OpCodes.Ldloc, scope_instance);
659 ig.Emit (OpCodes.Ldarg_0);
661 ig.Emit (OpCodes.Stfld, THIS);
665 // Copy the parameter values, if any
667 int extra = ec.IsStatic ? 0 : 1;
668 if (CaptureContext.Host.IsIterator)
670 if (HostsParameters){
671 Hashtable captured_parameters = CaptureContext.captured_parameters;
673 foreach (DictionaryEntry de in captured_parameters){
674 CapturedParameter cp = (CapturedParameter) de.Value;
676 EmitScopeInstance (ig);
677 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
678 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
682 if (ParentScope != null){
683 if (!ParentScope.inited)
684 ParentScope.EmitInitScope (ec);
686 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
688 // Only emit initialization in our capturecontext world
690 if (ParentScope.CaptureContext == CaptureContext){
691 EmitScopeInstance (ig);
692 ParentScope.EmitScopeInstance (ig);
693 ig.Emit (OpCodes.Stfld, ParentLink);
695 EmitScopeInstance (ig);
696 ig.Emit (OpCodes.Ldarg_0);
697 ig.Emit (OpCodes.Stfld, ParentLink);
704 public void EmitScopeInstance (ILGenerator ig)
706 if (CaptureContext.Host.IsIterator)
707 ig.Emit (OpCodes.Ldarg_0);
709 ig.Emit (OpCodes.Ldloc, scope_instance);
712 static void DoPath (StringBuilder sb, ScopeInfo start)
714 if (start.ParentScope != null){
715 DoPath (sb, start.ParentScope);
718 sb.Append ((start.id).ToString ());
721 public override string ToString ()
723 StringBuilder sb = new StringBuilder ();
726 if (CaptureContext != null){
727 sb.Append (CaptureContext.ToString ());
734 return sb.ToString ();
739 // CaptureContext objects are created on demand if a method has
740 // anonymous methods and kept on the ToplevelBlock.
742 // If they exist, all ToplevelBlocks in the containing block are
743 // linked together (children pointing to their parents).
745 public class CaptureContext {
746 public static int count;
751 // Points to the toplevel block that owns this CaptureContext
753 ToplevelBlock toplevel_owner;
754 Hashtable scopes = new Hashtable ();
755 bool have_captured_vars = false;
756 bool referenced_this = false;
757 ScopeInfo topmost = null;
762 Hashtable captured_fields = new Hashtable ();
763 Hashtable captured_variables = new Hashtable ();
764 public Hashtable captured_parameters = new Hashtable ();
765 public AnonymousContainer Host;
767 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
768 AnonymousContainer host)
771 this.toplevel_owner = toplevel_owner;
778 void DoPath (StringBuilder sb, CaptureContext cc)
780 if (cc.ParentCaptureContext != null){
781 DoPath (sb, cc.ParentCaptureContext);
784 sb.Append (cc.cc_id.ToString ());
787 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
789 toplevel_owner = new_toplevel;
792 for (CaptureContext cc = ParentCaptureContext; cc != null;
793 cc = cc.ParentCaptureContext) {
798 public override string ToString ()
800 StringBuilder sb = new StringBuilder ();
804 return sb.ToString ();
807 public ToplevelBlock ParentToplevel {
809 return toplevel_owner.Container;
813 public CaptureContext ParentCaptureContext {
815 ToplevelBlock parent = ParentToplevel;
817 return (parent == null) ? null : parent.CaptureContext;
821 // Returns the deepest of two scopes
822 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
834 // If they Scopes are on the same CaptureContext, we do the double
835 // checks just so if there is an invariant change in the future,
836 // we get the exception at the end
838 for (p = a; p != null; p = p.ParentScope)
842 for (p = b; p != null; p = p.ParentScope)
846 CaptureContext ca = a.CaptureContext;
847 CaptureContext cb = b.CaptureContext;
849 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
853 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
856 throw new Exception ("Should never be reached");
859 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
861 am.Scope = Deepest (am.Scope, scope);
864 void LinkScope (ScopeInfo scope, int id)
866 ScopeInfo parent = (ScopeInfo) scopes [id];
867 scope.ParentScope = parent;
868 parent.AddChild (scope);
870 if (scope == topmost)
874 public void AddLocal (AnonymousContainer am, LocalInfo li)
876 if (li.Block.Toplevel != toplevel_owner){
877 ParentCaptureContext.AddLocal (am, li);
880 int block_id = li.Block.ID;
882 if (scopes [block_id] == null){
883 scope = new ScopeInfo (this, li.Block);
884 scopes [block_id] = scope;
886 scope = (ScopeInfo) scopes [block_id];
888 if (topmost == null){
893 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
894 if (scopes [b.ID] != null){
895 LinkScope (scope, b.ID);
900 if (scope.ParentScope == null && ParentCaptureContext != null){
901 CaptureContext pcc = ParentCaptureContext;
903 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
904 if (pcc.scopes [b.ID] != null){
905 pcc.LinkScope (scope, b.ID);
916 AdjustMethodScope (Host, topmost);
921 AdjustMethodScope (am, scope);
923 if (captured_variables [li] != null)
926 have_captured_vars = true;
927 captured_variables [li] = li;
932 // Retursn the CaptureContext for the block that defines the parameter `name'
934 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
936 ToplevelBlock container = current.Container;
937 if (container != null){
938 CaptureContext cc = _ContextForParameter (container, name);
942 if (current.IsParameterReference (name))
943 return current.ToplevelBlockCaptureContext;
947 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
949 CaptureContext cc = _ContextForParameter (current, name);
951 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
956 // Records the captured parameter at the appropriate CaptureContext
958 public void AddParameter (EmitContext ec, AnonymousContainer am,
959 string name, Type t, int idx)
961 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
963 cc.AddParameterToContext (am, name, t, idx);
967 // Records the parameters in the context
969 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
971 if (captured_parameters == null)
972 captured_parameters = new Hashtable ();
973 if (captured_parameters [name] != null)
975 captured_parameters [name] = new CapturedParameter (t, idx);
977 if (topmost == null){
979 // Create one ScopeInfo, if there are none.
981 topmost = new ScopeInfo (this, toplevel_owner);
982 scopes [toplevel_owner.ID] = topmost;
985 // If the topmost ScopeInfo is not at the topblock level, insert
986 // a new ScopeInfo there.
988 // FIXME: This code probably should be evolved to be like the code
991 if (topmost.ScopeBlock != toplevel_owner){
992 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
993 ScopeInfo old_top = topmost;
994 scopes [toplevel_owner.ID] = topmost;
995 topmost.ParentScope = par_si;
997 topmost.AddChild (old_top);
1001 topmost.HostsParameters = true;
1002 AdjustMethodScope (am, topmost);
1006 // Captured fields are only recorded on the topmost CaptureContext, because that
1007 // one is the one linked to the owner of instance fields
1009 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1011 if (fe.FieldInfo.IsStatic)
1012 throw new Exception ("Attempt to register a static field as a captured field");
1013 CaptureContext parent = ParentCaptureContext;
1014 if (parent != null) {
1015 parent.AddField (ec, am, fe);
1019 if (topmost == null){
1021 // Create one ScopeInfo, if there are none.
1023 topmost = new ScopeInfo (this, toplevel_owner);
1024 scopes [toplevel_owner.ID] = topmost;
1027 AdjustMethodScope (am, topmost);
1030 public void CaptureThis ()
1032 CaptureContext parent = ParentCaptureContext;
1034 parent.CaptureThis ();
1035 referenced_this = true;
1038 public bool HaveCapturedVariables {
1040 return have_captured_vars;
1044 public bool HaveCapturedFields {
1046 CaptureContext parent = ParentCaptureContext;
1048 return parent.HaveCapturedFields;
1049 return captured_fields.Count > 0;
1053 public bool IsCaptured (LocalInfo local)
1055 foreach (ScopeInfo si in scopes.Values){
1056 if (si.IsCaptured (local))
1063 // Returns whether the parameter is captured
1065 public bool IsParameterCaptured (string name)
1067 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1070 if (captured_parameters != null)
1071 return captured_parameters [name] != null;
1075 public void EmitAnonymousHelperClasses (EmitContext ec)
1077 if (topmost != null){
1078 topmost.NeedThis = HaveCapturedFields || referenced_this;
1079 topmost.EmitScopeType (ec);
1083 public void CloseAnonymousHelperClasses ()
1085 if (topmost != null)
1086 topmost.CloseTypes ();
1089 public void EmitInitScope (EmitContext ec)
1091 EmitAnonymousHelperClasses (ec);
1092 if (topmost != null)
1093 topmost.EmitInitScope (ec);
1096 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1100 si = (ScopeInfo) scopes [b.ID];
1102 throw new Exception ("Si is null for block " + b.ID);
1103 si.EmitInitScope (ec);
1109 // Emits the opcodes necessary to load the instance of the captured
1112 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1113 AnonymousContainer am)
1115 ILGenerator ig = ec.ig;
1118 if (li.Block.Toplevel == toplevel_owner){
1119 si = GetScopeFromBlock (ec, li.Block);
1120 si.EmitScopeInstance (ig);
1125 ig.Emit (OpCodes.Ldarg_0);
1127 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1131 while (si.ScopeBlock.ID != li.Block.ID){
1132 if (si.ParentLink != null)
1133 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1134 si = si.ParentScope;
1137 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1138 while (si.ScopeBlock.ID != li.Block.ID){
1139 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1140 si = si.ParentScope;
1143 throw new Exception (
1144 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1145 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1152 // Internal routine that loads the instance to reach parameter `name'
1154 void EmitParameterInstance (EmitContext ec, string name)
1156 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1158 cc.EmitParameterInstance (ec, name);
1162 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1163 if (par_info != null){
1165 // FIXME: implementing this.
1168 ILGenerator ig = ec.ig;
1172 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1173 si = GetScopeFromBlock (ec, toplevel_owner);
1174 si.EmitScopeInstance (ig);
1176 si = ec.CurrentAnonymousMethod.Scope;
1177 ig.Emit (OpCodes.Ldarg_0);
1181 while (si.ParentLink != null) {
1182 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1183 si = si.ParentScope;
1189 // Emits the code necessary to load the parameter named `name' within
1190 // an anonymous method.
1192 public void EmitParameter (EmitContext ec, string name)
1194 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1196 cc.EmitParameter (ec, name);
1199 EmitParameterInstance (ec, name);
1200 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1201 if (par_info != null){
1203 // FIXME: implementing this.
1206 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1210 // Implements the assignment of `source' to the paramenter named `name' within
1211 // an anonymous method.
1213 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1215 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1217 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1220 ILGenerator ig = ec.ig;
1221 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1223 EmitParameterInstance (ec, name);
1226 ig.Emit (OpCodes.Dup);
1227 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1231 // Emits the address for the parameter named `name' within
1232 // an anonymous method.
1234 public void EmitAddressOfParameter (EmitContext ec, string name)
1236 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1238 cc.EmitAddressOfParameter (ec, name);
1241 EmitParameterInstance (ec, name);
1242 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1243 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1247 // The following methods are only invoked on the host for the
1248 // anonymous method.
1250 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1252 ILGenerator ig = target.ig;
1253 ScopeInfo si = am.Scope;
1256 ig.Emit (OpCodes.Ldarg_0);
1260 si.EmitInitScope (target);
1261 si.EmitScopeInstance (ig);
1264 ArrayList all_scopes = new ArrayList ();
1266 public void AddScope (ScopeInfo si)
1268 all_scopes.Add (si);
1269 toplevel_owner.RegisterCaptureContext (this);
1273 // Links any scopes that were not linked previously
1275 public void AdjustScopes ()
1277 foreach (ScopeInfo scope in all_scopes){
1278 if (scope.ParentScope != null)
1281 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1282 if (scopes [b.ID] != null){
1283 LinkScope (scope, b.ID);
1288 if (scope.ParentScope == null && ParentCaptureContext != null){
1289 CaptureContext pcc = ParentCaptureContext;
1291 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1292 if (pcc.scopes [b.ID] != null){
1293 pcc.LinkScope (scope, b.ID);