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) {
86 Report.Error (1706, l, "Anonymous methods are not allowed in attribute declaration");
89 container.SetHaveAnonymousMethods (l, this);
90 block.SetHaveAnonymousMethods (l, this);
93 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
95 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
99 public override Expression DoResolve (EmitContext ec)
102 // Set class type, set type
105 eclass = ExprClass.Value;
108 // This hack means `The type is not accessible
109 // anywhere', we depend on special conversion
112 type = TypeManager.anonymous_method_type;
117 public void RegisterScope (ScopeInfo scope)
119 if (scopes_used.Contains (scope))
121 scopes_used.Add (scope);
124 // Returns the deepest of two scopes
125 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
137 // If they Scopes are on the same CaptureContext, we do the double
138 // checks just so if there is an invariant change in the future,
139 // we get the exception at the end
141 for (p = a; p != null; p = p.ParentScope)
145 for (p = b; p != null; p = p.ParentScope)
149 CaptureContext ca = a.CaptureContext;
150 CaptureContext cb = b.CaptureContext;
152 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
156 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
159 throw new Exception ("Should never be reached");
163 // Determines the proper host for a method considering the
164 // scopes it references
166 public void ComputeMethodHost ()
168 if (computed_method_scope)
172 int top = scopes_used.Count;
173 computed_method_scope = true;
178 method_scope = (ScopeInfo) scopes_used [0];
182 for (int i = 1; i < top; i++)
183 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
186 public ScopeInfo Scope {
188 if (computed_method_scope)
192 // This means that ComputeMethodHost is not being called, most
193 // likely by someone who overwrote the CreateMethodHost method
195 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
200 protected abstract bool CreateMethodHost (EmitContext ec);
202 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
204 public abstract Iterator Iterator {
208 public abstract bool IsIterator {
213 public class AnonymousMethod : AnonymousContainer
217 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
218 ToplevelBlock block, Location l)
219 : base (parameters, container, block, l)
224 public override Iterator Iterator {
228 public override bool IsIterator {
229 get { return false; }
232 public override void Emit (EmitContext ec)
234 // nothing, as we only exist to not do anything.
238 // Creates the host for the anonymous method
240 protected override bool CreateMethodHost (EmitContext ec)
242 ComputeMethodHost ();
245 // Crude hack follows: we replace the TypeBuilder during the
246 // definition to get the method hosted in the right class
248 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
249 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
251 if (current_type == null)
252 throw new Exception ("The current_type is null");
254 if (type_host == null)
255 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
257 if (current_type != type_host)
258 method_modifiers = Modifiers.INTERNAL;
260 if (current_type == type_host && ec.IsStatic){
261 method_modifiers |= Modifiers.STATIC;
265 string name = "<#AnonymousMethod>" + anonymous_method_count++;
266 MemberName member_name;
268 GenericMethod generic_method = null;
269 if (TypeParameters != null) {
270 TypeArguments args = new TypeArguments (loc);
271 foreach (string t in TypeParameters)
272 args.Add (new SimpleName (t, loc));
274 member_name = new MemberName (name, args, loc);
276 generic_method = new GenericMethod (
277 ec.DeclContainer.NamespaceEntry,
278 (TypeContainer) ec.TypeContainer, member_name,
279 new TypeExpression (invoke_mb.ReturnType, loc),
282 generic_method.SetParameterInfo (null);
284 member_name = new MemberName (name, loc);
286 method = new Method (
287 (TypeContainer) ec.TypeContainer, generic_method,
288 new TypeExpression (invoke_mb.ReturnType, loc),
289 method_modifiers, false, member_name, Parameters, null);
290 method.Block = Block;
293 // Swap the TypeBuilder while we define the method, then restore
295 if (current_type != null)
296 ec.TypeContainer.TypeBuilder = type_host;
297 bool res = method.Define ();
298 if (current_type != null)
299 ec.TypeContainer.TypeBuilder = current_type;
304 void Error_ParameterMismatch (Type t)
306 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
307 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
310 public bool ImplicitStandardConversionExists (Type delegate_type)
312 if (Parameters == null)
315 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
316 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
317 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
319 if (Parameters.Count != invoke_pd.Count)
322 for (int i = 0; i < Parameters.Count; ++i) {
323 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
330 // Returns true if this anonymous method can be implicitly
331 // converted to the delegate type `delegate_type'
333 public Expression Compatible (EmitContext ec, Type delegate_type)
336 // At this point its the first time we know the return type that is
337 // needed for the anonymous method. We create the method here.
340 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
341 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
342 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
344 if (delegate_type.IsGenericType) {
345 Type def = delegate_type.GetGenericTypeDefinition ();
347 if (def != delegate_type) {
348 Type[] tparam = TypeManager.GetTypeArguments (def);
350 TypeArguments = TypeManager.GetTypeArguments (delegate_type);
351 TypeParameters = new string [tparam.Length];
352 for (int i = 0; i < tparam.Length; i++)
353 TypeParameters [i] = tparam [i].Name;
357 if (Parameters == null) {
359 // We provide a set of inaccessible parameters
361 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
363 for (int i = 0; i < invoke_pd.Count; i++){
364 fixedpars [i] = new Parameter (
365 invoke_pd.ParameterType (i),
366 "+" + i, invoke_pd.ParameterModifier (i), null, loc);
369 Parameters = new Parameters (fixedpars);
371 if (Parameters.Count != invoke_pd.Count) {
372 Report.SymbolRelatedToPreviousError (delegate_type);
373 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
374 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
375 Error_ParameterMismatch (delegate_type);
379 for (int i = 0; i < Parameters.Count; ++i) {
380 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
381 if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
382 if (p_mod == Parameter.Modifier.NONE)
383 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
384 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
386 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
387 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
388 Error_ParameterMismatch (delegate_type);
392 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
393 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
395 TypeManager.CSharpName (Parameters.ParameterType (i)),
396 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
397 Error_ParameterMismatch (delegate_type);
404 // Second: the return type of the delegate must be compatible with
405 // the anonymous type. Instead of doing a pass to examine the block
406 // we satisfy the rule by setting the return type on the EmitContext
407 // to be the delegate type return type.
410 //MethodBuilder builder = method_data.MethodBuilder;
411 //ILGenerator ig = builder.GetILGenerator ();
413 aec = new EmitContext (ec.ResolveContext,
414 ec.TypeContainer, ec.DeclContainer, loc, null,
415 invoke_mb.ReturnType,
416 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
417 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
418 (ec.IsStatic ? Modifiers.STATIC : 0),
419 /* No constructor */ false);
421 aec.CurrentAnonymousMethod = this;
422 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
423 ContainingBlock = ec.CurrentBlock;
425 if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable))
426 return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
431 public override Expression DoResolve (EmitContext ec)
433 if (Parameters != null && !Parameters.Resolve (ec)) {
437 return base.DoResolve (ec);
441 public override string ExprClassName {
443 return "anonymous method";
447 public MethodInfo GetMethodBuilder ()
449 MethodInfo builder = method.MethodData.MethodBuilder;
450 if (TypeArguments != null)
451 return builder.MakeGenericMethod (TypeArguments);
456 public override string GetSignatureForError ()
458 string s = TypeManager.CSharpSignature (invoke_mb);
459 return s.Substring (0, s.IndexOf (".Invoke("));
462 public bool EmitMethod (EmitContext ec)
464 if (!CreateMethodHost (ec))
467 MethodBuilder builder = method.MethodData.MethodBuilder;
468 ILGenerator ig = builder.GetILGenerator ();
471 Parameters.ApplyAttributes (builder);
474 // Adjust based on the computed state of the
475 // method from CreateMethodHost
477 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
479 aec.EmitMeta (Block);
480 aec.EmitResolvedTopBlock (Block, unreachable);
484 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
486 TypeBuilder container = ec.TypeContainer.TypeBuilder;
487 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
489 scope.ScopeTypeBuilder = container.DefineNestedType (
490 name, TypeAttributes.AutoLayout | TypeAttributes.Class |
491 TypeAttributes.NestedAssembly, TypeManager.object_type, null);
493 Type [] constructor_types = Type.EmptyTypes;
494 ConstructorBuilder ctor = scope.ScopeTypeBuilder.DefineConstructor (
495 MethodAttributes.Public | MethodAttributes.HideBySig |
496 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
497 CallingConventions.HasThis, constructor_types);
498 TypeManager.RegisterMethod (ctor, Parameters.EmptyReadOnlyParameters);
500 ILGenerator cig = ctor.GetILGenerator ();
501 cig.Emit (OpCodes.Ldarg_0);
502 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
503 cig.Emit (OpCodes.Ret);
505 if (ec.TypeContainer.IsGeneric) {
506 TypeParameter[] tparam = ec.TypeContainer.TypeParameters;
507 string[] names = new string [tparam.Length];
508 Type[] types = new Type [tparam.Length];
510 for (int i = 0; i < names.Length; i++) {
511 names [i] = tparam [i].Name;
512 types [i] = tparam [i].Type;
515 scope.ScopeTypeBuilder.DefineGenericParameters (names);
516 scope.ScopeTypeBuilder.GetGenericTypeDefinition ();
518 scope.ScopeType = scope.ScopeTypeBuilder.MakeGenericType (types);
520 scope.ScopeType = scope.ScopeTypeBuilder;
523 if (ec.TypeContainer.IsGeneric)
524 scope.ScopeConstructor = TypeBuilder.GetConstructor (
525 scope.ScopeType, ctor);
527 scope.ScopeConstructor = ctor;
530 public static void Error_AddressOfCapturedVar (string name, Location loc)
532 Report.Error (1686, loc,
533 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
539 // This will emit the code for the delegate, as well delegate creation on the host
541 public class AnonymousDelegate : DelegateCreation {
544 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
551 public override Expression DoResolve (EmitContext ec)
553 eclass = ExprClass.Value;
558 public override void Emit (EmitContext ec)
560 if (!am.EmitMethod (ec))
564 // Now emit the delegate creation.
566 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
567 delegate_instance_expression = new AnonymousInstance (am);
569 Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", loc);
570 constructor_method = ((MethodGroupExpr) ml).Methods [0];
571 delegate_method = am.GetMethodBuilder ();
575 class AnonymousInstance : Expression {
578 public AnonymousInstance (AnonymousMethod am)
581 eclass = ExprClass.Value;
584 public override Expression DoResolve (EmitContext ec)
589 public override void Emit (EmitContext ec)
591 am.aec.EmitMethodHostInstance (ec, am);
596 class CapturedParameter {
598 public FieldBuilder FieldBuilder;
601 public CapturedParameter (Type type, int idx)
609 // Here we cluster all the variables captured on a given scope, we also
610 // keep some extra information that might be required on each scope.
612 public class ScopeInfo {
613 public CaptureContext CaptureContext;
614 public ScopeInfo ParentScope;
615 public Block ScopeBlock;
616 public bool NeedThis = false;
617 public bool HostsParameters = false;
619 // For tracking the number of scopes created.
624 ArrayList locals = new ArrayList ();
625 ArrayList children = new ArrayList ();
628 // The types and fields generated
630 public TypeBuilder ScopeTypeBuilder;
631 public Type ScopeType;
632 public ConstructorInfo ScopeConstructor;
633 public FieldBuilder THIS;
634 public FieldBuilder ParentLink;
637 // Points to the object of type `ScopeTypeBuilder' that
638 // holds the data for the scope
640 LocalBuilder scope_instance;
642 public ScopeInfo (CaptureContext cc, Block b)
648 cc.RegisterCaptureContext ();
651 public void AddLocal (LocalInfo li)
653 if (locals.Contains (li))
659 public bool IsCaptured (LocalInfo li)
661 return locals.Contains (li);
664 internal void AddChild (ScopeInfo si)
666 if (children.Contains (si))
670 // If any of the current children should be a children of `si', move them there
672 ArrayList move_queue = null;
673 foreach (ScopeInfo child in children){
674 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
675 if (move_queue == null)
676 move_queue = new ArrayList ();
677 move_queue.Add (child);
678 child.ParentScope = si;
685 if (move_queue != null){
686 foreach (ScopeInfo child in move_queue){
687 children.Remove (child);
692 static int indent = 0;
696 for (int i = 0; i < indent; i++)
702 //Console.WriteLine (Environment.StackTrace);
704 Console.WriteLine ("START");
707 Console.WriteLine ("NeedThis=" + NeedThis);
708 foreach (LocalInfo li in locals){
710 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
713 foreach (ScopeInfo si in children)
717 Console.WriteLine ("END");
720 public string MakeHelperName ()
722 return String.Format ("<>AnonHelp<{0}>", id);
725 private string MakeFieldName (string local_name)
727 return "<" + id + ":" + local_name + ">";
730 public void EmitScopeType (EmitContext ec)
734 if (ScopeTypeBuilder != null)
738 if (ec.TypeContainer.CurrentType != null)
739 container = ec.TypeContainer.CurrentType;
741 container = ec.TypeContainer.TypeBuilder;
743 CaptureContext.Host.CreateScopeType (ec, this);
746 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
748 if (ParentScope != null){
749 if (ParentScope.ScopeTypeBuilder == null){
750 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
753 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
754 ParentLink = ScopeTypeBuilder.DefineField (
755 "<>parent", ParentScope.ScopeType, FieldAttributes.Assembly);
758 if (NeedThis && ParentScope != null)
759 throw new Exception ("I was not expecting THIS && having a parent");
761 foreach (LocalInfo info in locals)
762 info.FieldBuilder = ScopeTypeBuilder.DefineField (
763 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
765 if (HostsParameters){
766 Hashtable captured_parameters = CaptureContext.captured_parameters;
768 foreach (DictionaryEntry de in captured_parameters){
769 string name = (string) de.Key;
770 CapturedParameter cp = (CapturedParameter) de.Value;
773 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
774 cp.FieldBuilder = fb;
778 foreach (ScopeInfo si in children){
779 si.EmitScopeType (ec);
783 public void CloseTypes ()
785 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
786 foreach (ScopeInfo si in children)
791 // Emits the initialization code for the scope
793 public void EmitInitScope (EmitContext ec)
795 ILGenerator ig = ec.ig;
800 if (ScopeConstructor == null)
801 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
803 if (!CaptureContext.Host.IsIterator) {
804 scope_instance = ig.DeclareLocal (ScopeType);
805 ig.Emit (OpCodes.Newobj, ScopeConstructor);
806 ig.Emit (OpCodes.Stloc, scope_instance);
810 if (CaptureContext.Host.IsIterator) {
811 ig.Emit (OpCodes.Ldarg_0);
812 ig.Emit (OpCodes.Ldarg_1);
814 ig.Emit (OpCodes.Ldloc, scope_instance);
815 ig.Emit (OpCodes.Ldarg_0);
817 ig.Emit (OpCodes.Stfld, THIS);
821 // Copy the parameter values, if any
823 int extra = ec.IsStatic ? 0 : 1;
824 if (CaptureContext.Host.IsIterator)
826 if (HostsParameters){
827 Hashtable captured_parameters = CaptureContext.captured_parameters;
829 foreach (DictionaryEntry de in captured_parameters){
830 CapturedParameter cp = (CapturedParameter) de.Value;
832 EmitScopeInstance (ig);
833 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
834 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
838 if (ParentScope != null){
839 if (!ParentScope.inited)
840 ParentScope.EmitInitScope (ec);
842 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
844 // Only emit initialization in our capturecontext world
846 if (ParentScope.CaptureContext == CaptureContext){
847 EmitScopeInstance (ig);
848 ParentScope.EmitScopeInstance (ig);
849 ig.Emit (OpCodes.Stfld, ParentLink);
851 EmitScopeInstance (ig);
852 ig.Emit (OpCodes.Ldarg_0);
853 ig.Emit (OpCodes.Stfld, ParentLink);
860 public void EmitScopeInstance (ILGenerator ig)
862 if (CaptureContext.Host.IsIterator)
863 ig.Emit (OpCodes.Ldarg_0);
865 if (scope_instance == null){
867 // This is needed if someone overwrites the Emit method
868 // of Statement and manually calls Block.Emit without
869 // this snippet first:
871 // ec.EmitScopeInitFromBlock (The_Block);
872 // The_Block.Emit (ec);
876 "The scope_instance has not been emitted, this typically means\n" +
877 "that inside the compiler someone is calling Block.Emit without\n" +
878 "first calling EmitScopeInitFromBlock for the block. See compiler" +
879 "source code for an explanation");
880 throw new Exception ("Internal compiler error");
883 ig.Emit (OpCodes.Ldloc, scope_instance);
887 public static void CheckCycles (string msg, ScopeInfo s)
889 ArrayList l = new ArrayList ();
892 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
894 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
895 throw new Exception ();
901 static void DoPath (StringBuilder sb, ScopeInfo start)
903 CheckCycles ("print", start);
905 if (start.ParentScope != null){
906 DoPath (sb, start.ParentScope);
909 sb.Append ((start.id).ToString ());
912 public override string ToString ()
914 StringBuilder sb = new StringBuilder ();
917 if (CaptureContext != null){
918 sb.Append (CaptureContext.ToString ());
925 return sb.ToString ();
930 // CaptureContext objects are created on demand if a method has
931 // anonymous methods and kept on the ToplevelBlock.
933 // If they exist, all ToplevelBlocks in the containing block are
934 // linked together (children pointing to their parents).
936 public class CaptureContext {
937 public static int count;
942 // Points to the toplevel block that owns this CaptureContext
944 ToplevelBlock toplevel_owner;
947 // All the scopes we capture
949 Hashtable scopes = new Hashtable ();
952 // All the root scopes
954 ArrayList roots = new ArrayList ();
956 bool have_captured_vars = false;
957 bool referenced_this = false;
962 Hashtable captured_fields = new Hashtable ();
963 Hashtable captured_variables = new Hashtable ();
964 public Hashtable captured_parameters = new Hashtable ();
965 public AnonymousContainer Host;
967 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
968 AnonymousContainer host)
971 this.toplevel_owner = toplevel_owner;
978 void DoPath (StringBuilder sb, CaptureContext cc)
980 if (cc.ParentCaptureContext != null){
981 DoPath (sb, cc.ParentCaptureContext);
984 sb.Append (cc.cc_id.ToString ());
987 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
989 toplevel_owner = new_toplevel;
992 for (CaptureContext cc = ParentCaptureContext; cc != null;
993 cc = cc.ParentCaptureContext) {
998 public override string ToString ()
1000 StringBuilder sb = new StringBuilder ();
1004 return sb.ToString ();
1007 public ToplevelBlock ParentToplevel {
1009 return toplevel_owner.Container;
1013 public CaptureContext ParentCaptureContext {
1015 ToplevelBlock parent = ParentToplevel;
1017 return (parent == null) ? null : parent.CaptureContext;
1021 ScopeInfo GetScopeForBlock (Block block)
1023 ScopeInfo si = (ScopeInfo) scopes [block.ID];
1026 si = new ScopeInfo (this, block);
1027 scopes [block.ID] = si;
1031 public void AddLocal (AnonymousContainer am, LocalInfo li)
1033 if (li.Block.Toplevel != toplevel_owner){
1034 ParentCaptureContext.AddLocal (am, li);
1037 ScopeInfo scope = GetScopeForBlock (li.Block);
1043 Host.RegisterScope (scope);
1048 am.RegisterScope (scope);
1050 if (captured_variables [li] != null)
1053 have_captured_vars = true;
1054 captured_variables [li] = li;
1055 scope.AddLocal (li);
1059 // Retursn the CaptureContext for the block that defines the parameter `name'
1061 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
1063 ToplevelBlock container = current.Container;
1064 if (container != null){
1065 CaptureContext cc = _ContextForParameter (container, name);
1069 if (current.IsParameterReference (name))
1070 return current.ToplevelBlockCaptureContext;
1074 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1076 CaptureContext cc = _ContextForParameter (current, name);
1078 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1083 // Records the captured parameter at the appropriate CaptureContext
1085 public void AddParameter (EmitContext ec, AnonymousContainer am,
1086 string name, Type t, int idx)
1088 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1090 cc.AddParameterToContext (am, name, t, idx);
1094 // Records the parameters in the context
1096 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1098 if (captured_parameters == null)
1099 captured_parameters = new Hashtable ();
1100 if (captured_parameters [name] == null)
1101 captured_parameters [name] = new CapturedParameter (t, idx);
1103 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1104 scope.HostsParameters = true;
1105 am.RegisterScope (scope);
1109 // Captured fields are only recorded on the topmost CaptureContext, because that
1110 // one is the one linked to the owner of instance fields
1112 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1114 if (fe.FieldInfo.IsStatic)
1115 throw new Exception ("Attempt to register a static field as a captured field");
1116 CaptureContext parent = ParentCaptureContext;
1117 if (parent != null) {
1118 parent.AddField (ec, am, fe);
1122 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1123 am.RegisterScope (scope);
1126 public void CaptureThis (AnonymousContainer am)
1129 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1130 CaptureContext parent = ParentCaptureContext;
1131 if (parent != null) {
1132 parent.CaptureThis (am);
1135 referenced_this = true;
1137 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1138 am.RegisterScope (scope);
1141 public bool HaveCapturedVariables {
1143 return have_captured_vars;
1147 public bool HaveCapturedFields {
1149 CaptureContext parent = ParentCaptureContext;
1151 return parent.HaveCapturedFields;
1152 return captured_fields.Count > 0;
1156 public bool IsCaptured (LocalInfo local)
1158 foreach (ScopeInfo si in scopes.Values){
1159 if (si.IsCaptured (local))
1166 // Returns whether the parameter is captured
1168 public bool IsParameterCaptured (string name)
1170 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1173 if (captured_parameters != null)
1174 return captured_parameters [name] != null;
1178 public void EmitAnonymousHelperClasses (EmitContext ec)
1180 if (roots.Count != 0){
1181 foreach (ScopeInfo root in roots){
1183 // FIXME: We really should do this in a per-ScopeInfo
1184 // basis, instead of having the NeedThis applied to
1185 // all of the roots.
1187 root.NeedThis = HaveCapturedFields || referenced_this;
1189 root.EmitScopeType (ec);
1194 public void CloseAnonymousHelperClasses ()
1196 if (roots.Count != 0)
1197 foreach (ScopeInfo root in roots)
1201 public void EmitInitScope (EmitContext ec)
1203 EmitAnonymousHelperClasses (ec);
1204 if (roots.Count != 0)
1205 foreach (ScopeInfo root in roots)
1206 root.EmitInitScope (ec); }
1209 // This is called externally when we start emitting code for a block
1210 // if the block has a ScopeInfo associated, emit the init code
1212 public void EmitScopeInitFromBlock (EmitContext ec, Block b)
1214 ScopeInfo si = (ScopeInfo) scopes [b.ID];
1218 si.EmitInitScope (ec);
1222 // Emits the opcodes necessary to load the instance of the captured
1225 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1226 AnonymousContainer am)
1228 ILGenerator ig = ec.ig;
1231 if (li.Block.Toplevel == toplevel_owner){
1232 si = (ScopeInfo) scopes [li.Block.ID];
1233 si.EmitScopeInstance (ig);
1238 ig.Emit (OpCodes.Ldarg_0);
1240 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1244 while (si.ScopeBlock.ID != li.Block.ID){
1245 if (si.ParentLink != null)
1246 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1247 si = si.ParentScope;
1250 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1251 while (si.ScopeBlock.ID != li.Block.ID){
1252 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1253 si = si.ParentScope;
1256 throw new Exception (
1257 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1258 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1265 // Internal routine that loads the instance to reach parameter `name'
1267 void EmitParameterInstance (EmitContext ec, string name)
1269 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1271 cc.EmitParameterInstance (ec, name);
1275 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1276 if (par_info != null){
1278 // FIXME: implementing this.
1281 ILGenerator ig = ec.ig;
1285 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1286 si = (ScopeInfo) scopes [toplevel_owner.ID];
1287 si.EmitScopeInstance (ig);
1289 si = ec.CurrentAnonymousMethod.Scope;
1290 ig.Emit (OpCodes.Ldarg_0);
1294 while (si.ParentLink != null) {
1295 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1296 si = si.ParentScope;
1302 // Emits the code necessary to load the parameter named `name' within
1303 // an anonymous method.
1305 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1307 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1309 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1313 EmitParameterInstance (ec, name);
1314 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1315 if (par_info != null){
1317 // FIXME: implementing this.
1320 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1323 ec.ig.Emit (OpCodes.Dup);
1324 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1330 // Implements the assignment of `source' to the paramenter named `name' within
1331 // an anonymous method.
1333 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1335 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1337 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1340 ILGenerator ig = ec.ig;
1341 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1343 EmitParameterInstance (ec, name);
1344 if (prepare_for_load)
1345 ig.Emit (OpCodes.Dup);
1348 ig.Emit (OpCodes.Dup);
1349 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1352 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1358 // Emits the address for the parameter named `name' within
1359 // an anonymous method.
1361 public void EmitAddressOfParameter (EmitContext ec, string name)
1363 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1365 cc.EmitAddressOfParameter (ec, name);
1368 EmitParameterInstance (ec, name);
1369 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1370 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1374 // The following methods are only invoked on the host for the
1375 // anonymous method.
1377 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1379 ILGenerator ig = target.ig;
1380 ScopeInfo si = am.Scope;
1382 AnonymousContainer container = am.ContainerAnonymousMethod;
1384 if ((si == null) || ((container != null) && (si == container.Scope))) {
1385 ig.Emit (OpCodes.Ldarg_0);
1389 si.EmitInitScope (target);
1390 si.EmitScopeInstance (ig);
1393 public void RegisterCaptureContext ()
1395 toplevel_owner.RegisterCaptureContext (this);
1399 // Returs true if `probe' is an ancestor of `scope' in the
1402 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1404 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1405 if (probe.ScopeBlock == b)
1412 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1413 // of `scope' found in `scope_list'.
1415 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1417 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1419 object ancestors = null;
1421 for (int i = 0; i < scope_list.Length; i++){
1422 // Ignore the same scope
1423 if (scope_list [i] == scope)
1426 if (IsAncestor (scope_list [i], scope)){
1427 if (ancestors == null){
1428 ancestors = scope_list [i];
1432 if (ancestors is ScopeInfo){
1433 object old = ancestors;
1434 ancestors = new ArrayList (4);
1435 ((ArrayList)ancestors).Add (old);
1438 ((ArrayList)ancestors).Add (scope_list [i]);
1445 // Returns the immediate parent of `scope' from all the captured
1446 // scopes found in `scope_list', or null if this is a toplevel scope.
1448 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1450 object ancestors = GetAncestorScopes (scope, scope_list);
1451 if (ancestors == null)
1454 // Single match, thats the parent.
1455 if (ancestors is ScopeInfo)
1456 return (ScopeInfo) ancestors;
1458 ArrayList candidates = (ArrayList) ancestors;
1459 ScopeInfo parent = (ScopeInfo) candidates [0];
1460 for (int i = 1; i < candidates.Count; i++){
1461 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1462 parent = (ScopeInfo) candidates [i];
1468 // Links all the scopes
1471 public void LinkScopes ()
1477 if (ParentCaptureContext != null)
1478 ParentCaptureContext.LinkScopes ();
1480 int scope_count = scopes.Keys.Count;
1481 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1482 scopes.Values.CopyTo (scope_list, 0);
1484 for (int i = 0; i < scope_count; i++){
1485 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1487 if (parent == null){
1488 roots.Add (scope_list [i]);
1492 scope_list [i].ParentScope = parent;
1493 parent.AddChild (scope_list [i]);
1497 // Link the roots to their parent containers if any.
1499 if (ParentCaptureContext != null && roots.Count != 0){
1500 ScopeInfo one_root = (ScopeInfo) roots [0];
1503 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1504 if (!IsAncestor (a_parent_root, one_root))
1509 // Found, link all the roots to this root
1510 foreach (ScopeInfo root in roots){
1511 root.ParentScope = a_parent_root;
1512 a_parent_root.AddChild (root);
1518 // This is to catch a condition in which it is
1519 // not possible to determine the containing ScopeInfo
1520 // from an encapsulating CaptureContext
1522 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");