2006-04-27 Jonathan Chambers <jonathan.chambers@ansys.com>
[mcs.git] / gmcs / anonymous.cs
blob0970d2359930427acff774dcf6a88da80542c399
1 //
2 // anonymous.cs: Support for anonymous methods
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximain.com)
6 //
7 // (C) 2003, 2004 Novell, Inc.
8 //
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
15 using System;
16 using System.Text;
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
44 public Method method;
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;
54 // The method scope
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
66 // host the method
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;
79 Block = block;
80 loc = l;
83 // The order is important: this setups the CaptureContext tree hierarchy.
85 if (container == null) {
86 return;
88 container.SetHaveAnonymousMethods (l, this);
89 block.SetHaveAnonymousMethods (l, this);
92 protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
93 Location l):
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
109 // rules.
111 type = TypeManager.anonymous_method_type;
113 return this;
116 public void RegisterScope (ScopeInfo scope)
118 if (scopes_used.Contains (scope))
119 return;
120 scopes_used.Add (scope);
123 // Returns the deepest of two scopes
124 ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
126 ScopeInfo p;
128 if (a == null)
129 return b;
130 if (b == null)
131 return a;
132 if (a == b)
133 return a;
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)
141 if (p == b)
142 return a;
144 for (p = b; p != null; p = p.ParentScope)
145 if (p == a)
146 return b;
148 CaptureContext ca = a.CaptureContext;
149 CaptureContext cb = b.CaptureContext;
151 for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
152 if (c == cb)
153 return a;
155 for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
156 if (c == ca)
157 return b;
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)
168 return;
170 method_scope = null;
171 int top = scopes_used.Count;
172 computed_method_scope = true;
174 if (top == 0)
175 return;
177 method_scope = (ScopeInfo) scopes_used [0];
178 if (top == 1)
179 return;
181 for (int i = 1; i < top; i++)
182 method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
185 public ScopeInfo Scope {
186 get {
187 if (computed_method_scope)
188 return 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 {
204 get;
207 public abstract bool IsIterator {
208 get;
212 public class AnonymousMethod : AnonymousContainer
214 TypeContainer host;
216 public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
217 ToplevelBlock block, Location l)
218 : base (parameters, container, block, l)
220 this.host = host;
223 public override Iterator Iterator {
224 get { return null; }
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;
261 current_type = null;
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),
279 Parameters);
281 generic_method.SetParameterInfo (null);
282 } else
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;
300 return res;
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)
312 return true;
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)
319 return false;
321 for (int i = 0; i < Parameters.Count; ++i) {
322 if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
323 return false;
325 return true;
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);
369 } else {
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);
375 return null;
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)));
384 else
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);
388 return null;
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}'",
393 (i+1).ToString (),
394 TypeManager.CSharpName (Parameters.ParameterType (i)),
395 TypeManager.CSharpName (invoke_pd.ParameterType (i)));
396 Error_ParameterMismatch (delegate_type);
397 return null;
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);
427 return null;
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");
434 return null;
437 if (Parameters != null && !Parameters.Resolve (ec)) {
438 return null;
441 return base.DoResolve (ec);
445 public override string ExprClassName {
446 get {
447 return "anonymous method";
451 public MethodInfo GetMethodBuilder ()
453 MethodInfo builder = method.MethodBuilder;
454 if (TypeArguments != null)
455 return builder.MakeGenericMethod (TypeArguments);
456 else
457 return builder;
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))
469 return false;
471 MethodBuilder builder = method.MethodBuilder;
472 ILGenerator ig = builder.GetILGenerator ();
473 aec.ig = ig;
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);
485 return true;
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);
523 } else
524 scope.ScopeType = scope.ScopeTypeBuilder;
527 if (ec.TypeContainer.IsGeneric)
528 scope.ScopeConstructor = TypeBuilder.GetConstructor (
529 scope.ScopeType, ctor);
530 else
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",
538 name);
543 // This will emit the code for the delegate, as well delegate creation on the host
545 public class AnonymousDelegate : DelegateCreation {
546 AnonymousMethod am;
548 public AnonymousDelegate (AnonymousMethod am, Type target_type, Location l)
550 type = target_type;
551 loc = l;
552 this.am = am;
555 public override Expression DoResolve (EmitContext ec)
557 eclass = ExprClass.Value;
559 return this;
562 public override void Emit (EmitContext ec)
564 if (!am.EmitMethod (ec))
565 return;
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 ();
576 base.Emit (ec);
579 class AnonymousInstance : Expression {
580 AnonymousMethod am;
582 public AnonymousInstance (AnonymousMethod am)
584 this.am = am;
585 eclass = ExprClass.Value;
588 public override Expression DoResolve (EmitContext ec)
590 return this;
593 public override void Emit (EmitContext ec)
595 am.aec.EmitMethodHostInstance (ec, am);
600 class CapturedParameter {
601 public Type Type;
602 public FieldBuilder FieldBuilder;
603 public int Idx;
605 public CapturedParameter (Type type, int idx)
607 Type = type;
608 Idx = 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.
624 public int id;
625 static int count;
626 bool inited = false;
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)
648 CaptureContext = cc;
649 ScopeBlock = b;
650 id = count++;
652 cc.RegisterCaptureContext ();
655 public void AddLocal (LocalInfo li)
657 if (locals.Contains (li))
658 return;
660 locals.Add (li);
663 public bool IsCaptured (LocalInfo li)
665 return locals.Contains (li);
668 internal void AddChild (ScopeInfo si)
670 if (children.Contains (si))
671 return;
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;
683 si.AddChild (child);
687 children.Add (si);
689 if (move_queue != null){
690 foreach (ScopeInfo child in move_queue){
691 children.Remove (child);
696 static int indent = 0;
698 void Pad ()
700 for (int i = 0; i < indent; i++)
701 Console.Write (" ");
704 void EmitDebug ()
706 //Console.WriteLine (Environment.StackTrace);
707 Pad ();
708 Console.WriteLine ("START");
709 indent++;
710 Pad ();
711 Console.WriteLine ("NeedThis=" + NeedThis);
712 foreach (LocalInfo li in locals){
713 Pad ();
714 Console.WriteLine ("var {0}", MakeFieldName (li.Name));
717 foreach (ScopeInfo si in children)
718 si.EmitDebug ();
719 indent--;
720 Pad ();
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)
736 // EmitDebug ();
738 if (ScopeTypeBuilder != null)
739 return;
741 Type container;
742 if (ec.TypeContainer.CurrentType != null)
743 container = ec.TypeContainer.CurrentType;
744 else
745 container = ec.TypeContainer.TypeBuilder;
747 CaptureContext.Host.CreateScopeType (ec, this);
749 if (NeedThis)
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;
775 FieldBuilder fb;
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)
791 si.CloseTypes ();
795 // Emits the initialization code for the scope
797 public void EmitInitScope (EmitContext ec)
799 ILGenerator ig = ec.ig;
801 if (inited)
802 return;
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);
813 if (THIS != null){
814 if (CaptureContext.Host.IsIterator) {
815 ig.Emit (OpCodes.Ldarg_0);
816 ig.Emit (OpCodes.Ldarg_1);
817 } else {
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)
829 extra++;
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);
854 } else {
855 EmitScopeInstance (ig);
856 ig.Emit (OpCodes.Ldarg_0);
857 ig.Emit (OpCodes.Stfld, ParentLink);
861 inited = true;
864 public void EmitScopeInstance (ILGenerator ig)
866 if (CaptureContext.Host.IsIterator)
867 ig.Emit (OpCodes.Ldarg_0);
868 else {
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);
879 Console.WriteLine (
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 ();
894 int n = 0;
896 for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
897 if (l.Contains (p)){
898 Console.WriteLine ("Loop detected {0} in {1}", n, msg);
899 throw new Exception ();
901 l.Add (p);
905 static void DoPath (StringBuilder sb, ScopeInfo start)
907 CheckCycles ("print", start);
909 if (start.ParentScope != null){
910 DoPath (sb, start.ParentScope);
911 sb.Append (", ");
913 sb.Append ((start.id).ToString ());
916 public override string ToString ()
918 StringBuilder sb = new StringBuilder ();
920 sb.Append ("{");
921 if (CaptureContext != null){
922 sb.Append (CaptureContext.ToString ());
923 sb.Append (":");
926 DoPath (sb, this);
927 sb.Append ("}");
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;
942 public int cc_id;
943 public Location loc;
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;
964 // Captured fields
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)
974 cc_id = count++;
975 this.toplevel_owner = toplevel_owner;
976 this.loc = loc;
978 if (host != null)
979 Host = host;
982 void DoPath (StringBuilder sb, CaptureContext cc)
984 if (cc.ParentCaptureContext != null){
985 DoPath (sb, cc.ParentCaptureContext);
986 sb.Append (".");
988 sb.Append (cc.cc_id.ToString ());
991 public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
993 toplevel_owner = new_toplevel;
994 Host = new_host;
996 for (CaptureContext cc = ParentCaptureContext; cc != null;
997 cc = cc.ParentCaptureContext) {
998 cc.Host = new_host;
1002 public override string ToString ()
1004 StringBuilder sb = new StringBuilder ();
1005 sb.Append ("[");
1006 DoPath (sb, this);
1007 sb.Append ("]");
1008 return sb.ToString ();
1011 public ToplevelBlock ParentToplevel {
1012 get {
1013 return toplevel_owner.Container;
1017 public CaptureContext ParentCaptureContext {
1018 get {
1019 ToplevelBlock parent = ParentToplevel;
1021 return (parent == null) ? null : parent.CaptureContext;
1025 ScopeInfo GetScopeForBlock (Block block)
1027 ScopeInfo si = (ScopeInfo) scopes [block.ID];
1028 if (si != null)
1029 return si;
1030 si = new ScopeInfo (this, block);
1031 scopes [block.ID] = si;
1032 return si;
1035 public void AddLocal (AnonymousContainer am, LocalInfo li)
1037 if (li.Block.Toplevel != toplevel_owner){
1038 ParentCaptureContext.AddLocal (am, li);
1039 return;
1041 ScopeInfo scope = GetScopeForBlock (li.Block);
1044 // Adjust the owner
1046 if (Host != null)
1047 Host.RegisterScope (scope);
1050 // Adjust the user
1052 am.RegisterScope (scope);
1054 if (captured_variables [li] != null)
1055 return;
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);
1070 if (cc != null)
1071 return cc;
1073 if (current.IsParameterReference (name))
1074 return current.ToplevelBlockCaptureContext;
1075 return null;
1078 static CaptureContext ContextForParameter (ToplevelBlock current, string name)
1080 CaptureContext cc = _ContextForParameter (current, name);
1081 if (cc == null)
1082 throw new Exception (String.Format ("request for parameteter {0} failed: not found", name));
1083 return cc;
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);
1123 return;
1126 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1127 am.RegisterScope (scope);
1130 public void CaptureThis (AnonymousContainer am)
1132 if (am == null)
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);
1137 return;
1139 referenced_this = true;
1141 ScopeInfo scope = GetScopeForBlock (toplevel_owner);
1142 am.RegisterScope (scope);
1145 public bool HaveCapturedVariables {
1146 get {
1147 return have_captured_vars;
1151 public bool HaveCapturedFields {
1152 get {
1153 CaptureContext parent = ParentCaptureContext;
1154 if (parent != null)
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))
1164 return true;
1166 return false;
1170 // Returns whether the parameter is captured
1172 public bool IsParameterCaptured (string name)
1174 if (ParentCaptureContext != null && ParentCaptureContext.IsParameterCaptured (name))
1175 return true;
1177 if (captured_parameters != null)
1178 return captured_parameters [name] != null;
1179 return false;
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)
1202 root.CloseTypes ();
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];
1219 if (si == null)
1220 return;
1222 si.EmitInitScope (ec);
1226 // Emits the opcodes necessary to load the instance of the captured
1227 // variable in `li'
1229 public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
1230 AnonymousContainer am)
1232 ILGenerator ig = ec.ig;
1233 ScopeInfo si;
1235 if (li.Block.Toplevel == toplevel_owner){
1236 si = (ScopeInfo) scopes [li.Block.ID];
1237 si.EmitScopeInstance (ig);
1238 return;
1241 si = am.Scope;
1242 ig.Emit (OpCodes.Ldarg_0);
1243 if (si != null){
1244 if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
1245 return;
1248 while (si.ScopeBlock.ID != li.Block.ID){
1249 if (si.ParentLink != null)
1250 ig.Emit (OpCodes.Ldfld, si.ParentLink);
1251 si = si.ParentScope;
1252 if (si == null) {
1253 si = am.Scope;
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);
1274 if (cc != this){
1275 cc.EmitParameterInstance (ec, name);
1276 return;
1279 CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
1280 if (par_info != null){
1282 // FIXME: implementing this.
1285 ILGenerator ig = ec.ig;
1287 ScopeInfo si;
1289 if (ec.CurrentBlock.Toplevel == toplevel_owner) {
1290 si = (ScopeInfo) scopes [toplevel_owner.ID];
1291 si.EmitScopeInstance (ig);
1292 } else {
1293 si = ec.CurrentAnonymousMethod.Scope;
1294 ig.Emit (OpCodes.Ldarg_0);
1297 if (si != null){
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);
1312 if (cc != this){
1313 cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
1314 return;
1316 if (!prepared)
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);
1326 if (leave_copy){
1327 ec.ig.Emit (OpCodes.Dup);
1328 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1329 temp.Store (ec);
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);
1340 if (cc != this){
1341 cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
1342 return;
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);
1350 source.Emit (ec);
1351 if (leave_copy){
1352 ig.Emit (OpCodes.Dup);
1353 temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
1354 temp.Store (ec);
1356 ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
1357 if (temp != null)
1358 temp.Emit (ec);
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);
1368 if (cc != this){
1369 cc.EmitAddressOfParameter (ec, name);
1370 return;
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);
1390 return;
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
1404 // scope chain
1406 bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
1408 for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
1409 if (probe.ScopeBlock == b)
1410 return true;
1412 return false;
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)
1428 continue;
1430 if (IsAncestor (scope_list [i], scope)){
1431 if (ancestors == null){
1432 ancestors = scope_list [i];
1433 continue;
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]);
1445 return ancestors;
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)
1456 return 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];
1468 return parent;
1472 // Links all the scopes
1474 bool linked;
1475 public void LinkScopes ()
1477 if (linked)
1478 return;
1480 linked = true;
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]);
1493 continue;
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];
1505 bool found = false;
1507 foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
1508 if (!IsAncestor (a_parent_root, one_root))
1509 continue;
1511 found = true;
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);
1518 break;
1520 if (!found){
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");