2 // anonymous.cs: Support for anonymous methods
5 // Miguel de Icaza (miguel@ximain.com)
7 // (C) 2003, 2004 Novell, Inc.
9 // TODO: Ideally, we should have the helper classes emited as a hierarchy to map
10 // their nesting, and have the visibility set to private, instead of NestedAssembly
17 using System.Collections;
18 using System.Reflection;
19 using System.Reflection.Emit;
21 namespace Mono.CSharp {
23 public abstract class AnonymousContainer : Expression
25 // Used to generate unique method names.
26 protected static int anonymous_method_count;
28 // An array list of AnonymousMethodParameter or null
29 public Parameters Parameters;
32 // The block that makes up the body for the anonymous mehtod
34 public ToplevelBlock Block;
37 // The container block for this anonymous method.
39 public Block ContainingBlock;
42 // The implicit method we create
46 protected MethodInfo invoke_mb;
48 // The emit context for the anonymous method
49 public EmitContext aec;
50 public InternalParameters amp;
51 public string[] TypeParameters;
52 public Type[] TypeArguments;
53 protected bool unreachable;
56 // The modifiers applied to the method, we aggregate them
58 protected int method_modifiers = Modifiers.PRIVATE;
61 // During the resolve stage of the anonymous method body,
62 // we discover the actual scope where we are hosted, or
63 // null to host the method in the same class
65 public ScopeInfo Scope;
68 // Points to our container anonymous method if its present
70 public AnonymousContainer ContainerAnonymousMethod;
72 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
73 ToplevelBlock block, Location l)
75 Parameters = parameters;
80 // The order is important: this setups the CaptureContext tree hierarchy.
82 container.SetHaveAnonymousMethods (l, this);
83 block.SetHaveAnonymousMethods (l, this);
86 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
89 Parameters = parameters;
90 Block = new ToplevelBlock (container, Parameters, l);
94 // The order is important: this setups the CaptureContext tree hierarchy.
96 container.SetHaveAnonymousMethods (loc, this);
97 Block.SetHaveAnonymousMethods (loc, this);
100 public override Expression DoResolve (EmitContext ec)
103 // Set class type, set type
106 eclass = ExprClass.Value;
109 // This hack means `The type is not accessible
110 // anywhere', we depend on special conversion
113 type = TypeManager.anonymous_method_type;
118 protected abstract bool CreateMethodHost (EmitContext ec);
120 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
122 public abstract Iterator Iterator {
126 public abstract bool IsIterator {
131 public class AnonymousMethod : AnonymousContainer
133 public AnonymousMethod (Parameters parameters, ToplevelBlock container,
134 ToplevelBlock block, Location l)
135 : base (parameters, container, block, l)
139 public override Iterator Iterator {
143 public override bool IsIterator {
144 get { return false; }
147 public override void Emit (EmitContext ec)
149 // nothing, as we only exist to not do anything.
153 // Creates the host for the anonymous method
155 protected override bool CreateMethodHost (EmitContext ec)
158 // Crude hack follows: we replace the TypeBuilder during the
159 // definition to get the method hosted in the right class
162 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
163 TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
164 ? current_type : Scope.ScopeTypeBuilder;
166 if (current_type == null)
167 throw new Exception ("The current_type is null");
169 if (type_host == null)
170 throw new Exception (String.Format ("Type host is null, Scope is {0}", Scope == null ? "null" : "Not null"));
172 if (current_type != type_host)
173 method_modifiers = Modifiers.INTERNAL;
175 if (current_type == type_host && ec.IsStatic){
177 method_modifiers |= Modifiers.STATIC;
182 string name = "<#AnonymousMethod>" + anonymous_method_count++;
183 MemberName member_name;
185 GenericMethod generic_method = null;
186 if (TypeParameters != null) {
187 TypeArguments args = new TypeArguments (loc);
188 foreach (string t in TypeParameters)
189 args.Add (new SimpleName (t, loc));
191 member_name = new MemberName (name, args, loc);
193 generic_method = new GenericMethod (
194 ec.DeclSpace.NamespaceEntry,
195 (TypeContainer) ec.TypeContainer, member_name,
196 new TypeExpression (invoke_mb.ReturnType, loc),
199 generic_method.SetParameterInfo (null);
201 member_name = new MemberName (name, loc);
203 method = new Method (
204 (TypeContainer) ec.TypeContainer, generic_method,
205 new TypeExpression (invoke_mb.ReturnType, loc),
206 method_modifiers, false, member_name, Parameters, null);
207 method.Block = Block;
210 // Swap the TypeBuilder while we define the method, then restore
212 if (current_type != null)
213 ec.TypeContainer.TypeBuilder = type_host;
214 bool res = method.Define ();
215 if (current_type != null)
216 ec.TypeContainer.TypeBuilder = current_type;
221 void Error_ParameterMismatch (Type t)
223 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
224 "{0}' since there is a parameter mismatch", t);
228 // Returns true if this anonymous method can be implicitly
229 // converted to the delegate type `delegate_type'
231 public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
234 // At this point its the first time we know the return type that is
235 // needed for the anonymous method. We create the method here.
238 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec, delegate_type, loc);
239 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
240 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
242 if (delegate_type.IsGenericInstance) {
243 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
245 Type def = delegate_type.GetGenericTypeDefinition ();
246 Type[] tparam = TypeManager.GetTypeArguments (def);
247 TypeParameters = new string [tparam.Length];
248 for (int i = 0; i < tparam.Length; i++)
249 TypeParameters [i] = tparam [i].Name;
252 if (Parameters == null){
256 // We provide a set of inaccessible parameters
259 for (i = 0; i < invoke_pd.Count; i++){
260 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
263 int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
264 Parameter [] fixedpars = new Parameter [n];
266 for (i = j = 0; i < invoke_pd.Count; i++){
267 if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
269 fixedpars [j] = new Parameter (
270 new TypeExpression (invoke_pd.ParameterType (i), loc),
271 "+" + j, invoke_pd.ParameterModifier (i), null, loc);
275 Parameter variable = null;
276 if (params_idx != -1){
277 variable = new Parameter (
278 new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
279 "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
282 Parameters = new Parameters (fixedpars, variable);
286 // First, parameter types of `delegate_type' must be compatible
287 // with the anonymous method.
289 amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
291 if (amp.Count != invoke_pd.Count){
293 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
294 TypeManager.CSharpName (delegate_type), amp.Count);
295 Error_ParameterMismatch (delegate_type);
300 for (int i = 0; i < amp.Count; i++){
301 Parameter.Modifier amp_mod = amp.ParameterModifier (i);
304 if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
305 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
306 i+1, amp.ModifierDesc (i));
307 Error_ParameterMismatch (delegate_type);
311 if (amp_mod != invoke_pd.ParameterModifier (i)){
312 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
313 i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
314 Error_ParameterMismatch (delegate_type);
318 if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
319 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
321 TypeManager.CSharpName (amp.ParameterType (i)),
322 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
323 Error_ParameterMismatch (delegate_type);
330 // If we are only probing, return ourselves
336 // Second: the return type of the delegate must be compatible with
337 // the anonymous type. Instead of doing a pass to examine the block
338 // we satisfy the rule by setting the return type on the EmitContext
339 // to be the delegate type return type.
342 //MethodBuilder builder = method_data.MethodBuilder;
343 //ILGenerator ig = builder.GetILGenerator ();
346 aec = new EmitContext (
347 ec.TypeContainer, ec.DeclSpace, loc, null,
348 invoke_mb.ReturnType,
349 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
350 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
351 (ec.IsStatic ? Modifiers.STATIC : 0),
352 /* No constructor */ false);
354 aec.CurrentAnonymousMethod = this;
355 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
356 ContainingBlock = ec.CurrentBlock;
358 if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
359 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
364 public override string ExprClassName {
366 return "anonymous method";
370 public MethodInfo GetMethodBuilder ()
372 MethodInfo builder = method.MethodData.MethodBuilder;
373 if (TypeArguments != null)
374 return builder.MakeGenericMethod (TypeArguments);
379 public override string GetSignatureForError ()
381 string s = TypeManager.CSharpSignature (invoke_mb);
382 return s.Substring (0, s.IndexOf (".Invoke("));
385 public bool EmitMethod (EmitContext ec)
387 if (!CreateMethodHost (ec))
390 MethodBuilder builder = method.MethodData.MethodBuilder;
391 ILGenerator ig = builder.GetILGenerator ();
394 Parameters.LabelParameters (aec, builder);
397 // Adjust based on the computed state of the
398 // method from CreateMethodHost
400 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
402 aec.EmitMeta (Block, amp);
403 aec.EmitResolvedTopBlock (Block, unreachable);
407 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
409 TypeBuilder container = ec.TypeContainer.TypeBuilder;
410 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
412 scope.ScopeTypeBuilder = container.DefineNestedType (
413 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
414 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
416 Type [] constructor_types = TypeManager.NoTypes;
417 Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
418 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
419 MethodAttributes.Public | MethodAttributes.HideBySig |
420 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
421 CallingConventions.HasThis, constructor_types);
422 InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
423 TypeManager.RegisterMethod (scope.ScopeConstructor, parameter_info, constructor_types);
425 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
426 cig.Emit (OpCodes.Ldarg_0);
427 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
428 cig.Emit (OpCodes.Ret);
431 public static void Error_AddressOfCapturedVar (string name, Location loc)
433 Report.Error (1686, loc,
434 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
440 // This will emit the code for the delegate, as well delegate creation on the host
442 public class AnonymousDelegate : DelegateCreation {
445 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
452 public override Expression DoResolve (EmitContext ec)
454 eclass = ExprClass.Value;
459 public override void Emit (EmitContext ec)
461 if (!am.EmitMethod (ec))
465 // Now emit the delegate creation.
467 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
468 delegate_instance_expression = new AnonymousInstance (am);
470 Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
471 constructor_method = ((MethodGroupExpr) ml).Methods [0];
472 delegate_method = am.GetMethodBuilder ();
476 class AnonymousInstance : Expression {
479 public AnonymousInstance (AnonymousMethod am)
482 eclass = ExprClass.Value;
485 public override Expression DoResolve (EmitContext ec)
490 public override void Emit (EmitContext ec)
492 am.aec.EmitMethodHostInstance (ec, am);
497 class CapturedParameter {
499 public FieldBuilder FieldBuilder;
502 public CapturedParameter (Type type, int idx)
510 // Here we cluster all the variables captured on a given scope, we also
511 // keep some extra information that might be required on each scope.
513 public class ScopeInfo {
514 public CaptureContext CaptureContext;
515 public ScopeInfo ParentScope;
516 public Block ScopeBlock;
517 public bool NeedThis = false;
518 public bool HostsParameters = false;
520 // For tracking the number of scopes created.
525 ArrayList locals = new ArrayList ();
526 ArrayList children = new ArrayList ();
529 // The types and fields generated
531 public TypeBuilder ScopeTypeBuilder;
532 public ConstructorBuilder ScopeConstructor;
533 public FieldBuilder THIS;
534 public FieldBuilder ParentLink;
537 // Points to the object of type `ScopeTypeBuilder' that
538 // holds the data for the scope
540 LocalBuilder scope_instance;
542 public ScopeInfo (CaptureContext cc, Block b)
551 public void AddLocal (LocalInfo li)
553 if (locals.Contains (li))
559 public bool IsCaptured (LocalInfo li)
561 return locals.Contains (li);
564 internal void AddChild (ScopeInfo si)
566 if (children.Contains (si))
570 // If any of the current children should be a children of `si', move them there
572 ArrayList move_queue = null;
573 foreach (ScopeInfo child in children){
574 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
575 if (move_queue == null)
576 move_queue = new ArrayList ();
577 move_queue.Add (child);
578 child.ParentScope = si;
585 if (move_queue != null){
586 foreach (ScopeInfo child in move_queue){
587 children.Remove (child);
592 static int indent = 0;
596 for (int i = 0; i < indent; i++)
602 //Console.WriteLine (Environment.StackTrace);
604 Console.WriteLine ("START");
607 Console.WriteLine ("NeedThis=" + NeedThis);
608 foreach (LocalInfo li in locals){
610 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
613 foreach (ScopeInfo si in children)
617 Console.WriteLine ("END");
620 public string MakeHelperName ()
622 return String.Format ("<>AnonHelp<{0}>", id);
625 private string MakeFieldName (string local_name)
627 return "<" + id + ":" + local_name + ">";
630 public void EmitScopeType (EmitContext ec)
634 if (ScopeTypeBuilder != null)
637 TypeBuilder 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 ("<>parent", ParentScope.ScopeTypeBuilder,
651 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 (ScopeTypeBuilder);
701 ig.Emit (OpCodes.Newobj, (ConstructorInfo) 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 static void DoPath (StringBuilder sb, ScopeInfo start)
766 if (start.ParentScope != null){
767 DoPath (sb, start.ParentScope);
770 sb.Append ((start.id).ToString ());
773 public override string ToString ()
775 StringBuilder sb = new StringBuilder ();
778 if (CaptureContext != null){
779 sb.Append (CaptureContext.ToString ());
786 return sb.ToString ();
791 // CaptureContext objects are created on demand if a method has
792 // anonymous methods and kept on the ToplevelBlock.
794 // If they exist, all ToplevelBlocks in the containing block are
795 // linked together (children pointing to their parents).
797 public class CaptureContext {
798 public static int count;
803 // Points to the toplevel block that owns this CaptureContext
805 ToplevelBlock toplevel_owner;
806 Hashtable scopes = new Hashtable ();
807 bool have_captured_vars = false;
808 bool referenced_this = false;
809 ScopeInfo topmost = null;
814 Hashtable captured_fields = new Hashtable ();
815 Hashtable captured_variables = new Hashtable ();
816 public Hashtable captured_parameters = new Hashtable ();
817 public AnonymousContainer Host;
819 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
820 AnonymousContainer host)
823 this.toplevel_owner = toplevel_owner;
830 void DoPath (StringBuilder sb, CaptureContext cc)
832 if (cc.ParentCaptureContext != null){
833 DoPath (sb, cc.ParentCaptureContext);
836 sb.Append (cc.cc_id.ToString ());
839 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
841 toplevel_owner = new_toplevel;
844 for (CaptureContext cc = ParentCaptureContext; cc != null;
845 cc = cc.ParentCaptureContext) {
850 public override string ToString ()
852 StringBuilder sb = new StringBuilder ();
856 return sb.ToString ();
859 public ToplevelBlock ParentToplevel {
861 return toplevel_owner.Container;
865 public CaptureContext ParentCaptureContext {
867 ToplevelBlock parent = ParentToplevel;
869 return (parent == null) ? null : parent.CaptureContext;
873 // Returns the deepest of two scopes
874 public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
886 // If they Scopes are on the same CaptureContext, we do the double
887 // checks just so if there is an invariant change in the future,
888 // we get the exception at the end
890 for (p = a; p != null; p = p.ParentScope)
894 for (p = b; p != null; p = p.ParentScope)
898 CaptureContext ca = a.CaptureContext;
899 CaptureContext cb = b.CaptureContext;
901 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
905 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
908 throw new Exception ("Should never be reached");
911 void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
913 am.Scope = Deepest (am.Scope, scope);
916 void LinkScope (ScopeInfo scope, int id)
918 ScopeInfo parent = (ScopeInfo) scopes [id];
919 scope.ParentScope = parent;
920 parent.AddChild (scope);
922 if (scope == topmost)
926 public void AddLocal (AnonymousContainer am, LocalInfo li)
928 if (li.Block.Toplevel != toplevel_owner){
929 ParentCaptureContext.AddLocal (am, li);
932 int block_id = li.Block.ID;
934 if (scopes [block_id] == null){
935 scope = new ScopeInfo (this, li.Block);
936 scopes [block_id] = scope;
938 scope = (ScopeInfo) scopes [block_id];
940 if (topmost == null){
945 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
946 if (scopes [b.ID] != null){
947 LinkScope (scope, b.ID);
952 if (scope.ParentScope == null && ParentCaptureContext != null){
953 CaptureContext pcc = ParentCaptureContext;
955 for (Block b = am.ContainingBlock; b != null; b = b.Parent){
956 if (pcc.scopes [b.ID] != null){
957 pcc.LinkScope (scope, b.ID);
968 AdjustMethodScope (Host, topmost);
973 AdjustMethodScope (am, scope);
975 if (captured_variables [li] != null)
978 have_captured_vars = true;
979 captured_variables [li] = li;
984 // Retursn the CaptureContext for the block that defines the parameter `name'
986 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
988 ToplevelBlock container = current.Container;
989 if (container != null){
990 CaptureContext cc = _ContextForParameter (container, name);
994 if (current.IsParameterReference (name))
995 return current.ToplevelBlockCaptureContext;
999 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1001 CaptureContext cc = _ContextForParameter (current, name);
1003 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1008 // Records the captured parameter at the appropriate CaptureContext
1010 public void AddParameter (EmitContext ec, AnonymousContainer am,
1011 string name, Type t, int idx)
1013 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1015 cc.AddParameterToContext (am, name, t, idx);
1019 // Records the parameters in the context
1021 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1023 if (captured_parameters == null)
1024 captured_parameters = new Hashtable ();
1025 if (captured_parameters [name] != null)
1027 captured_parameters [name] = new CapturedParameter (t, idx);
1029 if (topmost == null){
1031 // Create one ScopeInfo, if there are none.
1033 topmost = new ScopeInfo (this, toplevel_owner);
1034 scopes [toplevel_owner.ID] = topmost;
1037 // If the topmost ScopeInfo is not at the topblock level, insert
1038 // a new ScopeInfo there.
1040 // FIXME: This code probably should be evolved to be like the code
1043 if (topmost.ScopeBlock != toplevel_owner){
1044 ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
1045 ScopeInfo old_top = topmost;
1046 scopes [toplevel_owner.ID] = topmost;
1047 topmost.ParentScope = par_si;
1049 topmost.AddChild (old_top);
1053 topmost.HostsParameters = true;
1054 AdjustMethodScope (am, topmost);
1058 // Captured fields are only recorded on the topmost CaptureContext, because that
1059 // one is the one linked to the owner of instance fields
1061 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1063 if (fe.FieldInfo.IsStatic)
1064 throw new Exception ("Attempt to register a static field as a captured field");
1065 CaptureContext parent = ParentCaptureContext;
1066 if (parent != null) {
1067 parent.AddField (ec, am, fe);
1071 if (topmost == null){
1073 // Create one ScopeInfo, if there are none.
1075 topmost = new ScopeInfo (this, toplevel_owner);
1076 scopes [toplevel_owner.ID] = topmost;
1079 AdjustMethodScope (am, topmost);
1082 public void CaptureThis (AnonymousContainer am)
1084 CaptureContext parent = ParentCaptureContext;
1085 if (parent != null) {
1086 parent.CaptureThis (am);
1089 referenced_this = true;
1091 if (topmost == null){
1093 // Create one ScopeInfo, if there are none.
1095 topmost = new ScopeInfo (this, toplevel_owner);
1096 scopes [toplevel_owner.ID] = topmost;
1098 AdjustMethodScope (am, topmost);
1102 public bool HaveCapturedVariables {
1104 return have_captured_vars;
1108 public bool HaveCapturedFields {
1110 CaptureContext parent = ParentCaptureContext;
1112 return parent.HaveCapturedFields;
1113 return captured_fields.Count > 0;
1117 public bool IsCaptured (LocalInfo local)
1119 foreach (ScopeInfo si in scopes.Values){
1120 if (si.IsCaptured (local))
1127 // Returns whether the parameter is captured
1129 public bool IsParameterCaptured (string name)
1131 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1134 if (captured_parameters != null)
1135 return captured_parameters [name] != null;
1139 public void EmitAnonymousHelperClasses (EmitContext ec)
1141 if (topmost != null){
1142 topmost.NeedThis = HaveCapturedFields || referenced_this;
1143 topmost.EmitScopeType (ec);
1147 public void CloseAnonymousHelperClasses ()
1149 if (topmost != null)
1150 topmost.CloseTypes ();
1153 public void EmitInitScope (EmitContext ec)
1155 EmitAnonymousHelperClasses (ec);
1156 if (topmost != null)
1157 topmost.EmitInitScope (ec);
1160 ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
1164 si = (ScopeInfo) scopes [b.ID];
1166 throw new Exception ("Si is null for block " + b.ID);
1167 si.EmitInitScope (ec);
1173 // Emits the opcodes necessary to load the instance of the captured
1176 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1177 AnonymousContainer am)
1179 ILGenerator ig = ec.ig;
1182 if (li.Block.Toplevel == toplevel_owner){
1183 si = GetScopeFromBlock (ec, li.Block);
1184 si.EmitScopeInstance (ig);
1189 ig.Emit (OpCodes.Ldarg_0);
1191 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1195 while (si.ScopeBlock.ID != li.Block.ID){
1196 if (si.ParentLink != null)
1197 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1198 si = si.ParentScope;
1201 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1202 while (si.ScopeBlock.ID != li.Block.ID){
1203 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1204 si = si.ParentScope;
1207 throw new Exception (
1208 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1209 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1216 // Internal routine that loads the instance to reach parameter `name'
1218 void EmitParameterInstance (EmitContext ec, string name)
1220 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1222 cc.EmitParameterInstance (ec, name);
1226 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1227 if (par_info != null){
1229 // FIXME: implementing this.
1232 ILGenerator ig = ec.ig;
1236 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1237 si = GetScopeFromBlock (ec, toplevel_owner);
1238 si.EmitScopeInstance (ig);
1240 si = ec.CurrentAnonymousMethod.Scope;
1241 ig.Emit (OpCodes.Ldarg_0);
1245 while (si.ParentLink != null) {
1246 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1247 si = si.ParentScope;
1253 // Emits the code necessary to load the parameter named `name' within
1254 // an anonymous method.
1256 public void EmitParameter (EmitContext ec, string name)
1258 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1260 cc.EmitParameter (ec, name);
1263 EmitParameterInstance (ec, name);
1264 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1265 if (par_info != null){
1267 // FIXME: implementing this.
1270 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1274 // Implements the assignment of `source' to the paramenter named `name' within
1275 // an anonymous method.
1277 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
1279 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1281 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
1284 ILGenerator ig = ec.ig;
1285 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1287 EmitParameterInstance (ec, name);
1290 ig.Emit (OpCodes.Dup);
1291 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1295 // Emits the address for the parameter named `name' within
1296 // an anonymous method.
1298 public void EmitAddressOfParameter (EmitContext ec, string name)
1300 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1302 cc.EmitAddressOfParameter (ec, name);
1305 EmitParameterInstance (ec, name);
1306 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1307 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1311 // The following methods are only invoked on the host for the
1312 // anonymous method.
1314 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1316 ILGenerator ig = target.ig;
1317 ScopeInfo si = am.Scope;
1320 ig.Emit (OpCodes.Ldarg_0);
1324 si.EmitInitScope (target);
1325 si.EmitScopeInstance (ig);
1328 ArrayList all_scopes = new ArrayList ();
1330 public void AddScope (ScopeInfo si)
1332 all_scopes.Add (si);
1333 toplevel_owner.RegisterCaptureContext (this);
1337 // Links any scopes that were not linked previously
1339 public void AdjustScopes ()
1341 foreach (ScopeInfo scope in all_scopes){
1342 if (scope.ParentScope != null)
1345 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1346 if (scopes [b.ID] != null){
1347 LinkScope (scope, b.ID);
1352 if (scope.ParentScope == null && ParentCaptureContext != null){
1353 CaptureContext pcc = ParentCaptureContext;
1355 for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
1356 if (pcc.scopes [b.ID] != null){
1357 pcc.LinkScope (scope, b.ID);