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 class AnonymousMethod
: Expression
{
24 // Used to generate unique method names.
25 static int anonymous_method_count
;
27 // An array list of AnonymousMethodParameter or null
28 public Parameters Parameters
;
31 // The block that makes up the body for the anonymous mehtod
33 public ToplevelBlock Block
;
36 // The container block for this anonymous method.
38 public Block ContainingBlock
;
41 // The implicit method we create
47 // The emit context for the anonymous method
48 public EmitContext aec
;
49 public InternalParameters amp
;
53 // The modifiers applied to the method, we aggregate them
55 int method_modifiers
= Modifiers
.INTERNAL
;
58 // During the resolve stage of the anonymous method body,
59 // we discover the actual scope where we are hosted, or
60 // null to host the method in the same class
62 public ScopeInfo Scope
;
65 // Points to our container anonymous method if its present
67 public AnonymousMethod ContainerAnonymousMethod
;
69 public AnonymousMethod (Parameters parameters
, ToplevelBlock container
, ToplevelBlock block
, Location l
)
71 Parameters
= parameters
;
76 // The order is important: this setups the CaptureContext tree hierarchy.
78 container
.SetHaveAnonymousMethods (l
, this);
79 block
.SetHaveAnonymousMethods (l
, this);
82 public override Expression
DoResolve (EmitContext ec
)
85 // Set class type, set type
88 eclass
= ExprClass
.Value
;
91 // This hack means `The type is not accessible
92 // anywhere', we depend on special conversion
95 type
= TypeManager
.anonymous_method_type
;
100 public override void Emit (EmitContext ec
)
102 // nothing, as we only exist to not do anything.
106 // Creates the host for the anonymous method
108 bool CreateMethodHost (EmitContext ec
, Type return_type
)
111 // Crude hack follows: we replace the TypeBuilder during the
112 // definition to get the method hosted in the right class
115 TypeBuilder current_type
= ec
.TypeContainer
.TypeBuilder
;
116 TypeBuilder type_host
= Scope
== null ? current_type
: Scope
.ScopeTypeBuilder
;
118 if (current_type
== null)
119 throw new Exception ("The current_type is null");
121 if (type_host
== null)
122 throw new Exception ("Type host is null");
124 if (current_type
== type_host
&& ec
.IsStatic
){
126 method_modifiers
|= Modifiers
.STATIC
;
130 method
= new Method (
131 (TypeContainer
) ec
.TypeContainer
,
132 new TypeExpression (return_type
, loc
),
133 method_modifiers
, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count
++),
134 Parameters
, null, loc
);
135 method
.Block
= Block
;
139 // Swap the TypeBuilder while we define the method, then restore
141 if (current_type
!= null)
142 ec
.TypeContainer
.TypeBuilder
= type_host
;
143 bool res
= method
.Define ();
144 if (current_type
!= null)
145 ec
.TypeContainer
.TypeBuilder
= current_type
;
149 void Error_ParameterMismatch (Type t
)
151 Report
.Error (1661, loc
, "Anonymous method could not be converted to delegate `" +
152 "{0}' since there is a parameter mismatch", t
);
156 // Returns true if this anonymous method can be implicitly
157 // converted to the delegate type `delegate_type'
159 public Expression
Compatible (EmitContext ec
, Type delegate_type
, bool probe
)
162 // At this point its the first time we know the return type that is
163 // needed for the anonymous method. We create the method here.
166 invoke_mb
= (MethodInfo
) Delegate
.GetInvokeMethod (ec
, delegate_type
, loc
);
167 ParameterData invoke_pd
= Invocation
.GetParameterData (invoke_mb
);
170 // If implicit parameters are set, then we must check for out in the parameters
171 // and flag it accordingly.
173 bool out_invalid_check
= false;
175 if (Parameters
== null){
177 out_invalid_check
= true;
180 // We provide a set of inaccessible parameters
183 for (i
= 0; i
< invoke_pd
.Count
; i
++){
184 if (invoke_pd
.ParameterModifier (i
) == Parameter
.Modifier
.PARAMS
)
187 int n
= invoke_pd
.Count
- (params_idx
!= -1 ? 1 : 0);
188 Parameter
[] fixedpars
= new Parameter
[n
];
190 for (i
= j
= 0; i
< invoke_pd
.Count
; i
++){
191 if (invoke_pd
.ParameterModifier (i
) == Parameter
.Modifier
.PARAMS
)
193 fixedpars
[j
] = new Parameter (
194 new TypeExpression (invoke_pd
.ParameterType (i
), loc
),
195 "+" + j
, invoke_pd
.ParameterModifier (i
), null);
199 Parameter variable
= null;
200 if (params_idx
!= -1){
201 variable
= new Parameter (
202 new TypeExpression (invoke_pd
.ParameterType (params_idx
), loc
),
203 "+" + params_idx
, invoke_pd
.ParameterModifier (params_idx
), null);
206 Parameters
= new Parameters (fixedpars
, variable
, loc
);
210 // First, parameter types of `delegate_type' must be compatible
211 // with the anonymous method.
213 amp
= new InternalParameters (Parameters
.GetParameterInfo (ec
), Parameters
);
215 if (amp
.Count
!= invoke_pd
.Count
){
217 Report
.Error (1593, loc
,
218 "Anonymous method has {0} parameters, while delegate requires {1}",
219 amp
.Count
, invoke_pd
.Count
);
220 Error_ParameterMismatch (delegate_type
);
225 for (int i
= 0; i
< amp
.Count
; i
++){
226 Parameter
.Modifier amp_mod
= amp
.ParameterModifier (i
);
227 if (amp_mod
!= invoke_pd
.ParameterModifier (i
)){
229 Report
.Error (1676, loc
,
230 "Signature mismatch in parameter modifier for parameter #0", i
+ 1);
231 Error_ParameterMismatch (delegate_type
);
236 if (amp
.ParameterType (i
) != invoke_pd
.ParameterType (i
)){
238 Report
.Error (1678, loc
,
239 "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i
+ 1,
240 TypeManager
.CSharpName (invoke_pd
.ParameterType (i
)),
241 TypeManager
.CSharpName (amp
.ParameterType (i
)));
242 Error_ParameterMismatch (delegate_type
);
247 if (out_invalid_check
&& (invoke_pd
.ParameterModifier (i
) & Parameter
.Modifier
.OUT
) != 0){
249 Report
.Error (1676, loc
,"Parameter {0} must include the `out' modifier ", i
+1);
250 Error_ParameterMismatch (delegate_type
);
257 // If we are only probing, return ourselves
263 // Second: the return type of the delegate must be compatible with
264 // the anonymous type. Instead of doing a pass to examine the block
265 // we satisfy the rule by setting the return type on the EmitContext
266 // to be the delegate type return type.
269 //MethodBuilder builder = method_data.MethodBuilder;
270 //ILGenerator ig = builder.GetILGenerator ();
273 aec
= new EmitContext (
274 ec
.TypeContainer
, ec
.DeclSpace
, loc
, null,
275 invoke_mb
.ReturnType
,
276 /* REVIEW */ (ec
.InIterator
? Modifiers
.METHOD_YIELDS
: 0) |
277 (ec
.InUnsafe
? Modifiers
.UNSAFE
: 0),
278 /* No constructor */ false);
280 aec
.CurrentAnonymousMethod
= this;
281 ContainerAnonymousMethod
= ec
.CurrentAnonymousMethod
;
282 ContainingBlock
= ec
.CurrentBlock
;
284 if (aec
.ResolveTopBlock (ec
, Block
, amp
, loc
, out unreachable
))
285 return new AnonymousDelegate (this, delegate_type
, loc
).Resolve (ec
);
290 public MethodBuilder
GetMethodBuilder ()
292 return method
.MethodData
.MethodBuilder
;
295 public void EmitMethod (EmitContext ec
)
297 if (!CreateMethodHost (ec
, invoke_mb
.ReturnType
))
300 MethodBuilder builder
= GetMethodBuilder ();
301 ILGenerator ig
= builder
.GetILGenerator ();
304 Parameters
.LabelParameters (aec
, builder
, loc
);
307 // Adjust based on the computed state of the
308 // method from CreateMethodHost
310 if ((method_modifiers
& Modifiers
.STATIC
) != 0)
313 aec
.EmitMeta (Block
, amp
);
314 aec
.EmitResolvedTopBlock (Block
, unreachable
);
319 // This will emit the code for the delegate, as well delegate creation on the host
321 public class AnonymousDelegate
: DelegateCreation
{
324 public AnonymousDelegate (AnonymousMethod am
, Type target_type
, Location l
)
331 public override Expression
DoResolve (EmitContext ec
)
333 eclass
= ExprClass
.Value
;
337 public override void Emit (EmitContext ec
)
342 // Now emit the delegate creation.
344 if ((am
.method
.ModFlags
& Modifiers
.STATIC
) == 0)
345 delegate_instance_expression
= new AnonymousInstance (am
);
347 Expression ml
= Expression
.MemberLookup (ec
, type
, ".ctor", loc
);
348 constructor_method
= ((MethodGroupExpr
) ml
).Methods
[0];
349 delegate_method
= am
.GetMethodBuilder ();
353 class AnonymousInstance
: Expression
{
356 public AnonymousInstance (AnonymousMethod am
)
359 eclass
= ExprClass
.Value
;
362 public override Expression
DoResolve (EmitContext ec
)
367 public override void Emit (EmitContext ec
)
369 am
.aec
.EmitMethodHostInstance (ec
, am
);
374 class CapturedParameter
{
376 public FieldBuilder FieldBuilder
;
379 public CapturedParameter (Type type
, int idx
)
387 // Here we cluster all the variables captured on a given scope, we also
388 // keep some extra information that might be required on each scope.
390 public class ScopeInfo
{
391 public CaptureContext CaptureContext
;
392 public ScopeInfo ParentScope
;
393 public Block ScopeBlock
;
394 public bool NeedThis
= false;
395 public bool HostsParameters
= false;
397 // For tracking the number of scopes created.
402 ArrayList locals
= new ArrayList ();
403 ArrayList children
= new ArrayList ();
406 // The types and fields generated
408 public TypeBuilder ScopeTypeBuilder
;
409 public ConstructorBuilder ScopeConstructor
;
410 public FieldBuilder THIS
;
411 public FieldBuilder ParentLink
;
414 // Points to the object of type `ScopeTypeBuilder' that
415 // holds the data for the scope
417 public LocalBuilder ScopeInstance
;
420 public ScopeInfo (CaptureContext cc
, Block b
)
429 public void AddLocal (LocalInfo li
)
431 if (locals
.Contains (li
))
437 public bool IsCaptured (LocalInfo li
)
439 return locals
.Contains (li
);
442 public void AddChild (ScopeInfo si
)
444 if (children
.Contains (si
))
449 static int indent
= 0;
453 for (int i
= 0; i
< indent
; i
++)
459 //Console.WriteLine (Environment.StackTrace);
461 Console
.WriteLine ("START");
464 Console
.WriteLine ("NeedThis=" + NeedThis
);
465 foreach (LocalInfo li
in locals
){
467 Console
.WriteLine ("var {0}", li
.Name
);
470 foreach (ScopeInfo si
in children
)
474 Console
.WriteLine ("END");
477 public string MakeHelperName ()
479 return String
.Format ("<>AnonHelp<{0}>", id
);
482 public void EmitScopeConstructor ()
484 Type
[] constructor_types
= TypeManager
.NoTypes
;
485 Parameters constructor_parameters
= Parameters
.EmptyReadOnlyParameters
;
486 ScopeConstructor
= ScopeTypeBuilder
.DefineConstructor (
487 MethodAttributes
.Public
| MethodAttributes
.HideBySig
|
488 MethodAttributes
.SpecialName
| MethodAttributes
.RTSpecialName
,
489 CallingConventions
.HasThis
, constructor_types
);
490 InternalParameters parameter_info
= new InternalParameters (constructor_types
, constructor_parameters
);
491 TypeManager
.RegisterMethod (ScopeConstructor
, parameter_info
, constructor_types
);
493 ILGenerator cig
= ScopeConstructor
.GetILGenerator ();
494 cig
.Emit (OpCodes
.Ldarg_0
);
495 cig
.Emit (OpCodes
.Call
, TypeManager
.object_ctor
);
496 cig
.Emit (OpCodes
.Ret
);
499 public void EmitScopeType (EmitContext ec
)
503 if (ScopeTypeBuilder
!= null)
506 ILGenerator ig
= ec
.ig
;
507 TypeBuilder container
= ec
.TypeContainer
.TypeBuilder
;
509 ScopeTypeBuilder
= container
.DefineNestedType (
510 MakeHelperName (), TypeAttributes
.AutoLayout
| TypeAttributes
.Class
| TypeAttributes
.NestedAssembly
,
511 TypeManager
.object_type
, null);
514 THIS
= ScopeTypeBuilder
.DefineField ("<>THIS", container
, FieldAttributes
.Assembly
);
516 if (ParentScope
!= null){
517 if (ParentScope
.ScopeTypeBuilder
== null){
518 throw new Exception (String
.Format ("My parent has not been initialized {0} and {1}", ParentScope
, this));
521 ParentLink
= ScopeTypeBuilder
.DefineField ("<>parent", ParentScope
.ScopeTypeBuilder
,
522 FieldAttributes
.Assembly
);
525 if (NeedThis
&& ParentScope
!= null)
526 throw new Exception ("I was not expecting THIS && having a parent");
528 foreach (LocalInfo info
in locals
){
529 info
.FieldBuilder
= ScopeTypeBuilder
.DefineField (
530 info
.Name
, info
.VariableType
, FieldAttributes
.Assembly
);
533 if (HostsParameters
){
534 Hashtable captured_parameters
= CaptureContext
.captured_parameters
;
536 foreach (DictionaryEntry de
in captured_parameters
){
537 string name
= (string) de
.Key
;
538 CapturedParameter cp
= (CapturedParameter
) de
.Value
;
541 fb
= ScopeTypeBuilder
.DefineField ("<p:" + name
+ ">", cp
.Type
, FieldAttributes
.Assembly
);
542 cp
.FieldBuilder
= fb
;
546 EmitScopeConstructor ();
547 foreach (ScopeInfo si
in children
){
548 si
.EmitScopeType (ec
);
552 public void CloseTypes ()
554 RootContext
.RegisterHelperClass (ScopeTypeBuilder
);
555 foreach (ScopeInfo si
in children
)
560 // Emits the initialization code for the scope
562 public void EmitInitScope (EmitContext ec
)
564 ILGenerator ig
= ec
.ig
;
569 ig
.Emit (OpCodes
.Newobj
, (ConstructorInfo
) ScopeConstructor
);
570 ScopeInstance
= ig
.DeclareLocal (ScopeTypeBuilder
);
571 ig
.Emit (OpCodes
.Stloc
, ScopeInstance
);
574 ig
.Emit (OpCodes
.Ldloc
, ScopeInstance
);
575 ig
.Emit (OpCodes
.Ldarg_0
);
576 ig
.Emit (OpCodes
.Stfld
, THIS
);
580 // Copy the parameter values, if any
582 int extra
= ec
.IsStatic
? 0 : 1;
583 if (HostsParameters
){
584 Hashtable captured_parameters
= CaptureContext
.captured_parameters
;
586 foreach (DictionaryEntry de
in captured_parameters
){
587 string name
= (string) de
.Key
;
588 CapturedParameter cp
= (CapturedParameter
) de
.Value
;
590 ig
.Emit (OpCodes
.Ldloc
, ScopeInstance
);
591 ParameterReference
.EmitLdArg (ig
, cp
.Idx
+ extra
);
592 ig
.Emit (OpCodes
.Stfld
, cp
.FieldBuilder
);
596 if (ParentScope
!= null){
598 // Only emit initialization in our capturecontext world
600 if (ParentScope
.CaptureContext
== CaptureContext
){
601 ig
.Emit (OpCodes
.Ldloc
, ScopeInstance
);
602 ig
.Emit (OpCodes
.Ldloc
, ParentScope
.ScopeInstance
);
603 ig
.Emit (OpCodes
.Stfld
, ParentLink
);
605 ig
.Emit (OpCodes
.Ldloc
, ScopeInstance
);
606 ig
.Emit (OpCodes
.Ldarg_0
);
607 ig
.Emit (OpCodes
.Stfld
, ParentLink
);
613 static void DoPath (StringBuilder sb
, ScopeInfo start
)
615 if (start
.ParentScope
!= null){
616 DoPath (sb
, start
.ParentScope
);
619 sb
.Append ((start
.id
).ToString ());
622 public override string ToString ()
624 StringBuilder sb
= new StringBuilder ();
627 if (CaptureContext
!= null){
628 sb
.Append (CaptureContext
.ToString ());
635 return sb
.ToString ();
640 // CaptureContext objects are created on demand if a method has
641 // anonymous methods and kept on the ToplevelBlock.
643 // If they exist, all ToplevelBlocks in the containing block are
644 // linked together (children pointing to their parents).
646 public class CaptureContext
{
647 public static int count
;
652 // Points to the toplevel block that owns this CaptureContext
654 ToplevelBlock toplevel_owner
;
655 Hashtable scopes
= new Hashtable ();
656 bool have_captured_vars
= false;
657 ScopeInfo topmost
= null;
662 Hashtable captured_fields
= new Hashtable ();
663 Hashtable captured_variables
= new Hashtable ();
664 public Hashtable captured_parameters
= new Hashtable ();
665 public AnonymousMethod Host
;
667 public CaptureContext (ToplevelBlock toplevel_owner
, Location loc
, AnonymousMethod host
)
670 this.toplevel_owner
= toplevel_owner
;
677 void DoPath (StringBuilder sb
, CaptureContext cc
)
679 if (cc
.ParentCaptureContext
!= null){
680 DoPath (sb
, cc
.ParentCaptureContext
);
683 sb
.Append (cc_id
.ToString ());
686 public override string ToString ()
688 ToplevelBlock parent
= ParentToplevel
;
689 StringBuilder sb
= new StringBuilder ();
694 return sb
.ToString ();
697 public ToplevelBlock ParentToplevel
{
699 return toplevel_owner
.Container
;
703 public CaptureContext ParentCaptureContext
{
705 ToplevelBlock parent
= ParentToplevel
;
707 return (parent
== null) ? null : parent
.CaptureContext
;
711 // Returns the deepest of two scopes
712 public ScopeInfo
Deepest (ScopeInfo a
, ScopeInfo b
)
724 // If they Scopes are on the same CaptureContext, we do the double
725 // checks just so if there is an invariant change in the future,
726 // we get the exception at the end
728 for (p
= a
; p
!= null; p
= p
.ParentScope
)
732 for (p
= b
; p
!= null; p
= p
.ParentScope
)
736 CaptureContext ca
= a
.CaptureContext
;
737 CaptureContext cb
= b
.CaptureContext
;
739 for (CaptureContext c
= ca
; c
!= null; c
= c
.ParentCaptureContext
)
743 for (CaptureContext c
= cb
; c
!= null; c
= c
.ParentCaptureContext
)
746 throw new Exception ("Should never be reached");
749 void AdjustMethodScope (AnonymousMethod am
, ScopeInfo scope
)
751 am
.Scope
= Deepest (am
.Scope
, scope
);
754 void LinkScope (ScopeInfo scope
, int id
)
756 ScopeInfo parent
= (ScopeInfo
) scopes
[id
];
757 scope
.ParentScope
= parent
;
758 parent
.AddChild (scope
);
760 if (scope
== topmost
)
764 public void AddLocal (AnonymousMethod am
, LocalInfo li
)
766 if (li
.Block
.Toplevel
!= toplevel_owner
){
767 ParentCaptureContext
.AddLocal (am
, li
);
770 int block_id
= li
.Block
.ID
;
772 if (scopes
[block_id
] == null){
773 scope
= new ScopeInfo (this, li
.Block
);
774 scopes
[block_id
] = scope
;
776 scope
= (ScopeInfo
) scopes
[block_id
];
778 if (topmost
== null){
782 for (Block b
= scope
.ScopeBlock
.Parent
; b
!= null; b
= b
.Parent
){
783 if (scopes
[b
.ID
] != null){
784 LinkScope (scope
, b
.ID
);
789 if (scope
.ParentScope
== null && ParentCaptureContext
!= null){
790 CaptureContext pcc
= ParentCaptureContext
;
792 for (Block b
= am
.ContainingBlock
; b
!= null; b
= b
.Parent
){
793 if (pcc
.scopes
[b
.ID
] != null){
794 pcc
.LinkScope (scope
, b
.ID
);
805 AdjustMethodScope (Host
, topmost
);
810 AdjustMethodScope (am
, scope
);
812 if (captured_variables
[li
] != null)
815 have_captured_vars
= true;
816 captured_variables
[li
] = li
;
821 // Retursn the CaptureContext for the block that defines the parameter `name'
823 static CaptureContext
_ContextForParameter (ToplevelBlock current
, string name
)
825 ToplevelBlock container
= current
.Container
;
826 if (container
!= null){
827 CaptureContext cc
= _ContextForParameter (container
, name
);
831 if (current
.IsParameterReference (name
))
832 return current
.ToplevelBlockCaptureContext
;
836 static CaptureContext
ContextForParameter (ToplevelBlock current
, string name
)
838 CaptureContext cc
= _ContextForParameter (current
, name
);
840 throw new Exception (String
.Format ("request for parameteter {0} failed: not found", name
));
845 // Records the captured parameter at the appropriate CaptureContext
847 public void AddParameter (EmitContext ec
, AnonymousMethod am
, string name
, Type t
, int idx
)
849 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
851 cc
.AddParameterToContext (am
, name
, t
, idx
);
855 // Records the parameters in the context
857 void AddParameterToContext (AnonymousMethod am
, string name
, Type t
, int idx
)
859 if (captured_parameters
== null)
860 captured_parameters
= new Hashtable ();
861 if (captured_parameters
[name
] != null)
863 captured_parameters
[name
] = new CapturedParameter (t
, idx
);
865 if (topmost
== null){
867 // Create one ScopeInfo, if there are none.
869 topmost
= new ScopeInfo (this, toplevel_owner
);
870 scopes
[toplevel_owner
.ID
] = topmost
;
873 // If the topmost ScopeInfo is not at the topblock level, insert
874 // a new ScopeInfo there.
876 if (topmost
.ScopeBlock
!= toplevel_owner
){
877 ScopeInfo par_si
= new ScopeInfo (this, toplevel_owner
);
878 scopes
[toplevel_owner
.ID
] = topmost
;
879 topmost
.ParentScope
= par_si
;
884 topmost
.HostsParameters
= true;
885 AdjustMethodScope (am
, topmost
);
889 // Captured fields are only recorded on the topmost CaptureContext, because that
890 // one is the one linked to the owner of instance fields
892 public void AddField (FieldExpr fe
)
894 if (fe
.FieldInfo
.IsStatic
)
895 throw new Exception ("Attempt to register a static field as a captured field");
897 CaptureContext parent
= ParentCaptureContext
;
899 parent
.AddField (fe
);
901 captured_fields
[fe
] = fe
;
905 public bool HaveCapturedVariables
{
907 return have_captured_vars
;
911 public bool HaveCapturedFields
{
913 CaptureContext parent
= ParentCaptureContext
;
915 return parent
.HaveCapturedFields
;
916 return captured_fields
.Count
> 0;
920 public bool IsCaptured (LocalInfo local
)
922 foreach (ScopeInfo si
in scopes
.Values
){
923 if (si
.IsCaptured (local
))
930 // Returns whether the parameter is captured
932 public bool IsParameterCaptured (string name
)
934 if (ParentCaptureContext
!= null && ParentCaptureContext
.IsParameterCaptured (name
))
937 if (captured_parameters
!= null)
938 return captured_parameters
[name
] != null;
942 public void EmitHelperClasses (EmitContext ec
)
944 if (topmost
!= null){
945 topmost
.NeedThis
= HaveCapturedFields
;
946 topmost
.EmitScopeType (ec
);
950 public void CloseHelperClasses ()
953 topmost
.CloseTypes ();
956 ScopeInfo
GetScopeFromBlock (EmitContext ec
, Block b
)
960 si
= (ScopeInfo
) scopes
[b
.ID
];
962 throw new Exception ("Si is null for block " + b
.ID
);
963 si
.EmitInitScope (ec
);
969 // Emits the opcodes necessary to load the instance of the captured
972 public void EmitCapturedVariableInstance (EmitContext ec
, LocalInfo li
, AnonymousMethod am
)
974 ILGenerator ig
= ec
.ig
;
977 if (li
.Block
.Toplevel
== toplevel_owner
){
978 si
= GetScopeFromBlock (ec
, li
.Block
);
979 ig
.Emit (OpCodes
.Ldloc
, si
.ScopeInstance
);
984 ig
.Emit (OpCodes
.Ldarg_0
);
986 while (si
.ScopeBlock
.ID
!= li
.Block
.ID
){
987 if (si
.ParentLink
!= null)
988 ig
.Emit (OpCodes
.Ldfld
, si
.ParentLink
);
991 throw new Exception (
992 String
.Format ("Never found block {0} starting at {1} while looking up {2}",
993 li
.Block
.ID
, am
.Scope
.ScopeBlock
.ID
, li
.Name
));
999 // Internal routine that loads the instance to reach parameter `name'
1001 void EmitParameterInstance (EmitContext ec
, string name
)
1003 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1005 cc
.EmitParameterInstance (ec
, name
);
1008 Block invocation_block
= ec
.CurrentBlock
;
1009 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1010 ILGenerator ig
= ec
.ig
;
1013 if (ec
.CurrentBlock
== toplevel_owner
){
1014 si
= GetScopeFromBlock (ec
, toplevel_owner
);
1015 ig
.Emit (OpCodes
.Ldloc
, si
.ScopeInstance
);
1019 si
= ec
.CurrentAnonymousMethod
.Scope
;
1020 ig
.Emit (OpCodes
.Ldarg_0
);
1022 while (si
.ParentLink
!= null) {
1023 ig
.Emit (OpCodes
.Ldfld
, si
.ParentLink
);
1024 si
= si
.ParentScope
;
1030 // Emits the code necessary to load the parameter named `name' within
1031 // an anonymous method.
1033 public void EmitParameter (EmitContext ec
, string name
)
1035 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1037 cc
.EmitParameter (ec
, name
);
1040 EmitParameterInstance (ec
, name
);
1041 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1042 ec
.ig
.Emit (OpCodes
.Ldfld
, par_info
.FieldBuilder
);
1046 // Implements the assignment of `source' to the paramenter named `name' within
1047 // an anonymous method.
1049 public void EmitAssignParameter (EmitContext ec
, string name
, Expression source
, bool leave_copy
, bool prepare_for_load
)
1051 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1053 cc
.EmitAssignParameter (ec
, name
, source
, leave_copy
, prepare_for_load
);
1056 ILGenerator ig
= ec
.ig
;
1057 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1059 EmitParameterInstance (ec
, name
);
1062 ig
.Emit (OpCodes
.Dup
);
1063 ig
.Emit (OpCodes
.Stfld
, par_info
.FieldBuilder
);
1067 // Emits the address for the parameter named `name' within
1068 // an anonymous method.
1070 public void EmitAddressOfParameter (EmitContext ec
, string name
)
1072 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1074 cc
.EmitAddressOfParameter (ec
, name
);
1077 EmitParameterInstance (ec
, name
);
1078 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1079 ec
.ig
.Emit (OpCodes
.Ldflda
, par_info
.FieldBuilder
);
1083 // The following methods are only invoked on the host for the
1084 // anonymous method.
1086 public void EmitMethodHostInstance (EmitContext target
, AnonymousMethod am
)
1088 ILGenerator ig
= target
.ig
;
1089 ScopeInfo si
= am
.Scope
;
1092 ig
.Emit (OpCodes
.Ldarg_0
);
1096 si
.EmitInitScope (target
);
1097 ig
.Emit (OpCodes
.Ldloc
, si
.ScopeInstance
);
1100 ArrayList all_scopes
= new ArrayList ();
1102 public void AddScope (ScopeInfo si
)
1104 all_scopes
.Add (si
);
1105 toplevel_owner
.RegisterCaptureContext (this);
1109 // Links any scopes that were not linked previously
1111 public void AdjustScopes ()
1113 foreach (ScopeInfo scope
in all_scopes
){
1114 if (scope
.ParentScope
!= null)
1117 for (Block b
= scope
.ScopeBlock
.Parent
; b
!= null; b
= b
.Parent
){
1118 if (scopes
[b
.ID
] != null){
1119 LinkScope (scope
, b
.ID
);
1124 if (scope
.ParentScope
== null && ParentCaptureContext
!= null){
1125 CaptureContext pcc
= ParentCaptureContext
;
1127 for (Block b
= Host
.ContainingBlock
; b
!= null; b
= b
.Parent
){
1128 if (pcc
.scopes
[b
.ID
] != null){
1129 pcc
.LinkScope (scope
, b
.ID
);