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 string[] TypeParameters
;
51 public Type
[] TypeArguments
;
52 protected bool unreachable
;
55 ScopeInfo method_scope
;
56 bool computed_method_scope
= false;
59 // The modifiers applied to the method, we aggregate them
61 protected int method_modifiers
= Modifiers
.PRIVATE
;
64 // Track the scopes that this method has used. At the
65 // end this is used to determine the ScopeInfo that will
68 ArrayList scopes_used
= new ArrayList ();
71 // Points to our container anonymous method if its present
73 public AnonymousContainer ContainerAnonymousMethod
;
75 protected AnonymousContainer (Parameters parameters
, ToplevelBlock container
,
76 ToplevelBlock block
, Location l
)
78 Parameters
= parameters
;
83 // The order is important: this setups the CaptureContext tree hierarchy.
85 if (container
== null) {
88 container
.SetHaveAnonymousMethods (l
, this);
89 block
.SetHaveAnonymousMethods (l
, this);
92 protected AnonymousContainer (Parameters parameters
, ToplevelBlock container
,
94 this (parameters
, container
, new ToplevelBlock (container
, parameters
, l
), l
)
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 public void RegisterScope (ScopeInfo scope
)
118 if (scopes_used
.Contains (scope
))
120 scopes_used
.Add (scope
);
123 // Returns the deepest of two scopes
124 ScopeInfo
Deepest (ScopeInfo a
, ScopeInfo b
)
136 // If they Scopes are on the same CaptureContext, we do the double
137 // checks just so if there is an invariant change in the future,
138 // we get the exception at the end
140 for (p
= a
; p
!= null; p
= p
.ParentScope
)
144 for (p
= b
; p
!= null; p
= p
.ParentScope
)
148 CaptureContext ca
= a
.CaptureContext
;
149 CaptureContext cb
= b
.CaptureContext
;
151 for (CaptureContext c
= ca
; c
!= null; c
= c
.ParentCaptureContext
)
155 for (CaptureContext c
= cb
; c
!= null; c
= c
.ParentCaptureContext
)
158 throw new Exception ("Should never be reached");
162 // Determines the proper host for a method considering the
163 // scopes it references
165 public void ComputeMethodHost ()
167 if (computed_method_scope
)
171 int top
= scopes_used
.Count
;
172 computed_method_scope
= true;
177 method_scope
= (ScopeInfo
) scopes_used
[0];
181 for (int i
= 1; i
< top
; i
++)
182 method_scope
= Deepest (method_scope
, (ScopeInfo
) scopes_used
[i
]);
185 public ScopeInfo Scope
{
187 if (computed_method_scope
)
191 // This means that ComputeMethodHost is not being called, most
192 // likely by someone who overwrote the CreateMethodHost method
194 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
199 protected abstract bool CreateMethodHost (EmitContext ec
);
201 public abstract void CreateScopeType (EmitContext ec
, ScopeInfo scope
);
203 public abstract Iterator Iterator
{
207 public abstract bool IsIterator
{
212 public class AnonymousMethod
: AnonymousContainer
216 public AnonymousMethod (TypeContainer host
, Parameters parameters
, ToplevelBlock container
,
217 ToplevelBlock block
, Location l
)
218 : base (parameters
, container
, block
, l
)
223 public override Iterator Iterator
{
227 public override bool IsIterator
{
228 get { return false; }
231 public override void Emit (EmitContext ec
)
233 // nothing, as we only exist to not do anything.
237 // Creates the host for the anonymous method
239 protected override bool CreateMethodHost (EmitContext ec
)
241 ComputeMethodHost ();
244 // Crude hack follows: we replace the TypeBuilder during the
245 // definition to get the method hosted in the right class
247 TypeBuilder current_type
= ec
.TypeContainer
.TypeBuilder
;
248 TypeBuilder type_host
= (Scope
== null ) ? current_type
: Scope
.ScopeTypeBuilder
;
250 if (current_type
== null)
251 throw new Exception ("The current_type is null");
253 if (type_host
== null)
254 throw new Exception (String
.Format ("Type host is null, method_host is {0}", Scope
== null ? "null" : "Not null"));
256 if (current_type
!= type_host
)
257 method_modifiers
= Modifiers
.INTERNAL
;
259 if (current_type
== type_host
&& ec
.IsStatic
){
260 method_modifiers
|= Modifiers
.STATIC
;
264 string name
= "<#AnonymousMethod>" + anonymous_method_count
++;
265 MemberName member_name
;
267 GenericMethod generic_method
= null;
268 if (TypeParameters
!= null) {
269 TypeArguments args
= new TypeArguments (loc
);
270 foreach (string t
in TypeParameters
)
271 args
.Add (new SimpleName (t
, loc
));
273 member_name
= new MemberName (name
, args
, loc
);
275 generic_method
= new GenericMethod (
276 ec
.DeclContainer
.NamespaceEntry
,
277 (TypeContainer
) ec
.TypeContainer
, member_name
,
278 new TypeExpression (invoke_mb
.ReturnType
, loc
),
281 generic_method
.SetParameterInfo (null);
283 member_name
= new MemberName (name
, loc
);
285 method
= new Method (
286 (TypeContainer
) ec
.TypeContainer
, generic_method
,
287 new TypeExpression (invoke_mb
.ReturnType
, loc
),
288 method_modifiers
, false, member_name
, Parameters
, null);
289 method
.Block
= Block
;
292 // Swap the TypeBuilder while we define the method, then restore
294 if (current_type
!= null)
295 ec
.TypeContainer
.TypeBuilder
= type_host
;
296 bool res
= method
.Define ();
297 if (current_type
!= null)
298 ec
.TypeContainer
.TypeBuilder
= current_type
;
303 void Error_ParameterMismatch (Type t
)
305 Report
.Error (1661, loc
, "Anonymous method could not be converted to delegate `" +
306 "{0}' since there is a parameter mismatch", TypeManager
.CSharpName (t
));
309 public bool ImplicitStandardConversionExists (Type delegate_type
)
311 if (Parameters
== null)
314 MethodGroupExpr invoke_mg
= Delegate
.GetInvokeMethod (host
.TypeBuilder
, delegate_type
, loc
);
315 invoke_mb
= (MethodInfo
) invoke_mg
.Methods
[0];
316 ParameterData invoke_pd
= TypeManager
.GetParameterData (invoke_mb
);
318 if (Parameters
.Count
!= invoke_pd
.Count
)
321 for (int i
= 0; i
< Parameters
.Count
; ++i
) {
322 if (invoke_pd
.ParameterType (i
) != Parameters
.ParameterType (i
))
329 // Returns true if this anonymous method can be implicitly
330 // converted to the delegate type `delegate_type'
332 public Expression
Compatible (EmitContext ec
, Type delegate_type
)
335 // At this point its the first time we know the return type that is
336 // needed for the anonymous method. We create the method here.
339 MethodGroupExpr invoke_mg
= Delegate
.GetInvokeMethod (ec
.ContainerType
, delegate_type
, loc
);
340 invoke_mb
= (MethodInfo
) invoke_mg
.Methods
[0];
341 ParameterData invoke_pd
= TypeManager
.GetParameterData (invoke_mb
);
343 if (delegate_type
.IsGenericType
) {
344 Type def
= delegate_type
.GetGenericTypeDefinition ();
346 if (def
!= delegate_type
) {
347 Type
[] tparam
= TypeManager
.GetTypeArguments (def
);
349 TypeArguments
= TypeManager
.GetTypeArguments (delegate_type
);
350 TypeParameters
= new string [tparam
.Length
];
351 for (int i
= 0; i
< tparam
.Length
; i
++)
352 TypeParameters
[i
] = tparam
[i
].Name
;
356 if (Parameters
== null) {
358 // We provide a set of inaccessible parameters
360 Parameter
[] fixedpars
= new Parameter
[invoke_pd
.Count
];
362 for (int i
= 0; i
< invoke_pd
.Count
; i
++){
363 fixedpars
[i
] = new Parameter (
364 invoke_pd
.ParameterType (i
),
365 "+" + i
, invoke_pd
.ParameterModifier (i
), null, loc
);
368 Parameters
= new Parameters (fixedpars
);
370 if (Parameters
.Count
!= invoke_pd
.Count
) {
371 Report
.SymbolRelatedToPreviousError (delegate_type
);
372 Report
.Error (1593, loc
, "Delegate `{0}' does not take `{1}' arguments",
373 TypeManager
.CSharpName (delegate_type
), Parameters
.Count
.ToString ());
374 Error_ParameterMismatch (delegate_type
);
378 for (int i
= 0; i
< Parameters
.Count
; ++i
) {
379 Parameter
.Modifier p_mod
= invoke_pd
.ParameterModifier (i
);
380 if (Parameters
.ParameterModifier (i
) != p_mod
&& p_mod
!= Parameter
.Modifier
.PARAMS
) {
381 if (p_mod
== Parameter
.Modifier
.NONE
)
382 Report
.Error (1677, loc
, "Parameter `{0}' should not be declared with the `{1}' keyword",
383 (i
+ 1).ToString (), Parameter
.GetModifierSignature (Parameters
.ParameterModifier (i
)));
385 Report
.Error (1676, loc
, "Parameter `{0}' must be declared with the `{1}' keyword",
386 (i
+1).ToString (), Parameter
.GetModifierSignature (p_mod
));
387 Error_ParameterMismatch (delegate_type
);
391 if (invoke_pd
.ParameterType (i
) != Parameters
.ParameterType (i
)) {
392 Report
.Error (1678, loc
, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
394 TypeManager
.CSharpName (Parameters
.ParameterType (i
)),
395 TypeManager
.CSharpName (invoke_pd
.ParameterType (i
)));
396 Error_ParameterMismatch (delegate_type
);
403 // Second: the return type of the delegate must be compatible with
404 // the anonymous type. Instead of doing a pass to examine the block
405 // we satisfy the rule by setting the return type on the EmitContext
406 // to be the delegate type return type.
409 //MethodBuilder builder = method_data.MethodBuilder;
410 //ILGenerator ig = builder.GetILGenerator ();
412 aec
= new EmitContext (ec
.ResolveContext
,
413 ec
.TypeContainer
, ec
.DeclContainer
, loc
, null,
414 invoke_mb
.ReturnType
,
415 /* REVIEW */ (ec
.InIterator
? Modifiers
.METHOD_YIELDS
: 0) |
416 (ec
.InUnsafe
? Modifiers
.UNSAFE
: 0) |
417 (ec
.IsStatic
? Modifiers
.STATIC
: 0),
418 /* No constructor */ false);
420 aec
.CurrentAnonymousMethod
= this;
421 ContainerAnonymousMethod
= ec
.CurrentAnonymousMethod
;
422 ContainingBlock
= ec
.CurrentBlock
;
424 if (aec
.ResolveTopBlock (ec
, Block
, Parameters
, null, out unreachable
))
425 return new AnonymousDelegate (this, delegate_type
, loc
).Resolve (ec
);
430 public override Expression
DoResolve (EmitContext ec
)
432 if (!ec
.IsAnonymousMethodAllowed
) {
433 Report
.Error (1706, loc
, "Anonymous methods are not allowed in the attribute declaration");
437 if (Parameters
!= null && !Parameters
.Resolve (ec
)) {
441 return base.DoResolve (ec
);
445 public override string ExprClassName
{
447 return "anonymous method";
451 public MethodInfo
GetMethodBuilder ()
453 MethodInfo builder
= method
.MethodBuilder
;
454 if (TypeArguments
!= null)
455 return builder
.MakeGenericMethod (TypeArguments
);
460 public override string GetSignatureForError ()
462 string s
= TypeManager
.CSharpSignature (invoke_mb
);
463 return s
.Substring (0, s
.IndexOf (".Invoke("));
466 public bool EmitMethod (EmitContext ec
)
468 if (!CreateMethodHost (ec
))
471 MethodBuilder builder
= method
.MethodBuilder
;
472 ILGenerator ig
= builder
.GetILGenerator ();
475 Parameters
.ApplyAttributes (builder
);
478 // Adjust based on the computed state of the
479 // method from CreateMethodHost
481 aec
.MethodIsStatic
= (method_modifiers
& Modifiers
.STATIC
) != 0;
483 aec
.EmitMeta (Block
);
484 aec
.EmitResolvedTopBlock (Block
, unreachable
);
488 public override void CreateScopeType (EmitContext ec
, ScopeInfo scope
)
490 TypeBuilder container
= ec
.TypeContainer
.TypeBuilder
;
491 string name
= String
.Format ("<>AnonHelp<{0}>", scope
.id
);
493 scope
.ScopeTypeBuilder
= container
.DefineNestedType (
494 name
, TypeAttributes
.AutoLayout
| TypeAttributes
.Class
|
495 TypeAttributes
.NestedAssembly
, TypeManager
.object_type
, null);
497 Type
[] constructor_types
= Type
.EmptyTypes
;
498 ConstructorBuilder ctor
= scope
.ScopeTypeBuilder
.DefineConstructor (
499 MethodAttributes
.Public
| MethodAttributes
.HideBySig
|
500 MethodAttributes
.SpecialName
| MethodAttributes
.RTSpecialName
,
501 CallingConventions
.HasThis
, constructor_types
);
502 TypeManager
.RegisterMethod (ctor
, Parameters
.EmptyReadOnlyParameters
);
504 ILGenerator cig
= ctor
.GetILGenerator ();
505 cig
.Emit (OpCodes
.Ldarg_0
);
506 cig
.Emit (OpCodes
.Call
, TypeManager
.object_ctor
);
507 cig
.Emit (OpCodes
.Ret
);
509 if (ec
.TypeContainer
.IsGeneric
) {
510 TypeParameter
[] tparam
= ec
.TypeContainer
.TypeParameters
;
511 string[] names
= new string [tparam
.Length
];
512 Type
[] types
= new Type
[tparam
.Length
];
514 for (int i
= 0; i
< names
.Length
; i
++) {
515 names
[i
] = tparam
[i
].Name
;
516 types
[i
] = tparam
[i
].Type
;
519 scope
.ScopeTypeBuilder
.DefineGenericParameters (names
);
520 scope
.ScopeTypeBuilder
.GetGenericTypeDefinition ();
522 scope
.ScopeType
= scope
.ScopeTypeBuilder
.MakeGenericType (types
);
524 scope
.ScopeType
= scope
.ScopeTypeBuilder
;
527 if (ec
.TypeContainer
.IsGeneric
)
528 scope
.ScopeConstructor
= TypeBuilder
.GetConstructor (
529 scope
.ScopeType
, ctor
);
531 scope
.ScopeConstructor
= ctor
;
534 public static void Error_AddressOfCapturedVar (string name
, Location loc
)
536 Report
.Error (1686, loc
,
537 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
543 // This will emit the code for the delegate, as well delegate creation on the host
545 public class AnonymousDelegate
: DelegateCreation
{
548 public AnonymousDelegate (AnonymousMethod am
, Type target_type
, Location l
)
555 public override Expression
DoResolve (EmitContext ec
)
557 eclass
= ExprClass
.Value
;
562 public override void Emit (EmitContext ec
)
564 if (!am
.EmitMethod (ec
))
568 // Now emit the delegate creation.
570 if ((am
.method
.ModFlags
& Modifiers
.STATIC
) == 0)
571 delegate_instance_expression
= new AnonymousInstance (am
);
573 Expression ml
= Expression
.MemberLookup (ec
.ContainerType
, type
, ".ctor", loc
);
574 constructor_method
= ((MethodGroupExpr
) ml
).Methods
[0];
575 delegate_method
= am
.GetMethodBuilder ();
579 class AnonymousInstance
: Expression
{
582 public AnonymousInstance (AnonymousMethod am
)
585 eclass
= ExprClass
.Value
;
588 public override Expression
DoResolve (EmitContext ec
)
593 public override void Emit (EmitContext ec
)
595 am
.aec
.EmitMethodHostInstance (ec
, am
);
600 class CapturedParameter
{
602 public FieldBuilder FieldBuilder
;
605 public CapturedParameter (Type type
, int idx
)
613 // Here we cluster all the variables captured on a given scope, we also
614 // keep some extra information that might be required on each scope.
616 public class ScopeInfo
{
617 public CaptureContext CaptureContext
;
618 public ScopeInfo ParentScope
;
619 public Block ScopeBlock
;
620 public bool NeedThis
= false;
621 public bool HostsParameters
= false;
623 // For tracking the number of scopes created.
628 ArrayList locals
= new ArrayList ();
629 ArrayList children
= new ArrayList ();
632 // The types and fields generated
634 public TypeBuilder ScopeTypeBuilder
;
635 public Type ScopeType
;
636 public ConstructorInfo ScopeConstructor
;
637 public FieldBuilder THIS
;
638 public FieldBuilder ParentLink
;
641 // Points to the object of type `ScopeTypeBuilder' that
642 // holds the data for the scope
644 LocalBuilder scope_instance
;
646 public ScopeInfo (CaptureContext cc
, Block b
)
652 cc
.RegisterCaptureContext ();
655 public void AddLocal (LocalInfo li
)
657 if (locals
.Contains (li
))
663 public bool IsCaptured (LocalInfo li
)
665 return locals
.Contains (li
);
668 internal void AddChild (ScopeInfo si
)
670 if (children
.Contains (si
))
674 // If any of the current children should be a children of `si', move them there
676 ArrayList move_queue
= null;
677 foreach (ScopeInfo child
in children
){
678 if (child
.ScopeBlock
.IsChildOf (si
.ScopeBlock
)){
679 if (move_queue
== null)
680 move_queue
= new ArrayList ();
681 move_queue
.Add (child
);
682 child
.ParentScope
= si
;
689 if (move_queue
!= null){
690 foreach (ScopeInfo child
in move_queue
){
691 children
.Remove (child
);
696 static int indent
= 0;
700 for (int i
= 0; i
< indent
; i
++)
706 //Console.WriteLine (Environment.StackTrace);
708 Console
.WriteLine ("START");
711 Console
.WriteLine ("NeedThis=" + NeedThis
);
712 foreach (LocalInfo li
in locals
){
714 Console
.WriteLine ("var {0}", MakeFieldName (li
.Name
));
717 foreach (ScopeInfo si
in children
)
721 Console
.WriteLine ("END");
724 public string MakeHelperName ()
726 return String
.Format ("<>AnonHelp<{0}>", id
);
729 private string MakeFieldName (string local_name
)
731 return "<" + id
+ ":" + local_name
+ ">";
734 public void EmitScopeType (EmitContext ec
)
738 if (ScopeTypeBuilder
!= null)
742 if (ec
.TypeContainer
.CurrentType
!= null)
743 container
= ec
.TypeContainer
.CurrentType
;
745 container
= ec
.TypeContainer
.TypeBuilder
;
747 CaptureContext
.Host
.CreateScopeType (ec
, this);
750 THIS
= ScopeTypeBuilder
.DefineField ("<>THIS", container
, FieldAttributes
.Assembly
);
752 if (ParentScope
!= null){
753 if (ParentScope
.ScopeTypeBuilder
== null){
754 throw new Exception (String
.Format ("My parent has not been initialized {0} and {1}", ParentScope
, this));
757 if (ParentScope
.ScopeTypeBuilder
!= ScopeTypeBuilder
)
758 ParentLink
= ScopeTypeBuilder
.DefineField (
759 "<>parent", ParentScope
.ScopeType
, FieldAttributes
.Assembly
);
762 if (NeedThis
&& ParentScope
!= null)
763 throw new Exception ("I was not expecting THIS && having a parent");
765 foreach (LocalInfo info
in locals
)
766 info
.FieldBuilder
= ScopeTypeBuilder
.DefineField (
767 MakeFieldName (info
.Name
), info
.VariableType
, FieldAttributes
.Assembly
);
769 if (HostsParameters
){
770 Hashtable captured_parameters
= CaptureContext
.captured_parameters
;
772 foreach (DictionaryEntry de
in captured_parameters
){
773 string name
= (string) de
.Key
;
774 CapturedParameter cp
= (CapturedParameter
) de
.Value
;
777 fb
= ScopeTypeBuilder
.DefineField ("<p:" + name
+ ">", cp
.Type
, FieldAttributes
.Assembly
);
778 cp
.FieldBuilder
= fb
;
782 foreach (ScopeInfo si
in children
){
783 si
.EmitScopeType (ec
);
787 public void CloseTypes ()
789 RootContext
.RegisterCompilerGeneratedType (ScopeTypeBuilder
);
790 foreach (ScopeInfo si
in children
)
795 // Emits the initialization code for the scope
797 public void EmitInitScope (EmitContext ec
)
799 ILGenerator ig
= ec
.ig
;
804 if (ScopeConstructor
== null)
805 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
807 if (!CaptureContext
.Host
.IsIterator
) {
808 scope_instance
= ig
.DeclareLocal (ScopeType
);
809 ig
.Emit (OpCodes
.Newobj
, ScopeConstructor
);
810 ig
.Emit (OpCodes
.Stloc
, scope_instance
);
814 if (CaptureContext
.Host
.IsIterator
) {
815 ig
.Emit (OpCodes
.Ldarg_0
);
816 ig
.Emit (OpCodes
.Ldarg_1
);
818 ig
.Emit (OpCodes
.Ldloc
, scope_instance
);
819 ig
.Emit (OpCodes
.Ldarg_0
);
821 ig
.Emit (OpCodes
.Stfld
, THIS
);
825 // Copy the parameter values, if any
827 int extra
= ec
.IsStatic
? 0 : 1;
828 if (CaptureContext
.Host
.IsIterator
)
830 if (HostsParameters
){
831 Hashtable captured_parameters
= CaptureContext
.captured_parameters
;
833 foreach (DictionaryEntry de
in captured_parameters
){
834 CapturedParameter cp
= (CapturedParameter
) de
.Value
;
836 EmitScopeInstance (ig
);
837 ParameterReference
.EmitLdArg (ig
, cp
.Idx
+ extra
);
838 ig
.Emit (OpCodes
.Stfld
, cp
.FieldBuilder
);
842 if (ParentScope
!= null){
843 if (!ParentScope
.inited
)
844 ParentScope
.EmitInitScope (ec
);
846 if (ParentScope
.ScopeTypeBuilder
!= ScopeTypeBuilder
) {
848 // Only emit initialization in our capturecontext world
850 if (ParentScope
.CaptureContext
== CaptureContext
){
851 EmitScopeInstance (ig
);
852 ParentScope
.EmitScopeInstance (ig
);
853 ig
.Emit (OpCodes
.Stfld
, ParentLink
);
855 EmitScopeInstance (ig
);
856 ig
.Emit (OpCodes
.Ldarg_0
);
857 ig
.Emit (OpCodes
.Stfld
, ParentLink
);
864 public void EmitScopeInstance (ILGenerator ig
)
866 if (CaptureContext
.Host
.IsIterator
)
867 ig
.Emit (OpCodes
.Ldarg_0
);
869 if (scope_instance
== null){
871 // This is needed if someone overwrites the Emit method
872 // of Statement and manually calls Block.Emit without
873 // this snippet first:
875 // ec.EmitScopeInitFromBlock (The_Block);
876 // The_Block.Emit (ec);
880 "The scope_instance has not been emitted, this typically means\n" +
881 "that inside the compiler someone is calling Block.Emit without\n" +
882 "first calling EmitScopeInitFromBlock for the block. See compiler" +
883 "source code for an explanation");
884 throw new Exception ("Internal compiler error");
887 ig
.Emit (OpCodes
.Ldloc
, scope_instance
);
891 public static void CheckCycles (string msg
, ScopeInfo s
)
893 ArrayList l
= new ArrayList ();
896 for (ScopeInfo p
= s
; p
!= null; p
= p
.ParentScope
,n
++){
898 Console
.WriteLine ("Loop detected {0} in {1}", n
, msg
);
899 throw new Exception ();
905 static void DoPath (StringBuilder sb
, ScopeInfo start
)
907 CheckCycles ("print", start
);
909 if (start
.ParentScope
!= null){
910 DoPath (sb
, start
.ParentScope
);
913 sb
.Append ((start
.id
).ToString ());
916 public override string ToString ()
918 StringBuilder sb
= new StringBuilder ();
921 if (CaptureContext
!= null){
922 sb
.Append (CaptureContext
.ToString ());
929 return sb
.ToString ();
934 // CaptureContext objects are created on demand if a method has
935 // anonymous methods and kept on the ToplevelBlock.
937 // If they exist, all ToplevelBlocks in the containing block are
938 // linked together (children pointing to their parents).
940 public class CaptureContext
{
941 public static int count
;
946 // Points to the toplevel block that owns this CaptureContext
948 ToplevelBlock toplevel_owner
;
951 // All the scopes we capture
953 Hashtable scopes
= new Hashtable ();
956 // All the root scopes
958 ArrayList roots
= new ArrayList ();
960 bool have_captured_vars
= false;
961 bool referenced_this
= false;
966 Hashtable captured_fields
= new Hashtable ();
967 Hashtable captured_variables
= new Hashtable ();
968 public Hashtable captured_parameters
= new Hashtable ();
969 public AnonymousContainer Host
;
971 public CaptureContext (ToplevelBlock toplevel_owner
, Location loc
,
972 AnonymousContainer host
)
975 this.toplevel_owner
= toplevel_owner
;
982 void DoPath (StringBuilder sb
, CaptureContext cc
)
984 if (cc
.ParentCaptureContext
!= null){
985 DoPath (sb
, cc
.ParentCaptureContext
);
988 sb
.Append (cc
.cc_id
.ToString ());
991 public void ReParent (ToplevelBlock new_toplevel
, AnonymousContainer new_host
)
993 toplevel_owner
= new_toplevel
;
996 for (CaptureContext cc
= ParentCaptureContext
; cc
!= null;
997 cc
= cc
.ParentCaptureContext
) {
1002 public override string ToString ()
1004 StringBuilder sb
= new StringBuilder ();
1008 return sb
.ToString ();
1011 public ToplevelBlock ParentToplevel
{
1013 return toplevel_owner
.Container
;
1017 public CaptureContext ParentCaptureContext
{
1019 ToplevelBlock parent
= ParentToplevel
;
1021 return (parent
== null) ? null : parent
.CaptureContext
;
1025 ScopeInfo
GetScopeForBlock (Block block
)
1027 ScopeInfo si
= (ScopeInfo
) scopes
[block
.ID
];
1030 si
= new ScopeInfo (this, block
);
1031 scopes
[block
.ID
] = si
;
1035 public void AddLocal (AnonymousContainer am
, LocalInfo li
)
1037 if (li
.Block
.Toplevel
!= toplevel_owner
){
1038 ParentCaptureContext
.AddLocal (am
, li
);
1041 ScopeInfo scope
= GetScopeForBlock (li
.Block
);
1047 Host
.RegisterScope (scope
);
1052 am
.RegisterScope (scope
);
1054 if (captured_variables
[li
] != null)
1057 have_captured_vars
= true;
1058 captured_variables
[li
] = li
;
1059 scope
.AddLocal (li
);
1063 // Retursn the CaptureContext for the block that defines the parameter `name'
1065 static CaptureContext
_ContextForParameter (ToplevelBlock current
, string name
)
1067 ToplevelBlock container
= current
.Container
;
1068 if (container
!= null){
1069 CaptureContext cc
= _ContextForParameter (container
, name
);
1073 if (current
.IsParameterReference (name
))
1074 return current
.ToplevelBlockCaptureContext
;
1078 static CaptureContext
ContextForParameter (ToplevelBlock current
, string name
)
1080 CaptureContext cc
= _ContextForParameter (current
, name
);
1082 throw new Exception (String
.Format ("request for parameteter {0} failed: not found", name
));
1087 // Records the captured parameter at the appropriate CaptureContext
1089 public void AddParameter (EmitContext ec
, AnonymousContainer am
,
1090 string name
, Type t
, int idx
)
1092 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1094 cc
.AddParameterToContext (am
, name
, t
, idx
);
1098 // Records the parameters in the context
1100 public void AddParameterToContext (AnonymousContainer am
, string name
, Type t
, int idx
)
1102 if (captured_parameters
== null)
1103 captured_parameters
= new Hashtable ();
1104 if (captured_parameters
[name
] == null)
1105 captured_parameters
[name
] = new CapturedParameter (t
, idx
);
1107 ScopeInfo scope
= GetScopeForBlock (toplevel_owner
);
1108 scope
.HostsParameters
= true;
1109 am
.RegisterScope (scope
);
1113 // Captured fields are only recorded on the topmost CaptureContext, because that
1114 // one is the one linked to the owner of instance fields
1116 public void AddField (EmitContext ec
, AnonymousContainer am
, FieldExpr fe
)
1118 if (fe
.FieldInfo
.IsStatic
)
1119 throw new Exception ("Attempt to register a static field as a captured field");
1120 CaptureContext parent
= ParentCaptureContext
;
1121 if (parent
!= null) {
1122 parent
.AddField (ec
, am
, fe
);
1126 ScopeInfo scope
= GetScopeForBlock (toplevel_owner
);
1127 am
.RegisterScope (scope
);
1130 public void CaptureThis (AnonymousContainer am
)
1133 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1134 CaptureContext parent
= ParentCaptureContext
;
1135 if (parent
!= null) {
1136 parent
.CaptureThis (am
);
1139 referenced_this
= true;
1141 ScopeInfo scope
= GetScopeForBlock (toplevel_owner
);
1142 am
.RegisterScope (scope
);
1145 public bool HaveCapturedVariables
{
1147 return have_captured_vars
;
1151 public bool HaveCapturedFields
{
1153 CaptureContext parent
= ParentCaptureContext
;
1155 return parent
.HaveCapturedFields
;
1156 return captured_fields
.Count
> 0;
1160 public bool IsCaptured (LocalInfo local
)
1162 foreach (ScopeInfo si
in scopes
.Values
){
1163 if (si
.IsCaptured (local
))
1170 // Returns whether the parameter is captured
1172 public bool IsParameterCaptured (string name
)
1174 if (ParentCaptureContext
!= null && ParentCaptureContext
.IsParameterCaptured (name
))
1177 if (captured_parameters
!= null)
1178 return captured_parameters
[name
] != null;
1182 public void EmitAnonymousHelperClasses (EmitContext ec
)
1184 if (roots
.Count
!= 0){
1185 foreach (ScopeInfo root
in roots
){
1187 // FIXME: We really should do this in a per-ScopeInfo
1188 // basis, instead of having the NeedThis applied to
1189 // all of the roots.
1191 root
.NeedThis
= HaveCapturedFields
|| referenced_this
;
1193 root
.EmitScopeType (ec
);
1198 public void CloseAnonymousHelperClasses ()
1200 if (roots
.Count
!= 0)
1201 foreach (ScopeInfo root
in roots
)
1205 public void EmitInitScope (EmitContext ec
)
1207 EmitAnonymousHelperClasses (ec
);
1208 if (roots
.Count
!= 0)
1209 foreach (ScopeInfo root
in roots
)
1210 root
.EmitInitScope (ec
); }
1213 // This is called externally when we start emitting code for a block
1214 // if the block has a ScopeInfo associated, emit the init code
1216 public void EmitScopeInitFromBlock (EmitContext ec
, Block b
)
1218 ScopeInfo si
= (ScopeInfo
) scopes
[b
.ID
];
1222 si
.EmitInitScope (ec
);
1226 // Emits the opcodes necessary to load the instance of the captured
1229 public void EmitCapturedVariableInstance (EmitContext ec
, LocalInfo li
,
1230 AnonymousContainer am
)
1232 ILGenerator ig
= ec
.ig
;
1235 if (li
.Block
.Toplevel
== toplevel_owner
){
1236 si
= (ScopeInfo
) scopes
[li
.Block
.ID
];
1237 si
.EmitScopeInstance (ig
);
1242 ig
.Emit (OpCodes
.Ldarg_0
);
1244 if (am
.IsIterator
&& (si
.ScopeBlock
.Toplevel
== li
.Block
.Toplevel
)) {
1248 while (si
.ScopeBlock
.ID
!= li
.Block
.ID
){
1249 if (si
.ParentLink
!= null)
1250 ig
.Emit (OpCodes
.Ldfld
, si
.ParentLink
);
1251 si
= si
.ParentScope
;
1254 Console
.WriteLine ("Target: {0} {1}", li
.Block
.ID
, li
.Name
);
1255 while (si
.ScopeBlock
.ID
!= li
.Block
.ID
){
1256 Console
.WriteLine ("Trying: {0}", si
.ScopeBlock
.ID
);
1257 si
= si
.ParentScope
;
1260 throw new Exception (
1261 String
.Format ("Never found block {0} starting at {1} while looking up {2}",
1262 li
.Block
.ID
, am
.Scope
.ScopeBlock
.ID
, li
.Name
));
1269 // Internal routine that loads the instance to reach parameter `name'
1271 void EmitParameterInstance (EmitContext ec
, string name
)
1273 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1275 cc
.EmitParameterInstance (ec
, name
);
1279 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1280 if (par_info
!= null){
1282 // FIXME: implementing this.
1285 ILGenerator ig
= ec
.ig
;
1289 if (ec
.CurrentBlock
.Toplevel
== toplevel_owner
) {
1290 si
= (ScopeInfo
) scopes
[toplevel_owner
.ID
];
1291 si
.EmitScopeInstance (ig
);
1293 si
= ec
.CurrentAnonymousMethod
.Scope
;
1294 ig
.Emit (OpCodes
.Ldarg_0
);
1298 while (si
.ParentLink
!= null) {
1299 ig
.Emit (OpCodes
.Ldfld
, si
.ParentLink
);
1300 si
= si
.ParentScope
;
1306 // Emits the code necessary to load the parameter named `name' within
1307 // an anonymous method.
1309 public void EmitParameter (EmitContext ec
, string name
, bool leave_copy
, bool prepared
, ref LocalTemporary temp
)
1311 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1313 cc
.EmitParameter (ec
, name
, leave_copy
, prepared
, ref temp
);
1317 EmitParameterInstance (ec
, name
);
1318 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1319 if (par_info
!= null){
1321 // FIXME: implementing this.
1324 ec
.ig
.Emit (OpCodes
.Ldfld
, par_info
.FieldBuilder
);
1327 ec
.ig
.Emit (OpCodes
.Dup
);
1328 temp
= new LocalTemporary (par_info
.FieldBuilder
.FieldType
);
1334 // Implements the assignment of `source' to the paramenter named `name' within
1335 // an anonymous method.
1337 public void EmitAssignParameter (EmitContext ec
, string name
, Expression source
, bool leave_copy
, bool prepare_for_load
, ref LocalTemporary temp
)
1339 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1341 cc
.EmitAssignParameter (ec
, name
, source
, leave_copy
, prepare_for_load
, ref temp
);
1344 ILGenerator ig
= ec
.ig
;
1345 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1347 EmitParameterInstance (ec
, name
);
1348 if (prepare_for_load
)
1349 ig
.Emit (OpCodes
.Dup
);
1352 ig
.Emit (OpCodes
.Dup
);
1353 temp
= new LocalTemporary (par_info
.FieldBuilder
.FieldType
);
1356 ig
.Emit (OpCodes
.Stfld
, par_info
.FieldBuilder
);
1362 // Emits the address for the parameter named `name' within
1363 // an anonymous method.
1365 public void EmitAddressOfParameter (EmitContext ec
, string name
)
1367 CaptureContext cc
= ContextForParameter (ec
.CurrentBlock
.Toplevel
, name
);
1369 cc
.EmitAddressOfParameter (ec
, name
);
1372 EmitParameterInstance (ec
, name
);
1373 CapturedParameter par_info
= (CapturedParameter
) captured_parameters
[name
];
1374 ec
.ig
.Emit (OpCodes
.Ldflda
, par_info
.FieldBuilder
);
1378 // The following methods are only invoked on the host for the
1379 // anonymous method.
1381 public void EmitMethodHostInstance (EmitContext target
, AnonymousContainer am
)
1383 ILGenerator ig
= target
.ig
;
1384 ScopeInfo si
= am
.Scope
;
1386 AnonymousContainer container
= am
.ContainerAnonymousMethod
;
1388 if ((si
== null) || ((container
!= null) && (si
== container
.Scope
))) {
1389 ig
.Emit (OpCodes
.Ldarg_0
);
1393 si
.EmitInitScope (target
);
1394 si
.EmitScopeInstance (ig
);
1397 public void RegisterCaptureContext ()
1399 toplevel_owner
.RegisterCaptureContext (this);
1403 // Returs true if `probe' is an ancestor of `scope' in the
1406 bool IsAncestor (ScopeInfo probe
, ScopeInfo scope
)
1408 for (Block b
= scope
.ScopeBlock
.Parent
; b
!= null; b
= b
.Parent
){
1409 if (probe
.ScopeBlock
== b
)
1416 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1417 // of `scope' found in `scope_list'.
1419 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1421 object GetAncestorScopes (ScopeInfo scope
, ScopeInfo
[] scope_list
)
1423 object ancestors
= null;
1425 for (int i
= 0; i
< scope_list
.Length
; i
++){
1426 // Ignore the same scope
1427 if (scope_list
[i
] == scope
)
1430 if (IsAncestor (scope_list
[i
], scope
)){
1431 if (ancestors
== null){
1432 ancestors
= scope_list
[i
];
1436 if (ancestors
is ScopeInfo
){
1437 object old
= ancestors
;
1438 ancestors
= new ArrayList (4);
1439 ((ArrayList
)ancestors
).Add (old
);
1442 ((ArrayList
)ancestors
).Add (scope_list
[i
]);
1449 // Returns the immediate parent of `scope' from all the captured
1450 // scopes found in `scope_list', or null if this is a toplevel scope.
1452 ScopeInfo
GetParentScope (ScopeInfo scope
, ScopeInfo
[] scope_list
)
1454 object ancestors
= GetAncestorScopes (scope
, scope_list
);
1455 if (ancestors
== null)
1458 // Single match, thats the parent.
1459 if (ancestors
is ScopeInfo
)
1460 return (ScopeInfo
) ancestors
;
1462 ArrayList candidates
= (ArrayList
) ancestors
;
1463 ScopeInfo parent
= (ScopeInfo
) candidates
[0];
1464 for (int i
= 1; i
< candidates
.Count
; i
++){
1465 if (IsAncestor (parent
, (ScopeInfo
) candidates
[i
]))
1466 parent
= (ScopeInfo
) candidates
[i
];
1472 // Links all the scopes
1475 public void LinkScopes ()
1481 if (ParentCaptureContext
!= null)
1482 ParentCaptureContext
.LinkScopes ();
1484 int scope_count
= scopes
.Keys
.Count
;
1485 ScopeInfo
[] scope_list
= new ScopeInfo
[scope_count
];
1486 scopes
.Values
.CopyTo (scope_list
, 0);
1488 for (int i
= 0; i
< scope_count
; i
++){
1489 ScopeInfo parent
= GetParentScope (scope_list
[i
], scope_list
);
1491 if (parent
== null){
1492 roots
.Add (scope_list
[i
]);
1496 scope_list
[i
].ParentScope
= parent
;
1497 parent
.AddChild (scope_list
[i
]);
1501 // Link the roots to their parent containers if any.
1503 if (ParentCaptureContext
!= null && roots
.Count
!= 0){
1504 ScopeInfo one_root
= (ScopeInfo
) roots
[0];
1507 foreach (ScopeInfo a_parent_root
in ParentCaptureContext
.roots
){
1508 if (!IsAncestor (a_parent_root
, one_root
))
1513 // Found, link all the roots to this root
1514 foreach (ScopeInfo root
in roots
){
1515 root
.ParentScope
= a_parent_root
;
1516 a_parent_root
.AddChild (root
);
1522 // This is to catch a condition in which it is
1523 // not possible to determine the containing ScopeInfo
1524 // from an encapsulating CaptureContext
1526 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");