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 modifiers applied to the method, we aggregate them
//
- int method_modifiers = Modifiers.PRIVATE;
+ protected int method_modifiers = Modifiers.PRIVATE;
//
// During the resolve stage of the anonymous method body,
//
// 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;
block.SetHaveAnonymousMethods (l, this);
}
+ protected AnonymousContainer (Parameters parameters, ToplevelBlock container,
+ Location l)
+ {
+ Parameters = parameters;
+ Block = new ToplevelBlock (container, Parameters, l);
+ loc = l;
+
+ //
+ // The order is important: this setups the CaptureContext tree hierarchy.
+ //
+ container.SetHaveAnonymousMethods (loc, this);
+ Block.SetHaveAnonymousMethods (loc, this);
+ }
+
public override Expression DoResolve (EmitContext ec)
{
//
return this;
}
+ protected abstract bool CreateMethodHost (EmitContext ec);
+
+ public abstract void CreateScopeType (EmitContext ec, ScopeInfo scope);
+
+ public abstract bool IsIterator {
+ get;
+ }
+ }
+
+ public class AnonymousMethod : AnonymousContainer
+ {
+ public AnonymousMethod (Parameters parameters, ToplevelBlock container,
+ ToplevelBlock block, Location l)
+ : base (parameters, container, block, l)
+ {
+ }
+
+ 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)
{
//
// Crude hack follows: we replace the TypeBuilder during the
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, Scope is {0}", Scope == null ? "null" : "Not null"));
if (current_type == type_host && ec.IsStatic){
if (ec.IsStatic){
method = new Method (
(TypeContainer) ec.TypeContainer,
- new TypeExpression (return_type, loc),
+ new TypeExpression (invoke_mb.ReturnType, loc),
method_modifiers, false, new MemberName ("<#AnonymousMethod>" + anonymous_method_count++),
Parameters, null, loc);
method.Block = Block;
continue;
fixedpars [j] = new Parameter (
new TypeExpression (invoke_pd.ParameterType (i), loc),
- "+" + j, invoke_pd.ParameterModifier (i), null);
+ "+" + j, invoke_pd.ParameterModifier (i), null, loc);
j++;
}
if (params_idx != -1){
variable = new Parameter (
new TypeExpression (invoke_pd.ParameterType (params_idx), loc),
- "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null);
+ "+" + params_idx, invoke_pd.ParameterModifier (params_idx), null, loc);
}
- Parameters = new Parameters (fixedpars, variable, loc);
+ Parameters = new Parameters (fixedpars, variable);
}
//
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);
+ Report.Error (1593, loc, "Delegate `{0}' does not take `{1}' arguments",
+ TypeManager.CSharpName (delegate_type), amp.Count);
Error_ParameterMismatch (delegate_type);
}
return null;
for (int i = 0; i < amp.Count; i++){
Parameter.Modifier amp_mod = amp.ParameterModifier (i);
- if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
- if (!probe){
- Error_ParameterMismatch (delegate_type);
- Report.Error (1677, loc, "Parameter '{0}' should not be declared with the '{1}' keyword",
+ if (!probe) {
+ if ((amp_mod & (Parameter.Modifier.OUT | Parameter.Modifier.REF)) != 0){
+ Report.Error (1677, loc, "Parameter `{0}' should not be declared with the `{1}' keyword",
i+1, amp.ModifierDesc (i));
+ Error_ParameterMismatch (delegate_type);
+ return null;
}
- return null;
- }
- if (amp_mod != invoke_pd.ParameterModifier (i)){
- if (!probe){
- Report.Error (1676, loc,
- "Signature mismatch in parameter modifier for parameter #0", i + 1);
+ if (amp_mod != invoke_pd.ParameterModifier (i)){
+ Report.Error (1676, loc, "Parameter `{0}' must be declared with the `{1}' keyword",
+ i+1, Parameter.GetModifierSignature (invoke_pd.ParameterModifier (i)));
Error_ParameterMismatch (delegate_type);
+ return null;
}
- 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)));
+ if (amp.ParameterType (i) != invoke_pd.ParameterType (i)){
+ Report.Error (1678, loc, "Parameter `{0}' is declared as type `{1}' but should be `{2}'",
+ i+1,
+ TypeManager.CSharpName (amp.ParameterType (i)),
+ TypeManager.CSharpName (invoke_pd.ParameterType (i)));
Error_ParameterMismatch (delegate_type);
+ return null;
}
- return null;
}
}
ContainerAnonymousMethod = ec.CurrentAnonymousMethod;
ContainingBlock = ec.CurrentBlock;
- if (aec.ResolveTopBlock (ec, Block, amp, loc, out unreachable))
+ if (aec.ResolveTopBlock (ec, Block, amp, null, out unreachable))
return new AnonymousDelegate (this, delegate_type, loc).Resolve (ec);
return null;
{
return method.MethodData.MethodBuilder;
}
+
+ public override string GetSignatureForError ()
+ {
+ string s = TypeManager.CSharpSignature (invoke_mb);
+ return s.Substring (0, s.IndexOf (".Invoke("));
+ }
public bool EmitMethod (EmitContext ec)
{
- if (!CreateMethodHost (ec, invoke_mb.ReturnType))
+ if (!CreateMethodHost (ec))
return false;
MethodBuilder builder = GetMethodBuilder ();
ILGenerator ig = builder.GetILGenerator ();
aec.ig = ig;
- Parameters.LabelParameters (aec, builder, loc);
+ Parameters.LabelParameters (aec, builder);
//
// Adjust based on the computed state of the
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 = TypeManager.NoTypes;
+ Parameters constructor_parameters = Parameters.EmptyReadOnlyParameters;
+ scope.ScopeConstructor = scope.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 (scope.ScopeConstructor, parameter_info, constructor_types);
+
+ 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);
}
}
// 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)
{
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;
return String.Format ("<>AnonHelp<{0}>", id);
}
- public void EmitScopeConstructor ()
- {
- 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);
- }
-
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);
- }
if (HostsParameters){
Hashtable captured_parameters = CaptureContext.captured_parameters;
}
}
- EmitScopeConstructor ();
foreach (ScopeInfo si in children){
si.EmitScopeType (ec);
}
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
+ ig.Emit (OpCodes.Ldloc, scope_instance);
+ }
+
static void DoPath (StringBuilder sb, ScopeInfo start)
{
if (start.ParentScope != null){
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 ()
throw new Exception ("Should never be reached");
}
- void AdjustMethodScope (AnonymousMethod am, ScopeInfo scope)
+ void AdjustMethodScope (AnonymousContainer am, ScopeInfo scope)
{
am.Scope = Deepest (am.Scope, scope);
}
topmost = parent;
}
- public void AddLocal (AnonymousMethod am, LocalInfo li)
+ public void AddLocal (AnonymousContainer am, LocalInfo li)
{
if (li.Block.Toplevel != toplevel_owner){
ParentCaptureContext.AddLocal (am, li);
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);
//
// 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 the topmost ScopeInfo is not at the topblock level, insert
// a new ScopeInfo there.
//
+ // FIXME: This code probably should be evolved to be like the code
+ // in AddLocal
+ //
if (topmost.ScopeBlock != toplevel_owner){
ScopeInfo par_si = new ScopeInfo (this, toplevel_owner);
+ ScopeInfo old_top = topmost;
scopes [toplevel_owner.ID] = topmost;
topmost.ParentScope = par_si;
topmost = par_si;
+ topmost.AddChild (old_top);
}
}
topmost.CloseTypes ();
}
+ public void EmitInitScope (EmitContext ec)
+ {
+ EmitAnonymousHelperClasses (ec);
+ if (topmost != null)
+ topmost.EmitInitScope (ec);
+ }
+
ScopeInfo GetScopeFromBlock (EmitContext ec, Block b)
{
ScopeInfo si;
// 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.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));
+ }
}
}
}
ILGenerator ig = ec.ig;
ScopeInfo si;
- if (ec.CurrentBlock == toplevel_owner){
+
+ if (ec.CurrentBlock.Toplevel == toplevel_owner){
si = GetScopeFromBlock (ec, toplevel_owner);
- ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ si.EmitScopeInstance (ig);
return;
}
-
+
si = ec.CurrentAnonymousMethod.Scope;
ig.Emit (OpCodes.Ldarg_0);
if (si != null){
// 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){
ig.Emit (OpCodes.Ldarg_0);
return;
}
si.EmitInitScope (target);
- ig.Emit (OpCodes.Ldloc, si.ScopeInstance);
+ si.EmitScopeInstance (ig);
}
ArrayList all_scopes = new ArrayList ();