Skip to content

Commit a79078b

Browse files
committed
Fix IInvocation.MethodInvocationTarget for interfaces w/ default impl
1 parent 437b383 commit a79078b

5 files changed

Lines changed: 37 additions & 7 deletions

File tree

src/Castle.Core/DynamicProxy/Contributors/ClassProxyTargetContributor.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,16 @@ protected override MethodGenerator GetMethodGenerator(MetaMethod method, ClassEm
7676
return ExplicitlyImplementedInterfaceMethodGenerator(method, @class, overrideMethod);
7777
}
7878

79+
// since this contributor is used for class proxies without target,
80+
// the invocation type will be derived from `InheritanceInvocation`:
7981
var invocation = GetInvocationType(method, @class);
8082

8183
GetTargetExpressionDelegate getTargetTypeExpression = (c, m) => new TypeTokenExpression(targetType);
8284

85+
// `MethodWithInvocationGenerator` uses its `getTargetExpression` argument
86+
// to determine the first argument to be passed to the received invocation type;
87+
// and since `InheritanceInvocation`s' first ctor param is `targetType`,
88+
// we pass `getTargetTypeExpression` here:
8389
return new MethodWithInvocationGenerator(method,
8490
@class.GetField("__interceptors"),
8591
invocation,

src/Castle.Core/DynamicProxy/Generators/BaseClassProxyGenerator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,11 @@ private IEnumerable<Type> GetTypeImplementerMapping(out IEnumerable<ITypeContrib
153153
// 3. then additional interfaces
154154
if (interfaces.Length > 0)
155155
{
156-
var additionalInterfacesContributor = new InterfaceProxyWithoutTargetContributor(namingScope, (c, m) => NullExpression.Instance) { Logger = Logger };
156+
// this is explained in `InterfaceProxyWithoutTargetGenerator.GetProxyTargetContributor`:
157+
GetTargetExpressionDelegate getTargetType =
158+
(c, m) => m.IsAbstract ? NullExpression.Instance
159+
: new TypeTokenExpression(m.DeclaringType);
160+
var additionalInterfacesContributor = new InterfaceProxyWithoutTargetContributor(namingScope, getTargetType) { Logger = Logger };
157161
contributorsList.Add(additionalInterfacesContributor);
158162

159163
foreach (var @interface in interfaces)

src/Castle.Core/DynamicProxy/Generators/BaseInterfaceProxyGenerator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ protected override Type GenerateType(string typeName, INamingScope namingScope)
145145
protected virtual InterfaceProxyWithoutTargetContributor GetContributorForAdditionalInterfaces(
146146
INamingScope namingScope)
147147
{
148-
return new InterfaceProxyWithoutTargetContributor(namingScope, (c, m) => NullExpression.Instance) { Logger = Logger };
148+
// this is explained in `InterfaceProxyWithoutTargetGenerator.GetProxyTargetContributor`:
149+
GetTargetExpressionDelegate getTargetType =
150+
(c, m) => m.IsAbstract ? NullExpression.Instance
151+
: new TypeTokenExpression(m.DeclaringType);
152+
return new InterfaceProxyWithoutTargetContributor(namingScope, getTargetType) { Logger = Logger };
149153
}
150154

151155
protected virtual IEnumerable<Type> GetTypeImplementerMapping(Type proxyTargetType,

src/Castle.Core/DynamicProxy/Generators/InterfaceProxyWithoutTargetGenerator.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,16 @@ public InterfaceProxyWithoutTargetGenerator(ModuleScope scope, Type targetType,
3535

3636
protected override CompositeTypeContributor GetProxyTargetContributor(Type proxyTargetType, INamingScope namingScope)
3737
{
38-
return new InterfaceProxyWithoutTargetContributor(namingScope, (c, m) => NullExpression.Instance) { Logger = Logger };
38+
// The type of contributor instantiated below will use an inheritance-based invocation type.
39+
// Those expect a target type, not a target instance (see first ctor param of `InheritanceInvocation`).
40+
// For interface proxies without target, there typically isn't a target at all...
41+
// except when an interface method has a default implementation (i.e. isn't abstract).
42+
// Then the target type is the method's declaring interface itself.
43+
// (Similar scenario: class proxies w/o target inherit method impls from the proxied class.)
44+
GetTargetExpressionDelegate getTargetType =
45+
(c, m) => m.IsAbstract ? NullExpression.Instance
46+
: new TypeTokenExpression(m.DeclaringType);
47+
return new InterfaceProxyWithoutTargetContributor(namingScope, getTargetType) { Logger = Logger };
3948
}
4049

4150
protected override ProxyTargetAccessorContributor GetProxyTargetAccessorContributor()

src/Castle.Core/DynamicProxy/Internal/InvocationHelper.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,17 @@ private static MethodInfo ObtainMethod(MethodInfo proxiedMethod, Type type)
6565
MethodInfo methodOnTarget = null;
6666
if (declaringType.IsInterface)
6767
{
68-
var mapping = type.GetInterfaceMap(declaringType);
69-
var index = Array.IndexOf(mapping.InterfaceMethods, proxiedMethod);
70-
Debug.Assert(index != -1);
71-
methodOnTarget = mapping.TargetMethods[index];
68+
if (proxiedMethod.IsAbstract)
69+
{
70+
var mapping = type.GetInterfaceMap(declaringType);
71+
var index = Array.IndexOf(mapping.InterfaceMethods, proxiedMethod);
72+
Debug.Assert(index != -1);
73+
methodOnTarget = mapping.TargetMethods[index];
74+
}
75+
else
76+
{
77+
methodOnTarget = proxiedMethod;
78+
}
7279
}
7380
else
7481
{

0 commit comments

Comments
 (0)