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 protected bool unreachable;
53 ScopeInfo method_scope;
54 bool computed_method_scope = false;
57 // The modifiers applied to the method, we aggregate them
59 protected int method_modifiers = Modifiers.PRIVATE;
62 // Track the scopes that this method has used. At the
63 // end this is used to determine the ScopeInfo that will
66 ArrayList scopes_used = new ArrayList ();
69 // Points to our container anonymous method if its present
71 public AnonymousContainer ContainerAnonymousMethod;
73 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
74 ToplevelBlock block, Location l)
76 Parameters = parameters;
81 // The order is important: this setups the CaptureContext tree hierarchy.
83 if (container == null) {
86 container.SetHaveAnonymousMethods (l, this);
87 block.SetHaveAnonymousMethods (l, this);
90 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
92 this (parameters, container, new ToplevelBlock (container, parameters, l), l)
96 public override Expression DoResolve (EmitContext ec)
99 // Set class type, set type
102 eclass = ExprClass.Value;
105 // This hack means `The type is not accessible
106 // anywhere', we depend on special conversion
109 type = TypeManager.anonymous_method_type;
114 public void RegisterScope (ScopeInfo scope)
116 if (scopes_used.Contains (scope))
118 scopes_used.Add (scope);
121 // Returns the deepest of two scopes
122 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
134 // If they Scopes are on the same CaptureContext, we do the double
135 // checks just so if there is an invariant change in the future,
136 // we get the exception at the end
138 for (p = a; p != null; p = p.ParentScope)
142 for (p = b; p != null; p = p.ParentScope)
146 CaptureContext ca = a.CaptureContext;
147 CaptureContext cb = b.CaptureContext;
149 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
153 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
156 throw new Exception ("Should never be reached");
160 // Determines the proper host for a method considering the
161 // scopes it references
163 public void ComputeMethodHost ()
165 if (computed_method_scope)
169 int top = scopes_used.Count;
170 computed_method_scope = true;
175 method_scope = (ScopeInfo) scopes_used [0];
179 for (int i = 1; i < top; i++)
180 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
183 public ScopeInfo Scope {
185 if (computed_method_scope)
189 // This means that ComputeMethodHost is not being called, most
190 // likely by someone who overwrote the CreateMethodHost method
192 throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
197 protected abstract bool CreateMethodHost (EmitContext ec);
199 public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
201 public abstract bool IsIterator {
206 public class AnonymousMethod : AnonymousContainer
211 // The value return by the Compatible call, this ensure that
212 // the code works even if invoked more than once (Resolve called
213 // more than once, due to the way Convert.ImplicitConversion works
215 Expression anonymous_delegate;
217 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
218 ToplevelBlock block, Location l)
219 : base (parameters, container, block, l)
224 public override bool IsIterator {
225 get { return false; }
228 public override void Emit (EmitContext ec)
230 // nothing, as we only exist to not do anything.
234 // Creates the host for the anonymous method
236 protected override bool CreateMethodHost (EmitContext ec)
238 ComputeMethodHost ();
241 // Crude hack follows: we replace the TypeBuilder during the
242 // definition to get the method hosted in the right class
244 TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
245 TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
247 if (current_type == null)
248 throw new Exception ("The current_type is null");
250 if (type_host == null)
251 throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
253 if (current_type != type_host)
254 method_modifiers = Modifiers.INTERNAL;
256 if (current_type == type_host && ec.IsStatic){
257 method_modifiers |= Modifiers.STATIC;
261 method = new Method (
262 (TypeContainer) ec.TypeContainer, null,
263 new TypeExpression (invoke_mb.ReturnType, loc),
264 method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
266 method.Block = Block;
269 // Swap the TypeBuilder while we define the method, then restore
271 if (current_type != null)
272 ec.TypeContainer.TypeBuilder = type_host;
273 bool res = method.Define ();
274 if (current_type != null)
275 ec.TypeContainer.TypeBuilder = current_type;
279 void Error_ParameterMismatch (Type t)
281 Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
282 "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
285 public bool ImplicitStandardConversionExists (Type delegate_type)
287 if (Parameters == null)
290 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
291 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
292 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
294 if (Parameters.Count != invoke_pd.Count)
297 for (int i = 0; i < Parameters.Count; ++i) {
298 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
305 // Returns true if this anonymous method can be implicitly
306 // converted to the delegate type `delegate_type'
308 public Expression Compatible (EmitContext ec, Type delegate_type)
310 if (anonymous_delegate != null)
311 return anonymous_delegate;
314 // At this point its the first time we know the return type that is
315 // needed for the anonymous method. We create the method here.
318 MethodGroupExpr invoke_mg = Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
319 invoke_mb = (MethodInfo) invoke_mg.Methods [0];
320 ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
322 if (Parameters == null) {
324 // We provide a set of inaccessible parameters
326 Parameter [] fixedpars = new Parameter [invoke_pd.Count];
328 for (int i = 0; i < invoke_pd.Count; i++) {
329 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
330 if ((p_mod & Parameter.Modifier.OUT) != 0) {
331 Report.SymbolRelatedToPreviousError (delegate_type);
332 Report.Error (1688, loc, "Cannot convert anonymous method block without a parameter list " +
333 "to delegate type `{0}' because it has one or more `out' parameters",
334 TypeManager.CSharpName (delegate_type));
338 fixedpars [i] = new Parameter (invoke_pd.ParameterType (i),
339 "+" + i, p_mod, null, loc);
342 Parameters = new Parameters (fixedpars);
344 if (Parameters.Count != invoke_pd.Count) {
345 Report.SymbolRelatedToPreviousError (delegate_type);
346 Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
347 TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
348 Error_ParameterMismatch (delegate_type);
352 for (int i = 0; i < Parameters.Count; ++i) {
353 Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
354 if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
355 if (p_mod == Parameter.Modifier.NONE)
356 Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
357 (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
359 Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
360 (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
361 Error_ParameterMismatch (delegate_type);
365 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
366 Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
368 TypeManager.CSharpName (Parameters.ParameterType (i)),
369 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
370 Error_ParameterMismatch (delegate_type);
377 // Second: the return type of the delegate must be compatible with
378 // the anonymous type. Instead of doing a pass to examine the block
379 // we satisfy the rule by setting the return type on the EmitContext
380 // to be the delegate type return type.
383 //MethodBuilder builder = method_data.MethodBuilder;
384 //ILGenerator ig = builder.GetILGenerator ();
386 aec = new EmitContext (ec.ResolveContext,
387 ec.TypeContainer, ec.DeclContainer, loc, null,
388 invoke_mb.ReturnType,
389 /* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
390 (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
391 (ec.IsStatic ? Modifiers.STATIC : 0),
392 /* No constructor */ false);
394 aec.CurrentAnonymousMethod = this;
395 ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
396 ContainingBlock = ec.CurrentBlock;
398 if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable)){
399 anonymous_delegate = new AnonymousDelegate (
400 this, delegate_type, loc).Resolve (ec);
401 return anonymous_delegate;
406 public override Expression DoResolve (EmitContext ec)
408 if (!ec.IsAnonymousMethodAllowed) {
409 Report.Error (1706, loc, "Anonymous methods are not allowed in the attribute declaration");
413 if (Parameters != null && !Parameters.Resolve (ec)) {
417 return base.DoResolve (ec);
421 public override string ExprClassName {
423 return "anonymous method";
427 public MethodBuilder GetMethodBuilder ()
429 return method.MethodBuilder;
432 public override string GetSignatureForError ()
434 string s = TypeManager.CSharpSignature (invoke_mb);
435 return s.Substring (0, s.IndexOf (".Invoke("));
438 public bool EmitMethod (EmitContext ec)
440 if (!CreateMethodHost (ec))
443 MethodBuilder builder = GetMethodBuilder ();
444 ILGenerator ig = builder.GetILGenerator ();
447 Parameters.ApplyAttributes (builder);
450 // Adjust based on the computed state of the
451 // method from CreateMethodHost
453 aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
455 aec.EmitMeta (Block);
456 aec.EmitResolvedTopBlock (Block, unreachable);
460 public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
462 TypeBuilder container = ec.TypeContainer.TypeBuilder;
463 string name = String.Format ("<>AnonHelp<{0}>", scope.id);
465 scope.ScopeTypeBuilder = container.DefineNestedType (name,
466 TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit | TypeAttributes.NestedPrivate,
467 TypeManager.object_type);
469 Type [] constructor_types = Type.EmptyTypes;
470 scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
471 MethodAttributes.Public | MethodAttributes.HideBySig |
472 MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
473 CallingConventions.HasThis, constructor_types);
475 TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
477 ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
478 cig.Emit (OpCodes.Ldarg_0);
479 cig.Emit (OpCodes.Call, TypeManager.object_ctor);
480 cig.Emit (OpCodes.Ret);
483 public static void Error_AddressOfCapturedVar (string name, Location loc)
485 Report.Error (1686, loc,
486 "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
492 // This will emit the code for the delegate, as well delegate creation on the host
494 public class AnonymousDelegate : DelegateCreation {
497 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
504 public override Expression DoResolve (EmitContext ec)
506 eclass = ExprClass.Value;
511 public override void Emit (EmitContext ec)
513 if (!am.EmitMethod (ec))
517 // Now emit the delegate creation.
519 if ((am.method.ModFlags & Modifiers.STATIC) == 0)
520 delegate_instance_expression = new AnonymousInstance (am);
522 Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", MemberTypes.Constructor,
523 BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, loc);
524 constructor_method = ((MethodGroupExpr) ml).Methods [0];
525 delegate_method = am.GetMethodBuilder ();
529 class AnonymousInstance : Expression {
532 public AnonymousInstance (AnonymousMethod am)
535 eclass = ExprClass.Value;
538 public override Expression DoResolve (EmitContext ec)
543 public override void Emit (EmitContext ec)
545 am.aec.EmitMethodHostInstance (ec, am);
550 class CapturedParameter {
552 public FieldBuilder FieldBuilder;
555 public CapturedParameter (Type type, int idx)
563 // Here we cluster all the variables captured on a given scope, we also
564 // keep some extra information that might be required on each scope.
566 public class ScopeInfo {
567 public CaptureContext CaptureContext;
568 public ScopeInfo ParentScope;
569 public Block ScopeBlock;
570 public bool NeedThis = false;
571 public bool HostsParameters = false;
573 // For tracking the number of scopes created.
578 ArrayList locals = new ArrayList ();
579 ArrayList children = new ArrayList ();
582 // The types and fields generated
584 public TypeBuilder ScopeTypeBuilder;
585 public ConstructorBuilder ScopeConstructor;
586 public FieldBuilder THIS;
587 public FieldBuilder ParentLink;
590 // Points to the object of type `ScopeTypeBuilder' that
591 // holds the data for the scope
593 LocalBuilder scope_instance;
595 public ScopeInfo (CaptureContext cc, Block b)
601 cc.RegisterCaptureContext ();
604 public void AddLocal (LocalInfo li)
606 if (locals.Contains (li))
612 public bool IsCaptured (LocalInfo li)
614 return locals.Contains (li);
617 internal void AddChild (ScopeInfo si)
619 if (children.Contains (si))
623 // If any of the current children should be a children of `si', move them there
625 ArrayList move_queue = null;
626 foreach (ScopeInfo child in children){
627 if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
628 if (move_queue == null)
629 move_queue = new ArrayList ();
630 move_queue.Add (child);
631 child.ParentScope = si;
638 if (move_queue != null){
639 foreach (ScopeInfo child in move_queue){
640 children.Remove (child);
645 static int indent = 0;
649 for (int i = 0; i < indent; i++)
655 //Console.WriteLine (Environment.StackTrace);
657 Console.WriteLine ("START");
660 Console.WriteLine ("NeedThis=" + NeedThis);
661 foreach (LocalInfo li in locals){
663 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
666 foreach (ScopeInfo si in children)
670 Console.WriteLine ("END");
673 private string MakeFieldName (string local_name)
675 return "<" + id + ":" + local_name + ">";
678 public void EmitScopeType (EmitContext ec)
682 if (ScopeTypeBuilder != null)
685 TypeBuilder container = ec.TypeContainer.TypeBuilder;
687 CaptureContext.Host.CreateScopeType (ec, this);
690 THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
692 if (ParentScope != null){
693 if (ParentScope.ScopeTypeBuilder == null){
694 throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
697 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
698 ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
699 FieldAttributes.Assembly);
702 if (NeedThis && ParentScope != null)
703 throw new Exception ("I was not expecting THIS && having a parent");
705 foreach (LocalInfo info in locals)
706 info.FieldBuilder = ScopeTypeBuilder.DefineField (
707 MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
709 if (HostsParameters){
710 Hashtable captured_parameters = CaptureContext.captured_parameters;
712 foreach (DictionaryEntry de in captured_parameters){
713 string name = (string) de.Key;
714 CapturedParameter cp = (CapturedParameter) de.Value;
717 fb = ScopeTypeBuilder.DefineField ("<p:" + name + ">", cp.Type, FieldAttributes.Assembly);
718 cp.FieldBuilder = fb;
722 foreach (ScopeInfo si in children){
723 si.EmitScopeType (ec);
727 public void CloseTypes ()
729 RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
730 foreach (ScopeInfo si in children)
735 // Emits the initialization code for the scope
737 public void EmitInitScope (EmitContext ec)
739 ILGenerator ig = ec.ig;
744 if (ScopeConstructor == null)
745 throw new Exception ("ScopeConstructor is null for" + this.ToString ());
747 if (!CaptureContext.Host.IsIterator) {
748 scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
749 ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
750 ig.Emit (OpCodes.Stloc, scope_instance);
754 if (CaptureContext.Host.IsIterator) {
755 ig.Emit (OpCodes.Ldarg_0);
756 ig.Emit (OpCodes.Ldarg_1);
758 ig.Emit (OpCodes.Ldloc, scope_instance);
759 ig.Emit (OpCodes.Ldarg_0);
761 ig.Emit (OpCodes.Stfld, THIS);
765 // Copy the parameter values, if any
767 int extra = ec.IsStatic ? 0 : 1;
768 if (CaptureContext.Host.IsIterator)
770 if (HostsParameters){
771 Hashtable captured_parameters = CaptureContext.captured_parameters;
773 foreach (DictionaryEntry de in captured_parameters){
774 CapturedParameter cp = (CapturedParameter) de.Value;
776 EmitScopeInstance (ig);
777 ParameterReference.EmitLdArg (ig, cp.Idx + extra);
778 ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
782 if (ParentScope != null){
783 if (!ParentScope.inited)
784 ParentScope.EmitInitScope (ec);
786 if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
788 // Only emit initialization in our capturecontext world
790 if (ParentScope.CaptureContext == CaptureContext){
791 EmitScopeInstance (ig);
792 ParentScope.EmitScopeInstance (ig);
793 ig.Emit (OpCodes.Stfld, ParentLink);
795 EmitScopeInstance (ig);
796 ig.Emit (OpCodes.Ldarg_0);
797 ig.Emit (OpCodes.Stfld, ParentLink);
804 public void EmitScopeInstance (ILGenerator ig)
806 if (CaptureContext.Host.IsIterator)
807 ig.Emit (OpCodes.Ldarg_0);
809 if (scope_instance == null){
811 // This is needed if someone overwrites the Emit method
812 // of Statement and manually calls Block.Emit without
813 // this snippet first:
815 // ec.EmitScopeInitFromBlock (The_Block);
816 // The_Block.Emit (ec);
820 "The scope_instance has not been emitted, this typically means\n" +
821 "that inside the compiler someone is calling Block.Emit without\n" +
822 "first calling EmitScopeInitFromBlock for the block. See compiler" +
823 "source code for an explanation");
824 throw new Exception ("Internal compiler error");
827 ig.Emit (OpCodes.Ldloc, scope_instance);
831 public static void CheckCycles (string msg, ScopeInfo s)
833 ArrayList l = new ArrayList ();
836 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
838 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
839 throw new Exception ();
845 static void DoPath (StringBuilder sb, ScopeInfo start)
847 CheckCycles ("print", start);
849 if (start.ParentScope != null){
850 DoPath (sb, start.ParentScope);
853 sb.Append ((start.id).ToString ());
856 public override string ToString ()
858 StringBuilder sb = new StringBuilder ();
861 if (CaptureContext != null){
862 sb.Append (CaptureContext.ToString ());
869 return sb.ToString ();
874 // CaptureContext objects are created on demand if a method has
875 // anonymous methods and kept on the ToplevelBlock.
877 // If they exist, all ToplevelBlocks in the containing block are
878 // linked together (children pointing to their parents).
880 public class CaptureContext {
881 public static int count;
886 // Points to the toplevel block that owns this CaptureContext
888 ToplevelBlock toplevel_owner;
891 // All the scopes we capture
893 Hashtable scopes = new Hashtable ();
896 // All the root scopes
898 ArrayList roots = new ArrayList ();
900 bool have_captured_vars = false;
901 bool referenced_this = false;
906 Hashtable captured_fields = new Hashtable ();
907 Hashtable captured_variables = new Hashtable ();
908 public Hashtable captured_parameters = new Hashtable ();
909 public AnonymousContainer Host;
911 public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
912 AnonymousContainer host)
915 this.toplevel_owner = toplevel_owner;
922 void DoPath (StringBuilder sb, CaptureContext cc)
924 if (cc.ParentCaptureContext != null){
925 DoPath (sb, cc.ParentCaptureContext);
928 sb.Append (cc.cc_id.ToString ());
931 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
933 toplevel_owner = new_toplevel;
936 for (CaptureContext cc = ParentCaptureContext; cc != null;
937 cc = cc.ParentCaptureContext) {
942 public override string ToString ()
944 StringBuilder sb = new StringBuilder ();
948 return sb.ToString ();
951 public ToplevelBlock ParentToplevel {
953 return toplevel_owner.Container;
957 public CaptureContext ParentCaptureContext {
959 ToplevelBlock parent = ParentToplevel;
961 return (parent == null) ? null : parent.CaptureContext;
965 ScopeInfo GetScopeForBlock (Block block)
967 ScopeInfo si = (ScopeInfo) scopes [block.ID];
970 si = new ScopeInfo (this, block);
971 scopes [block.ID] = si;
975 public void AddLocal (AnonymousContainer am, LocalInfo li)
977 if (li.Block.Toplevel != toplevel_owner){
978 ParentCaptureContext.AddLocal (am, li);
981 ScopeInfo scope = GetScopeForBlock (li.Block);
987 Host.RegisterScope (scope);
992 am.RegisterScope (scope);
994 if (captured_variables [li] != null)
997 have_captured_vars = true;
998 captured_variables [li] = li;
1003 // Retursn the CaptureContext for the block that defines the parameter `name'
1005 static CaptureContext _ContextForParameter (ToplevelBlock current, string name)
1007 ToplevelBlock container = current.Container;
1008 if (container != null){
1009 CaptureContext cc = _ContextForParameter (container, name);
1013 if (current.IsParameterReference (name))
1014 return current.ToplevelBlockCaptureContext;
1018 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1020 CaptureContext cc = _ContextForParameter (current, name);
1022 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1027 // Records the captured parameter at the appropriate CaptureContext
1029 public void AddParameter (EmitContext ec, AnonymousContainer am,
1030 string name, Type t, int idx)
1032 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1034 cc.AddParameterToContext (am, name, t, idx);
1038 // Records the parameters in the context
1040 public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
1042 if (captured_parameters == null)
1043 captured_parameters = new Hashtable ();
1044 if (captured_parameters [name] == null)
1045 captured_parameters [name] = new CapturedParameter (t, idx);
1047 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1048 scope.HostsParameters = true;
1049 am.RegisterScope (scope);
1053 // Captured fields are only recorded on the topmost CaptureContext, because that
1054 // one is the one linked to the owner of instance fields
1056 public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
1058 if (fe.FieldInfo.IsStatic)
1059 throw new Exception ("Attempt to register a static field as a captured field");
1060 CaptureContext parent = ParentCaptureContext;
1061 if (parent != null) {
1062 parent.AddField (ec, am, fe);
1066 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1067 am.RegisterScope (scope);
1070 public void CaptureThis (AnonymousContainer am)
1073 throw new Exception ("Internal Compiler error: Capturethis called with a null method");
1074 CaptureContext parent = ParentCaptureContext;
1075 if (parent != null) {
1076 parent.CaptureThis (am);
1079 referenced_this = true;
1081 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1082 am.RegisterScope (scope);
1085 public bool HaveCapturedVariables {
1087 return have_captured_vars;
1091 public bool HaveCapturedFields {
1093 CaptureContext parent = ParentCaptureContext;
1095 return parent.HaveCapturedFields;
1096 return captured_fields.Count > 0;
1100 public bool IsCaptured (LocalInfo local)
1102 foreach (ScopeInfo si in scopes.Values){
1103 if (si.IsCaptured (local))
1110 // Returns whether the parameter is captured
1112 public bool IsParameterCaptured (string name)
1114 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1117 if (captured_parameters != null)
1118 return captured_parameters [name] != null;
1122 public void EmitAnonymousHelperClasses (EmitContext ec)
1124 if (roots.Count != 0){
1125 foreach (ScopeInfo root in roots){
1127 // FIXME: We really should do this in a per-ScopeInfo
1128 // basis, instead of having the NeedThis applied to
1129 // all of the roots.
1131 root.NeedThis = HaveCapturedFields || referenced_this;
1133 root.EmitScopeType (ec);
1138 public void CloseAnonymousHelperClasses ()
1140 if (roots.Count != 0)
1141 foreach (ScopeInfo root in roots)
1145 public void EmitInitScope (EmitContext ec)
1147 EmitAnonymousHelperClasses (ec);
1148 if (roots.Count != 0)
1149 foreach (ScopeInfo root in roots)
1150 root.EmitInitScope (ec); }
1153 // This is called externally when we start emitting code for a block
1154 // if the block has a ScopeInfo associated, emit the init code
1156 public void EmitScopeInitFromBlock (EmitContext ec, Block b)
1158 ScopeInfo si = (ScopeInfo) scopes [b.ID];
1162 si.EmitInitScope (ec);
1166 // Emits the opcodes necessary to load the instance of the captured
1169 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1170 AnonymousContainer am)
1172 ILGenerator ig = ec.ig;
1175 if (li.Block.Toplevel == toplevel_owner){
1176 si = (ScopeInfo) scopes [li.Block.ID];
1177 si.EmitScopeInstance (ig);
1182 ig.Emit (OpCodes.Ldarg_0);
1184 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1188 while (si.ScopeBlock.ID != li.Block.ID){
1189 if (si.ParentLink != null)
1190 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1191 si = si.ParentScope;
1194 Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
1195 while (si.ScopeBlock.ID != li.Block.ID){
1196 Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
1197 si = si.ParentScope;
1200 throw new Exception (
1201 String.Format ("Never found block {0} starting at {1} while looking up {2}",
1202 li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
1209 // Internal routine that loads the instance to reach parameter `name'
1211 void EmitParameterInstance (EmitContext ec, string name)
1213 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1215 cc.EmitParameterInstance (ec, name);
1219 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1220 if (par_info != null){
1222 // FIXME: implementing this.
1225 ILGenerator ig = ec.ig;
1229 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1230 si = (ScopeInfo) scopes [toplevel_owner.ID];
1231 si.EmitScopeInstance (ig);
1233 si = ec.CurrentAnonymousMethod.Scope;
1234 ig.Emit (OpCodes.Ldarg_0);
1238 while (si.ParentLink != null) {
1239 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1240 si = si.ParentScope;
1246 // Emits the code necessary to load the parameter named `name' within
1247 // an anonymous method.
1249 public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
1251 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1253 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1257 EmitParameterInstance (ec, name);
1258 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1259 if (par_info != null){
1261 // FIXME: implementing this.
1264 ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
1267 ec.ig.Emit (OpCodes.Dup);
1268 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1274 // Implements the assignment of `source' to the paramenter named `name' within
1275 // an anonymous method.
1277 public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
1279 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1281 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1284 ILGenerator ig = ec.ig;
1285 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1287 EmitParameterInstance (ec, name);
1288 if (prepare_for_load)
1289 ig.Emit (OpCodes.Dup);
1292 ig.Emit (OpCodes.Dup);
1293 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1296 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1302 // Emits the address for the parameter named `name' within
1303 // an anonymous method.
1305 public void EmitAddressOfParameter (EmitContext ec, string name)
1307 CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
1309 cc.EmitAddressOfParameter (ec, name);
1312 EmitParameterInstance (ec, name);
1313 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1314 ec.ig.Emit (OpCodes.Ldflda, par_info.FieldBuilder);
1318 // The following methods are only invoked on the host for the
1319 // anonymous method.
1321 public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
1323 ILGenerator ig = target.ig;
1324 ScopeInfo si = am.Scope;
1326 AnonymousContainer container = am.ContainerAnonymousMethod;
1328 if ((si == null) || ((container != null) && (si == container.Scope))) {
1329 ig.Emit (OpCodes.Ldarg_0);
1333 si.EmitInitScope (target);
1334 si.EmitScopeInstance (ig);
1337 public void RegisterCaptureContext ()
1339 toplevel_owner.RegisterCaptureContext (this);
1343 // Returs true if `probe' is an ancestor of `scope' in the
1346 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1348 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1349 if (probe.ScopeBlock == b)
1356 // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
1357 // of `scope' found in `scope_list'.
1359 // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
1361 object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
1363 object ancestors = null;
1365 for (int i = 0; i < scope_list.Length; i++){
1366 // Ignore the same scope
1367 if (scope_list [i] == scope)
1370 if (IsAncestor (scope_list [i], scope)){
1371 if (ancestors == null){
1372 ancestors = scope_list [i];
1376 if (ancestors is ScopeInfo){
1377 object old = ancestors;
1378 ancestors = new ArrayList (4);
1379 ((ArrayList)ancestors).Add (old);
1382 ((ArrayList)ancestors).Add (scope_list [i]);
1389 // Returns the immediate parent of `scope' from all the captured
1390 // scopes found in `scope_list', or null if this is a toplevel scope.
1392 ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
1394 object ancestors = GetAncestorScopes (scope, scope_list);
1395 if (ancestors == null)
1398 // Single match, thats the parent.
1399 if (ancestors is ScopeInfo)
1400 return (ScopeInfo) ancestors;
1402 ArrayList candidates = (ArrayList) ancestors;
1403 ScopeInfo parent = (ScopeInfo) candidates [0];
1404 for (int i = 1; i < candidates.Count; i++){
1405 if (IsAncestor (parent, (ScopeInfo) candidates [i]))
1406 parent = (ScopeInfo) candidates [i];
1412 // Links all the scopes
1415 public void LinkScopes ()
1421 if (ParentCaptureContext != null)
1422 ParentCaptureContext.LinkScopes ();
1424 int scope_count = scopes.Keys.Count;
1425 ScopeInfo [] scope_list = new ScopeInfo [scope_count];
1426 scopes.Values.CopyTo (scope_list, 0);
1428 for (int i = 0; i < scope_count; i++){
1429 ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
1431 if (parent == null){
1432 roots.Add (scope_list [i]);
1436 scope_list [i].ParentScope = parent;
1437 parent.AddChild (scope_list [i]);
1441 // Link the roots to their parent containers if any.
1443 if (ParentCaptureContext != null && roots.Count != 0){
1444 ScopeInfo one_root = (ScopeInfo) roots [0];
1447 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1448 if (!IsAncestor (a_parent_root, one_root))
1453 // Found, link all the roots to this root
1454 foreach (ScopeInfo root in roots){
1455 root.ParentScope = a_parent_root;
1456 a_parent_root.AddChild (root);
1462 // This is to catch a condition in which it is
1463 // not possible to determine the containing ScopeInfo
1464 // from an encapsulating CaptureContext
1466 throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");