namespace Mono.CSharp {
- public class AnonymousMethod : Expression {
+ public abstract class AnonymousContainer : Expression
+ {
// Used to generate unique method names.
- static int anonymous_method_count;
+ protected static int anonymous_method_count;
// An array list of AnonymousMethodParameter or null
public Parameters Parameters;
//
public Method method;
- MethodInfo invoke_mb;
+ protected MethodInfo invoke_mb;
// The emit context for the anonymous method
public EmitContext aec;
- public InternalParameters amp;
- bool unreachable;
+ protected bool unreachable;
+ // The method scope
+ ScopeInfo method_scope;
+ bool computed_method_scope = false;
+
//
// The modifiers applied to the method, we aggregate them
//
- int method_modifiers = Modifiers.INTERNAL;
+ protected int method_modifiers = Modifiers.PRIVATE;
//
- // During the resolve stage of the anonymous method body,
- // we discover the actual scope where we are hosted, or
- // null to host the method in the same class
+ // Track the scopes that this method has used. At the
+ // end this is used to determine the ScopeInfo that will
+ // host the method
//
- public ScopeInfo Scope;
-
+ ArrayList scopes_used = new ArrayList ();
+
//
// Points to our container anonymous method if its present
//
- public AnonymousMethod ContainerAnonymousMethod;
-
- public AnonymousMethod (Parameters parameters, ToplevelBlock container, ToplevelBlock block, Location l)
+ public AnonymousContainer ContainerAnonymousMethod;
+
+ protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
+ ToplevelBlock block, Location l)
{
Parameters = parameters;
Block = block;
//
// The order is important: this setups the CaptureContext tree hierarchy.
//
+ if (container == null) {
+ return;
+ }
container.SetHaveAnonymousMethods (l, this);
block.SetHaveAnonymousMethods (l, this);
}
+ protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
+ Location l):
+ this (parameters, container, new ToplevelBlock (container, parameters, l), l)
+ {
+ }
+
public override Expression DoResolve (EmitContext ec)
{
//
return this;
}
+ public void RegisterScope (ScopeInfo scope)
+ {
+ if (scopes_used.Contains (scope))
+ return;
+ scopes_used.Add (scope);
+ }
+
+ // Returns the deepest of two scopes
+ ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
+ {
+ ScopeInfo p;
+
+ if (a == null)
+ return b;
+ if (b == null)
+ return a;
+ if (a == b)
+ return a;
+
+ //
+ // If they Scopes are on the same CaptureContext, we do the double
+ // checks just so if there is an invariant change in the future,
+ // we get the exception at the end
+ //
+ for (p = a; p != null; p = p.ParentScope)
+ if (p == b)
+ return a;
+
+ for (p = b; p != null; p = p.ParentScope)
+ if (p == a)
+ return b;
+
+ CaptureContext ca = a.CaptureContext;
+ CaptureContext cb = b.CaptureContext;
+
+ for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
+ if (c == cb)
+ return a;
+
+ for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
+ if (c == ca)
+ return b;
+ throw new Exception ("Should never be reached");
+ }
+
+ //
+ // Determines the proper host for a method considering the
+ // scopes it references
+ //
+ public void ComputeMethodHost ()
+ {
+ if (computed_method_scope)
+ return;
+
+ method_scope = null;
+ int top = scopes_used.Count;
+ computed_method_scope = true;
+
+ if (top == 0)
+ return;
+
+ method_scope = (ScopeInfo) scopes_used [0];
+ if (top == 1)
+ return;
+
+ for (int i = 1; i < top; i++)
+ method_scope = Deepest (method_scope, (ScopeInfo) scopes_used [i]);
+ }
+
+ public ScopeInfo Scope {
+ get {
+ if (computed_method_scope)
+ return method_scope;
+
+ //
+ // This means that ComputeMethodHost is not being called, most
+ // likely by someone who overwrote the CreateMethodHost method
+ //
+ throw new Exception ("Internal error, AnonymousContainer.Scope is being used before its container is computed");
+ }
+ }
+
+
+ protected abstract bool CreateMethodHost (EmitContext ec);
+
+ public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
+
+ public abstract bool IsIterator {
+ get;
+ }
+ }
+
+ public class AnonymousMethod : AnonymousContainer
+ {
+ TypeContainer host;
+
+ public AnonymousMethod (TypeContainer host, Parameters parameters, ToplevelBlock container,
+ ToplevelBlock block, Location l)
+ : base (parameters, container, block, l)
+ {
+ this.host = host;
+ }
+
+ public override bool IsIterator {
+ get { return false; }
+ }
+
public override void Emit (EmitContext ec)
{
// nothing, as we only exist to not do anything.
//
// Creates the host for the anonymous method
//
- bool CreateMethodHost (EmitContext ec, Type return_type)
+ protected override bool CreateMethodHost (EmitContext ec)
{
+ ComputeMethodHost ();
+
//
// Crude hack follows: we replace the TypeBuilder during the
// definition to get the method hosted in the right class
//
-
TypeBuilder current_type = ec.TypeContainer.TypeBuilder;
- TypeBuilder type_host = (Scope == null ) // || Scope.ScopeTypeBuilder == null)
- ? current_type : Scope.ScopeTypeBuilder;
+ TypeBuilder type_host = (Scope == null ) ? current_type : Scope.ScopeTypeBuilder;
if (current_type == null)
throw new Exception ("The current_type is null");
if (type_host == null)
- throw new Exception ("Type host is null");
-
+ throw new Exception (String.Format ("Type host is null, method_host is {0}", Scope == null ? "null" : "Not null"));
+
+ if (current_type != type_host)
+ method_modifiers = Modifiers.INTERNAL;
+
if (current_type == type_host && ec.IsStatic){
- if (ec.IsStatic)
- method_modifiers |= Modifiers.STATIC;
+ method_modifiers |= Modifiers.STATIC;
current_type = null;
}
method = new Method (
(TypeContainer) ec.TypeContainer,
- new TypeExpression (return_type, loc),
- method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
- Parameters, null, loc);
+ new TypeExpression (invoke_mb.ReturnType, loc),
+ method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++, loc),
+ Parameters, null);
method.Block = Block;
-
//
// Swap the TypeBuilder while we define the method, then restore
ec.TypeContainer.TypeBuilder = current_type;
return res;
}
-
+
void Error_ParameterMismatch (Type t)
{
Report.Error (1661, loc, "Anonymous method could not be converted to delegate `" +
- "{0}' since there is a parameter mismatch", t);
+ "{0}' since there is a parameter mismatch", TypeManager.CSharpName (t));
+ }
+
+ public bool ImplicitStandardConversionExists (Type delegate_type)
+ {
+ if (Parameters == null)
+ return true;
+
+ invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (host.TypeBuilder, delegate_type, loc);
+ ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
+
+ if (Parameters.Count != invoke_pd.Count)
+ return false;
+
+ for (int i = 0; i < Parameters.Count; ++i) {
+ if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i))
+ return false;
+ }
+ return true;
}
//
// Returns true if this anonymous method can be implicitly
// converted to the delegate type `delegate_type'
//
- public Expression Compatible (EmitContext ec, Type delegate_type, bool probe)
+ public Expression Compatible (EmitContext ec, Type delegate_type)
{
//
// At this point its the first time we know the return type that is
// needed for the anonymous method. We create the method here.
//
- invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec, delegate_type, loc);
+ invoke_mb = (MethodInfo) Delegate.GetInvokeMethod (ec.ContainerType, delegate_type, loc);
ParameterData invoke_pd = TypeManager.GetParameterData (invoke_mb);
- //
- // If implicit parameters are set, then we must check for out in the parameters
- // and flag it accordingly.
- //
- bool out_invalid_check = false;
-
- if (Parameters == null){
- int i, j;
- out_invalid_check = true;
-
+ if (Parameters == null) {
//
// We provide a set of inaccessible parameters
//
- int params_idx = -1;
- for (i = 0; i < invoke_pd.Count; i++){
- if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
- params_idx = i;
- }
- int n = invoke_pd.Count - (params_idx != -1 ? 1 : 0);
- Parameter [] fixedpars = new Parameter [n];
-
- for (i = j = 0; i < invoke_pd.Count; i++){
- if (invoke_pd.ParameterModifier (i) == Parameter.Modifier.PARAMS)
- continue;
- fixedpars [j] = new Parameter (
- new TypeExpression (invoke_pd.ParameterType (i), loc),
- "+" + j, invoke_pd.ParameterModifier (i), null);
- j++;
+ Parameter [] fixedpars = new Parameter [invoke_pd.Count];
+
+ for (int i = 0; i < invoke_pd.Count; i++){
+ fixedpars [i] = new Parameter (
+ invoke_pd.ParameterType (i),
+ "+" + i, invoke_pd.ParameterModifier (i), null, loc);
}
-
- Parameter variable = null;
- if (params_idx != -1){
- variable = new Parameter (
- new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
- "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null);
- }
-
- Parameters = new Parameters (fixedpars, variable, loc);
- }
-
- //
- // First, parameter types of `delegate_type' must be compatible
- // with the anonymous method.
- //
- amp = new InternalParameters (Parameters.GetParameterInfo (ec), Parameters);
-
- if (amp.Count != invoke_pd.Count){
- if (!probe){
- Report.Error (1593, loc,
- "Anonymous method has {0} parameters, while delegate requires {1}",
- amp.Count, invoke_pd.Count);
+
+ Parameters = new Parameters (fixedpars);
+ } else {
+ if (Parameters.Count != invoke_pd.Count) {
+ Report.SymbolRelatedToPreviousError (delegate_type);
+ Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
+ TypeManager.CSharpName (delegate_type), Parameters.Count.ToString ());
Error_ParameterMismatch (delegate_type);
- }
- return null;
- }
-
- for (int i = 0; i < amp.Count; i++){
- Parameter.Modifier amp_mod = amp.ParameterModifier (i);
- if (amp_mod != invoke_pd.ParameterModifier (i)){
- if (!probe){
- Report.Error (1676, loc,
- "Signature mismatch in parameter modifier for parameter #0", i + 1);
- Error_ParameterMismatch (delegate_type);
- }
return null;
}
-
- if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
- if (!probe){
- Report.Error (1678, loc,
- "Signature mismatch in parameter {0}: need `{1}' got `{2}'", i + 1,
- TypeManager.CSharpName (invoke_pd.ParameterType (i)),
- TypeManager.CSharpName (amp.ParameterType (i)));
+
+ for (int i = 0; i < Parameters.Count; ++i) {
+ Parameter.Modifier p_mod = invoke_pd.ParameterModifier (i);
+ if (Parameters.ParameterModifier (i) != p_mod && p_mod != Parameter.Modifier.PARAMS) {
+ if (p_mod == Parameter.Modifier.NONE)
+ Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
+ (i + 1).ToString (), Parameter.GetModifierSignature (Parameters.ParameterModifier (i)));
+ else
+ Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
+ (i+1).ToString (), Parameter.GetModifierSignature (p_mod));
Error_ParameterMismatch (delegate_type);
+ return null;
}
- return null;
- }
-
- if (out_invalid_check && (invoke_pd.ParameterModifier (i) & Parameter.Modifier.OUT) != 0){
- if (!probe){
- Report.Error (1676, loc,"Parameter {0} must include the `out' modifier ", i+1);
+
+ if (invoke_pd.ParameterType (i) != Parameters.ParameterType (i)) {
+ Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
+ (i+1).ToString (),
+ TypeManager.CSharpName (Parameters.ParameterType (i)),
+ TypeManager.CSharpName (invoke_pd.ParameterType (i)));
Error_ParameterMismatch (delegate_type);
+ return null;
}
- return null;
}
}
-
- //
- // If we are only probing, return ourselves
- //
- if (probe)
- return this;
//
// Second: the return type of the delegate must be compatible with
//MethodBuilder builder = method_data.MethodBuilder;
//ILGenerator ig = builder.GetILGenerator ();
-
- aec = new EmitContext (
- ec.TypeContainer, ec.DeclSpace, loc, null,
+ aec = new EmitContext (ec.ResolveContext,
+ ec.TypeContainer, ec.DeclContainer, loc, null,
invoke_mb.ReturnType,
/* REVIEW */ (ec.InIterator ? Modifiers.METHOD_YIELDS : 0) |
- (ec.InUnsafe ? Modifiers.UNSAFE : 0),
+ (ec.InUnsafe ? Modifiers.UNSAFE : 0) |
+ (ec.IsStatic ? Modifiers.STATIC : 0),
/* No constructor */ false);
aec.CurrentAnonymousMethod = this;
ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
ContainingBlock = ec.CurrentBlock;
-
- if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
+
+ if (aec.ResolveTopBlock (ec, Block, Parameters, null, out unreachable))
return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
return null;
}
+ public override Expression DoResolve (EmitContext ec)
+ {
+ if (!ec.IsAnonymousMethodAllowed) {
+ Report.Error (1706, loc, "Anonymous methods are not allowed in the attribute declaration");
+ return null;
+ }
+
+ if (Parameters != null && !Parameters.Resolve (ec)) {
+ return null;
+ }
+
+ return base.DoResolve (ec);
+ }
+
+
+ public override string ExprClassName {
+ get {
+ return "anonymous method";
+ }
+ }
+
public MethodBuilder GetMethodBuilder ()
{
- return method.MethodData.MethodBuilder;
+ return method.MethodBuilder;
+ }
+
+ public override string GetSignatureForError ()
+ {
+ string s = TypeManager.CSharpSignature (invoke_mb);
+ return s.Substring (0, s.IndexOf (".Invoke("));
}
- public void EmitMethod (EmitContext ec)
+ public bool EmitMethod (EmitContext ec)
{
- if (!CreateMethodHost (ec, invoke_mb.ReturnType))
- return;
+ if (!CreateMethodHost (ec))
+ return false;
MethodBuilder builder = GetMethodBuilder ();
ILGenerator ig = builder.GetILGenerator ();
aec.ig = ig;
- Parameters.LabelParameters (aec, builder, loc);
+ Parameters.ApplyAttributes (builder);
//
// Adjust based on the computed state of the
// method from CreateMethodHost
- if ((method_modifiers & Modifiers.STATIC) != 0)
- aec.IsStatic = true;
+ aec.MethodIsStatic = (method_modifiers & Modifiers.STATIC) != 0;
- aec.EmitMeta (Block, amp);
+ aec.EmitMeta (Block);
aec.EmitResolvedTopBlock (Block, unreachable);
+ return true;
+ }
+
+ public override void CreateScopeType (EmitContext ec, ScopeInfo scope)
+ {
+ TypeBuilder container = ec.TypeContainer.TypeBuilder;
+ string name = String.Format ("<>AnonHelp<{0}>", scope.id);
+
+ scope.ScopeTypeBuilder = container.DefineNestedType (
+ name, TypeAttributes.AutoLayout | TypeAttributes.Class |
+ TypeAttributes.NestedAssembly, TypeManager.object_type, null);
+
+ Type [] constructor_types = Type.EmptyTypes;
+ scope.ScopeConstructor = scope.ScopeTypeBuilder.DefineConstructor (
+ MethodAttributes.Public | MethodAttributes.HideBySig |
+ MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
+ CallingConventions.HasThis, constructor_types);
+
+ TypeManager.RegisterMethod (scope.ScopeConstructor, Parameters.EmptyReadOnlyParameters);
+
+ ILGenerator cig = scope.ScopeConstructor.GetILGenerator ();
+ cig.Emit (OpCodes.Ldarg_0);
+ cig.Emit (OpCodes.Call, TypeManager.object_ctor);
+ cig.Emit (OpCodes.Ret);
}
public static void Error_AddressOfCapturedVar (string name, Location loc)
{
Report.Error (1686, loc,
- "Variable {0} is captured in an anonymous method and its address is also being taken: they are exclusive", name);
+ "Local variable `{0}' or its members cannot have their address taken and be used inside an anonymous method block",
+ name);
}
}
public override Expression DoResolve (EmitContext ec)
{
eclass = ExprClass.Value;
+
return this;
}
public override void Emit (EmitContext ec)
{
- am.EmitMethod (ec);
+ if (!am.EmitMethod (ec))
+ return;
//
// Now emit the delegate creation.
if ((am.method.ModFlags & Modifiers.STATIC) == 0)
delegate_instance_expression = new AnonymousInstance (am);
- Expression ml = Expression.MemberLookup (ec, type, ".ctor", loc);
+ Expression ml = Expression.MemberLookup (ec.ContainerType, type, ".ctor", loc);
constructor_method = ((MethodGroupExpr) ml).Methods [0];
delegate_method = am.GetMethodBuilder ();
base.Emit (ec);
// Points to the object of type `ScopeTypeBuilder' that
// holds the data for the scope
//
- public LocalBuilder ScopeInstance;
-
+ LocalBuilder scope_instance;
public ScopeInfo (CaptureContext cc, Block b)
{
ScopeBlock = b;
id = count++;
- cc.AddScope (this);
+ cc.RegisterCaptureContext ();
}
public void AddLocal (LocalInfo li)
return locals.Contains (li);
}
- public void AddChild (ScopeInfo si)
+ internal void AddChild (ScopeInfo si)
{
if (children.Contains (si))
return;
+
+ //
+ // If any of the current children should be a children of `si', move them there
+ //
+ ArrayList move_queue = null;
+ foreach (ScopeInfo child in children){
+ if (child.ScopeBlock.IsChildOf (si.ScopeBlock)){
+ if (move_queue == null)
+ move_queue = new ArrayList ();
+ move_queue.Add (child);
+ child.ParentScope = si;
+ si.AddChild (child);
+ }
+ }
+
children.Add (si);
- }
+
+ if (move_queue != null){
+ foreach (ScopeInfo child in move_queue){
+ children.Remove (child);
+ }
+ }
+ }
static int indent = 0;
Console.WriteLine ("NeedThis=" + NeedThis);
foreach (LocalInfo li in locals){
Pad ();
- Console.WriteLine ("var {0}", li.Name);
+ Console.WriteLine ("var {0}", MakeFieldName (li.Name));
}
foreach (ScopeInfo si in children)
return String.Format ("<>AnonHelp<{0}>", id);
}
- public void EmitScopeConstructor ()
+ private string MakeFieldName (string local_name)
{
- Type [] constructor_types = TypeManager.NoTypes;
- Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
- ScopeConstructor = ScopeTypeBuilder.DefineConstructor (
- MethodAttributes.Public | MethodAttributes.HideBySig |
- MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
- CallingConventions.HasThis, constructor_types);
- InternalParameters parameter_info = new InternalParameters (constructor_types, constructor_parameters);
- TypeManager.RegisterMethod (ScopeConstructor, parameter_info, constructor_types);
-
- ILGenerator cig = ScopeConstructor.GetILGenerator ();
- cig.Emit (OpCodes.Ldarg_0);
- cig.Emit (OpCodes.Call, TypeManager.object_ctor);
- cig.Emit (OpCodes.Ret);
+ return "<" + id + ":" + local_name + ">";
}
-
+
public void EmitScopeType (EmitContext ec)
{
- //EmitDebug ();
+ // EmitDebug ();
if (ScopeTypeBuilder != null)
return;
TypeBuilder container = ec.TypeContainer.TypeBuilder;
- ScopeTypeBuilder = container.DefineNestedType (
- MakeHelperName (), TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.NestedAssembly,
- TypeManager.object_type, null);
-
+ CaptureContext.Host.CreateScopeType (ec, this);
+
if (NeedThis)
THIS = ScopeTypeBuilder.DefineField ("<>THIS", container, FieldAttributes.Assembly);
if (ParentScope.ScopeTypeBuilder == null){
throw new Exception (String.Format ("My parent has not been initialized {0} and {1}", ParentScope, this));
}
-
- ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
- FieldAttributes.Assembly);
+
+ if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder)
+ ParentLink = ScopeTypeBuilder.DefineField ("<>parent", ParentScope.ScopeTypeBuilder,
+ FieldAttributes.Assembly);
}
if (NeedThis && ParentScope != null)
throw new Exception ("I was not expecting THIS && having a parent");
- foreach (LocalInfo info in locals){
+ foreach (LocalInfo info in locals)
info.FieldBuilder = ScopeTypeBuilder.DefineField (
- info.Name, info.VariableType, FieldAttributes.Assembly);
- }
+ MakeFieldName (info.Name), info.VariableType, FieldAttributes.Assembly);
if (HostsParameters){
Hashtable captured_parameters = CaptureContext.captured_parameters;
}
}
- EmitScopeConstructor ();
foreach (ScopeInfo si in children){
si.EmitScopeType (ec);
}
public void CloseTypes ()
{
- RootContext.RegisterHelperClass (ScopeTypeBuilder);
+ RootContext.RegisterCompilerGeneratedType (ScopeTypeBuilder);
foreach (ScopeInfo si in children)
si.CloseTypes ();
}
if (inited)
return;
- ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
- ScopeInstance = ig.DeclareLocal (ScopeTypeBuilder);
- ig.Emit (OpCodes.Stloc, ScopeInstance);
+ if (ScopeConstructor == null)
+ throw new Exception ("ScopeConstructor is null for" + this.ToString ());
+
+ if (!CaptureContext.Host.IsIterator) {
+ scope_instance = ig.DeclareLocal (ScopeTypeBuilder);
+ ig.Emit (OpCodes.Newobj, (ConstructorInfo) ScopeConstructor);
+ ig.Emit (OpCodes.Stloc, scope_instance);
+ }
if (THIS != null){
- ig.Emit (OpCodes.Ldloc, ScopeInstance);
- ig.Emit (OpCodes.Ldarg_0);
+ if (CaptureContext.Host.IsIterator) {
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Ldarg_1);
+ } else {
+ ig.Emit (OpCodes.Ldloc, scope_instance);
+ ig.Emit (OpCodes.Ldarg_0);
+ }
ig.Emit (OpCodes.Stfld, THIS);
}
// Copy the parameter values, if any
//
int extra = ec.IsStatic ? 0 : 1;
+ if (CaptureContext.Host.IsIterator)
+ extra++;
if (HostsParameters){
Hashtable captured_parameters = CaptureContext.captured_parameters;
foreach (DictionaryEntry de in captured_parameters){
CapturedParameter cp = (CapturedParameter) de.Value;
- ig.Emit (OpCodes.Ldloc, ScopeInstance);
+ EmitScopeInstance (ig);
ParameterReference.EmitLdArg (ig, cp.Idx + extra);
ig.Emit (OpCodes.Stfld, cp.FieldBuilder);
}
}
-
+
if (ParentScope != null){
if (!ParentScope.inited)
ParentScope.EmitInitScope (ec);
-
- //
- // Only emit initialization in our capturecontext world
- //
- if (ParentScope.CaptureContext == CaptureContext){
- ig.Emit (OpCodes.Ldloc, ScopeInstance);
- ig.Emit (OpCodes.Ldloc, ParentScope.ScopeInstance);
- ig.Emit (OpCodes.Stfld, ParentLink);
- } else {
- ig.Emit (OpCodes.Ldloc, ScopeInstance);
- ig.Emit (OpCodes.Ldarg_0);
- ig.Emit (OpCodes.Stfld, ParentLink);
+
+ if (ParentScope.ScopeTypeBuilder != ScopeTypeBuilder) {
+ //
+ // Only emit initialization in our capturecontext world
+ //
+ if (ParentScope.CaptureContext == CaptureContext){
+ EmitScopeInstance (ig);
+ ParentScope.EmitScopeInstance (ig);
+ ig.Emit (OpCodes.Stfld, ParentLink);
+ } else {
+ EmitScopeInstance (ig);
+ ig.Emit (OpCodes.Ldarg_0);
+ ig.Emit (OpCodes.Stfld, ParentLink);
+ }
}
}
inited = true;
}
+ public void EmitScopeInstance (ILGenerator ig)
+ {
+ if (CaptureContext.Host.IsIterator)
+ ig.Emit (OpCodes.Ldarg_0);
+ else {
+ if (scope_instance == null){
+ //
+ // This is needed if someone overwrites the Emit method
+ // of Statement and manually calls Block.Emit without
+ // this snippet first:
+ //
+ // ec.EmitScopeInitFromBlock (The_Block);
+ // The_Block.Emit (ec);
+ //
+
+ Console.WriteLine (
+ "The scope_instance has not been emitted, this typically means\n" +
+ "that inside the compiler someone is calling Block.Emit without\n" +
+ "first calling EmitScopeInitFromBlock for the block. See compiler" +
+ "source code for an explanation");
+ throw new Exception ("Internal compiler error");
+
+ }
+ ig.Emit (OpCodes.Ldloc, scope_instance);
+ }
+ }
+
+ public static void CheckCycles (string msg, ScopeInfo s)
+ {
+ ArrayList l = new ArrayList ();
+ int n = 0;
+
+ for (ScopeInfo p = s; p != null; p = p.ParentScope,n++){
+ if (l.Contains (p)){
+ Console.WriteLine ("Loop detected {0} in {1}", n, msg);
+ throw new Exception ();
+ }
+ l.Add (p);
+ }
+ }
+
static void DoPath (StringBuilder sb, ScopeInfo start)
{
+ CheckCycles ("print", start);
+
if (start.ParentScope != null){
DoPath (sb, start.ParentScope);
sb.Append (", ");
// Points to the toplevel block that owns this CaptureContext
//
ToplevelBlock toplevel_owner;
+
+ //
+ // All the scopes we capture
+ //
Hashtable scopes = new Hashtable ();
+
+ //
+ // All the root scopes
+ //
+ ArrayList roots = new ArrayList ();
+
bool have_captured_vars = false;
- ScopeInfo topmost = null;
+ bool referenced_this = false;
//
// Captured fields
Hashtable captured_fields = new Hashtable ();
Hashtable captured_variables = new Hashtable ();
public Hashtable captured_parameters = new Hashtable ();
- public AnonymousMethod Host;
+ public AnonymousContainer Host;
- public CaptureContext (ToplevelBlock toplevel_owner, Location loc, AnonymousMethod host)
+ public CaptureContext (ToplevelBlock toplevel_owner, Location loc,
+ AnonymousContainer host)
{
cc_id = count++;
this.toplevel_owner = toplevel_owner;
DoPath (sb, cc.ParentCaptureContext);
sb.Append (".");
}
- sb.Append (cc_id.ToString ());
+ sb.Append (cc.cc_id.ToString ());
+ }
+
+ public void ReParent (ToplevelBlock new_toplevel, AnonymousContainer new_host)
+ {
+ toplevel_owner = new_toplevel;
+ Host = new_host;
+
+ for (CaptureContext cc = ParentCaptureContext; cc != null;
+ cc = cc.ParentCaptureContext) {
+ cc.Host = new_host;
+ }
}
public override string ToString ()
}
}
- // Returns the deepest of two scopes
- public ScopeInfo Deepest (ScopeInfo a, ScopeInfo b)
+ ScopeInfo GetScopeForBlock (Block block)
{
- ScopeInfo p;
-
- if (a == null)
- return b;
- if (b == null)
- return a;
- if (a == b)
- return a;
-
- //
- // If they Scopes are on the same CaptureContext, we do the double
- // checks just so if there is an invariant change in the future,
- // we get the exception at the end
- //
- for (p = a; p != null; p = p.ParentScope)
- if (p == b)
- return a;
-
- for (p = b; p != null; p = p.ParentScope)
- if (p == a)
- return b;
-
- CaptureContext ca = a.CaptureContext;
- CaptureContext cb = b.CaptureContext;
-
- for (CaptureContext c = ca; c != null; c = c.ParentCaptureContext)
- if (c == cb)
- return a;
-
- for (CaptureContext c = cb; c != null; c = c.ParentCaptureContext)
- if (c == ca)
- return b;
- throw new Exception ("Should never be reached");
- }
-
- void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
- {
- am.Scope = Deepest (am.Scope, scope);
- }
-
- void LinkScope (ScopeInfo scope, int id)
- {
- ScopeInfo parent = (ScopeInfo) scopes [id];
- scope.ParentScope = parent;
- parent.AddChild (scope);
-
- if (scope == topmost)
- topmost = parent;
+ ScopeInfo si = (ScopeInfo) scopes [block.ID];
+ if (si != null)
+ return si;
+ si = new ScopeInfo (this, block);
+ scopes [block.ID] = si;
+ return si;
}
- public void AddLocal (AnonymousMethod am, LocalInfo li)
+ public void AddLocal (AnonymousContainer am, LocalInfo li)
{
if (li.Block.Toplevel != toplevel_owner){
ParentCaptureContext.AddLocal (am, li);
return;
}
- int block_id = li.Block.ID;
- ScopeInfo scope;
- if (scopes [block_id] == null){
- scope = new ScopeInfo (this, li.Block);
- scopes [block_id] = scope;
- } else
- scope = (ScopeInfo) scopes [block_id];
-
- if (topmost == null){
- topmost = scope;
- } else {
- // Link to parent
- for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
- if (scopes [b.ID] != null){
- LinkScope (scope, b.ID);
- break;
- }
- }
-
- if (scope.ParentScope == null && ParentCaptureContext != null){
- CaptureContext pcc = ParentCaptureContext;
-
- for (Block b = am.ContainingBlock; b != null; b = b.Parent){
- if (pcc.scopes [b.ID] != null){
- pcc.LinkScope (scope, b.ID);
- break;
- }
- }
- }
- }
+ ScopeInfo scope = GetScopeForBlock (li.Block);
//
// Adjust the owner
//
if (Host != null)
- AdjustMethodScope (Host, topmost);
+ Host.RegisterScope (scope);
//
// Adjust the user
//
- AdjustMethodScope (am, scope);
+ am.RegisterScope (scope);
if (captured_variables [li] != null)
return;
//
// Records the captured parameter at the appropriate CaptureContext
//
- public void AddParameter (EmitContext ec, AnonymousMethod am, string name, Type t, int idx)
+ public void AddParameter (EmitContext ec, AnonymousContainer am,
+ string name, Type t, int idx)
{
CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
//
// Records the parameters in the context
//
- void AddParameterToContext (AnonymousMethod am, string name, Type t, int idx)
+ public void AddParameterToContext (AnonymousContainer am, string name, Type t, int idx)
{
if (captured_parameters == null)
captured_parameters = new Hashtable ();
- if (captured_parameters [name] != null)
- return;
- captured_parameters [name] = new CapturedParameter (t, idx);
+ if (captured_parameters [name] == null)
+ captured_parameters [name] = new CapturedParameter (t, idx);
- if (topmost == null){
- //
- // Create one ScopeInfo, if there are none.
- //
- topmost = new ScopeInfo (this, toplevel_owner);
- scopes [toplevel_owner.ID] = topmost;
- } else {
- //
- // If the topmost ScopeInfo is not at the topblock level, insert
- // a new ScopeInfo there.
- //
- if (topmost.ScopeBlock != toplevel_owner){
- ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
- scopes [toplevel_owner.ID] = topmost;
- topmost.ParentScope = par_si;
- topmost = par_si;
- }
- }
-
- topmost.HostsParameters = true;
- AdjustMethodScope (am, topmost);
+ ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+ scope.HostsParameters = true;
+ am.RegisterScope (scope);
}
//
// Captured fields are only recorded on the topmost CaptureContext, because that
// one is the one linked to the owner of instance fields
//
- public void AddField (FieldExpr fe)
+ public void AddField (EmitContext ec, AnonymousContainer am, FieldExpr fe)
{
if (fe.FieldInfo.IsStatic)
throw new Exception ("Attempt to register a static field as a captured field");
-
CaptureContext parent = ParentCaptureContext;
- if (parent != null)
- parent.AddField (fe);
- else
- captured_fields [fe] = fe;
+ if (parent != null) {
+ parent.AddField (ec, am, fe);
+ return;
+ }
+
+ ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+ am.RegisterScope (scope);
+ }
+
+ public void CaptureThis (AnonymousContainer am)
+ {
+ if (am == null)
+ throw new Exception ("Internal Compiler error: Capturethis called with a null method");
+ CaptureContext parent = ParentCaptureContext;
+ if (parent != null) {
+ parent.CaptureThis (am);
+ return;
+ }
+ referenced_this = true;
+
+ ScopeInfo scope = GetScopeForBlock (toplevel_owner);
+ am.RegisterScope (scope);
}
-
public bool HaveCapturedVariables {
get {
public void EmitAnonymousHelperClasses (EmitContext ec)
{
- if (topmost != null){
- topmost.NeedThis = HaveCapturedFields;
- topmost.EmitScopeType (ec);
+ if (roots.Count != 0){
+ foreach (ScopeInfo root in roots){
+ //
+ // FIXME: We really should do this in a per-ScopeInfo
+ // basis, instead of having the NeedThis applied to
+ // all of the roots.
+ //
+ root.NeedThis = HaveCapturedFields || referenced_this;
+
+ root.EmitScopeType (ec);
+ }
}
}
public void CloseAnonymousHelperClasses ()
{
- if (topmost != null)
- topmost.CloseTypes ();
+ if (roots.Count != 0)
+ foreach (ScopeInfo root in roots)
+ root.CloseTypes ();
}
- ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
+ public void EmitInitScope (EmitContext ec)
{
- ScopeInfo si;
-
- si = (ScopeInfo) scopes [b.ID];
+ EmitAnonymousHelperClasses (ec);
+ if (roots.Count != 0)
+ foreach (ScopeInfo root in roots)
+ root.EmitInitScope (ec); }
+
+ //
+ // This is called externally when we start emitting code for a block
+ // if the block has a ScopeInfo associated, emit the init code
+ //
+ public void EmitScopeInitFromBlock (EmitContext ec, Block b)
+ {
+ ScopeInfo si = (ScopeInfo) scopes [b.ID];
if (si == null)
- throw new Exception ("Si is null for block " + b.ID);
- si.EmitInitScope (ec);
+ return;
- return si;
+ si.EmitInitScope (ec);
}
-
+
//
// Emits the opcodes necessary to load the instance of the captured
// variable in `li'
//
- public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li, AnonymousMethod am)
+ public void EmitCapturedVariableInstance (EmitContext ec, LocalInfo li,
+ AnonymousContainer am)
{
ILGenerator ig = ec.ig;
ScopeInfo si;
-
+
if (li.Block.Toplevel == toplevel_owner){
- si = GetScopeFromBlock (ec, li.Block);
- ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ si = (ScopeInfo) scopes [li.Block.ID];
+ si.EmitScopeInstance (ig);
return;
}
si = am.Scope;
ig.Emit (OpCodes.Ldarg_0);
if (si != null){
+ if (am.IsIterator && (si.ScopeBlock.Toplevel == li.Block.Toplevel)) {
+ return;
+ }
+
while (si.ScopeBlock.ID != li.Block.ID){
if (si.ParentLink != null)
ig.Emit (OpCodes.Ldfld, si.ParentLink);
si = si.ParentScope;
- if (si == null)
+ if (si == null) {
+ si = am.Scope;
+ Console.WriteLine ("Target: {0} {1}", li.Block.ID, li.Name);
+ while (si.ScopeBlock.ID != li.Block.ID){
+ Console.WriteLine ("Trying: {0}", si.ScopeBlock.ID);
+ si = si.ParentScope;
+ }
+
throw new Exception (
String.Format ("Never found block {0} starting at {1} while looking up {2}",
li.Block.ID, am.Scope.ScopeBlock.ID, li.Name));
+ }
}
}
}
cc.EmitParameterInstance (ec, name);
return;
}
-
+
CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
if (par_info != null){
//
ILGenerator ig = ec.ig;
ScopeInfo si;
- if (ec.CurrentBlock == toplevel_owner){
- si = GetScopeFromBlock (ec, toplevel_owner);
- ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
- return;
+
+ if (ec.CurrentBlock.Toplevel == toplevel_owner) {
+ si = (ScopeInfo) scopes [toplevel_owner.ID];
+ si.EmitScopeInstance (ig);
+ } else {
+ si = ec.CurrentAnonymousMethod.Scope;
+ ig.Emit (OpCodes.Ldarg_0);
}
-
- si = ec.CurrentAnonymousMethod.Scope;
- ig.Emit (OpCodes.Ldarg_0);
+
if (si != null){
while (si.ParentLink != null) {
ig.Emit (OpCodes.Ldfld, si.ParentLink);
// Emits the code necessary to load the parameter named `name' within
// an anonymous method.
//
- public void EmitParameter (EmitContext ec, string name)
+ public void EmitParameter (EmitContext ec, string name, bool leave_copy, bool prepared, ref LocalTemporary temp)
{
CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
if (cc != this){
- cc.EmitParameter (ec, name);
+ cc.EmitParameter (ec, name, leave_copy, prepared, ref temp);
return;
}
- EmitParameterInstance (ec, name);
+ if (!prepared)
+ EmitParameterInstance (ec, name);
CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
if (par_info != null){
//
//
}
ec.ig.Emit (OpCodes.Ldfld, par_info.FieldBuilder);
+
+ if (leave_copy){
+ ec.ig.Emit (OpCodes.Dup);
+ temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
+ temp.Store (ec);
+ }
}
//
// Implements the assignment of `source' to the paramenter named `name' within
// an anonymous method.
//
- public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load)
+ public void EmitAssignParameter (EmitContext ec, string name, Expression source, bool leave_copy, bool prepare_for_load, ref LocalTemporary temp)
{
CaptureContext cc = ContextForParameter (ec.CurrentBlock.Toplevel, name);
if (cc != this){
- cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load);
+ cc.EmitAssignParameter (ec, name, source, leave_copy, prepare_for_load, ref temp);
return;
}
ILGenerator ig = ec.ig;
CapturedParameter par_info = (CapturedParameter) captured_parameters [name];
EmitParameterInstance (ec, name);
+ if (prepare_for_load)
+ ig.Emit (OpCodes.Dup);
source.Emit (ec);
- if (leave_copy)
+ if (leave_copy){
ig.Emit (OpCodes.Dup);
+ temp = new LocalTemporary (par_info.FieldBuilder.FieldType);
+ temp.Store (ec);
+ }
ig.Emit (OpCodes.Stfld, par_info.FieldBuilder);
+ if (temp != null)
+ temp.Emit (ec);
}
//
// The following methods are only invoked on the host for the
// anonymous method.
//
- public void EmitMethodHostInstance (EmitContext target, AnonymousMethod am)
+ public void EmitMethodHostInstance (EmitContext target, AnonymousContainer am)
{
ILGenerator ig = target.ig;
ScopeInfo si = am.Scope;
-
- if (si == null){
+
+ AnonymousContainer container = am.ContainerAnonymousMethod;
+
+ if ((si == null) || ((container != null) && (si == container.Scope))) {
ig.Emit (OpCodes.Ldarg_0);
return;
}
si.EmitInitScope (target);
- ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ si.EmitScopeInstance (ig);
}
- ArrayList all_scopes = new ArrayList ();
-
- public void AddScope (ScopeInfo si)
+ public void RegisterCaptureContext ()
{
- all_scopes.Add (si);
toplevel_owner.RegisterCaptureContext (this);
}
//
- // Links any scopes that were not linked previously
+ // Returs true if `probe' is an ancestor of `scope' in the
+ // scope chain
//
- public void AdjustScopes ()
+ bool IsAncestor (ScopeInfo probe, ScopeInfo scope)
{
- foreach (ScopeInfo scope in all_scopes){
- if (scope.ParentScope != null)
- continue;
+ for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
+ if (probe.ScopeBlock == b)
+ return true;
+ }
+ return false;
+ }
- for (Block b = scope.ScopeBlock.Parent; b != null; b = b.Parent){
- if (scopes [b.ID] != null){
- LinkScope (scope, b.ID);
- break;
+ //
+ // Returns an ArrayList of ScopeInfos that enumerates all the ancestors
+ // of `scope' found in `scope_list'.
+ //
+ // The value returned is either a ScopeInfo or an Arraylist of ScopeInfos
+ //
+ object GetAncestorScopes (ScopeInfo scope, ScopeInfo [] scope_list)
+ {
+ object ancestors = null;
+
+ for (int i = 0; i < scope_list.Length; i++){
+ // Ignore the same scope
+ if (scope_list [i] == scope)
+ continue;
+
+ if (IsAncestor (scope_list [i], scope)){
+ if (ancestors == null){
+ ancestors = scope_list [i];
+ continue;
}
+
+ if (ancestors is ScopeInfo){
+ object old = ancestors;
+ ancestors = new ArrayList (4);
+ ((ArrayList)ancestors).Add (old);
+ }
+
+ ((ArrayList)ancestors).Add (scope_list [i]);
}
+ }
+ return ancestors;
+ }
+
+ //
+ // Returns the immediate parent of `scope' from all the captured
+ // scopes found in `scope_list', or null if this is a toplevel scope.
+ //
+ ScopeInfo GetParentScope (ScopeInfo scope, ScopeInfo [] scope_list)
+ {
+ object ancestors = GetAncestorScopes (scope, scope_list);
+ if (ancestors == null)
+ return null;
- if (scope.ParentScope == null && ParentCaptureContext != null){
- CaptureContext pcc = ParentCaptureContext;
+ // Single match, thats the parent.
+ if (ancestors is ScopeInfo)
+ return (ScopeInfo) ancestors;
+
+ ArrayList candidates = (ArrayList) ancestors;
+ ScopeInfo parent = (ScopeInfo) candidates [0];
+ for (int i = 1; i < candidates.Count; i++){
+ if (IsAncestor (parent, (ScopeInfo) candidates [i]))
+ parent = (ScopeInfo) candidates [i];
+ }
+ return parent;
+ }
+
+ //
+ // Links all the scopes
+ //
+ bool linked;
+ public void LinkScopes ()
+ {
+ if (linked)
+ return;
+
+ linked = true;
+ if (ParentCaptureContext != null)
+ ParentCaptureContext.LinkScopes ();
+
+ int scope_count = scopes.Keys.Count;
+ ScopeInfo [] scope_list = new ScopeInfo [scope_count];
+ scopes.Values.CopyTo (scope_list, 0);
+
+ for (int i = 0; i < scope_count; i++){
+ ScopeInfo parent = GetParentScope (scope_list [i], scope_list);
+
+ if (parent == null){
+ roots.Add (scope_list [i]);
+ continue;
+ }
+
+ scope_list [i].ParentScope = parent;
+ parent.AddChild (scope_list [i]);
+ }
+
+ //
+ // Link the roots to their parent containers if any.
+ //
+ if (ParentCaptureContext != null && roots.Count != 0){
+ ScopeInfo one_root = (ScopeInfo) roots [0];
+ bool found = false;
+
+ foreach (ScopeInfo a_parent_root in ParentCaptureContext.roots){
+ if (!IsAncestor (a_parent_root, one_root))
+ continue;
+
+ found = true;
- for (Block b = Host.ContainingBlock; b != null; b = b.Parent){
- if (pcc.scopes [b.ID] != null){
- pcc.LinkScope (scope, b.ID);
- break;
- }
+ // Found, link all the roots to this root
+ foreach (ScopeInfo root in roots){
+ root.ParentScope = a_parent_root;
+ a_parent_root.AddChild (root);
}
+ break;
+ }
+ if (!found){
+ //
+ // This is to catch a condition in which it is
+ // not possible to determine the containing ScopeInfo
+ // from an encapsulating CaptureContext
+ //
+ throw new Exception ("Internal compiler error: Did not find the parent for the root in the chain");
}
}
}