Skip to content

Commit a133ee3

Browse files
authored
Merge pull request castleproject#626 from stakx/refactor/inheritance-invocation-without-target
Use standard invocation type for abstract methods of class proxies w/o target
2 parents d0c6e38 + 51894ee commit a133ee3

File tree

8 files changed

+197
-0
lines changed

8 files changed

+197
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
Enhancements:
66
- Support for covariant method returns (@stakx, #619)
7+
- Performance improvement with proxy type generation for class proxies (without target). Abstract class methods now reuse a predefined invocation type (like methods of interface proxies without target; see explanation below at version 5.0.0 enhancements) (@stakx, #626)
78

89
Bugfixes:
910
- DynamicProxy emits invalid metadata for redeclared event (@stakx, #590)

ref/Castle.Core-net462.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2788,6 +2788,12 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
27882788
protected abstract override void InvokeMethodOnTarget() { }
27892789
}
27902790
[System.Serializable]
2791+
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
2792+
{
2793+
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
2794+
protected override void InvokeMethodOnTarget() { }
2795+
}
2796+
[System.Serializable]
27912797
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
27922798
{
27932799
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }

ref/Castle.Core-net6.0.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2742,6 +2742,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
27422742
public override System.Type TargetType { get; }
27432743
protected abstract override void InvokeMethodOnTarget() { }
27442744
}
2745+
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
2746+
{
2747+
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
2748+
protected override void InvokeMethodOnTarget() { }
2749+
}
27452750
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
27462751
{
27472752
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }

ref/Castle.Core-netstandard2.0.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,6 +2740,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
27402740
public override System.Type TargetType { get; }
27412741
protected abstract override void InvokeMethodOnTarget() { }
27422742
}
2743+
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
2744+
{
2745+
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
2746+
protected override void InvokeMethodOnTarget() { }
2747+
}
27432748
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
27442749
{
27452750
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }

ref/Castle.Core-netstandard2.1.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2740,6 +2740,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
27402740
public override System.Type TargetType { get; }
27412741
protected abstract override void InvokeMethodOnTarget() { }
27422742
}
2743+
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
2744+
{
2745+
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
2746+
protected override void InvokeMethodOnTarget() { }
2747+
}
27432748
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
27442749
{
27452750
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }

src/Castle.Core.Tests/DynamicProxy.Tests/InvocationTypeReuseTestCase.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,94 @@ public void Generic_method_of_interface_proxy_without_target__uses__InterfaceMet
5252
Assert.AreEqual(typeof(InterfaceMethodWithoutTargetInvocation), recorder.InvocationType);
5353
}
5454

55+
[Test]
56+
public void Non_generic_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
57+
{
58+
var recorder = new InvocationTypeRecorder();
59+
60+
var proxy = generator.CreateClassProxy<WithNonGenericAbstractMethod>(recorder);
61+
proxy.Method();
62+
63+
Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
64+
}
65+
66+
[Test]
67+
public void Non_generic_protected_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
68+
{
69+
var recorder = new InvocationTypeRecorder();
70+
71+
var proxy = generator.CreateClassProxy<WithNonGenericProtectedAbstractMethod>(recorder);
72+
proxy.InvokeMethod();
73+
74+
Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
75+
}
76+
77+
[Test]
78+
public void Non_generic_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
79+
{
80+
var recorder = new InvocationTypeRecorder();
81+
82+
var proxy = generator.CreateClassProxy<WithNonGenericVirtualMethod>(recorder);
83+
proxy.Method();
84+
85+
Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
86+
}
87+
88+
[Test]
89+
public void Non_generic_protected_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
90+
{
91+
var recorder = new InvocationTypeRecorder();
92+
93+
var proxy = generator.CreateClassProxy<WithNonGenericProtectedVirtualMethod>(recorder);
94+
proxy.InvokeMethod();
95+
96+
Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
97+
}
98+
99+
[Test]
100+
public void Generic_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
101+
{
102+
var recorder = new InvocationTypeRecorder();
103+
104+
var proxy = generator.CreateClassProxy<WithGenericAbstractMethod>(recorder);
105+
proxy.Method(42);
106+
107+
Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
108+
}
109+
110+
[Test]
111+
public void Generic_protected_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
112+
{
113+
var recorder = new InvocationTypeRecorder();
114+
115+
var proxy = generator.CreateClassProxy<WithGenericProtectedAbstractMethod>(recorder);
116+
proxy.InvokeMethod(42);
117+
118+
Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
119+
}
120+
121+
[Test]
122+
public void Generic_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
123+
{
124+
var recorder = new InvocationTypeRecorder();
125+
126+
var proxy = generator.CreateClassProxy<WithGenericVirtualMethod>(recorder);
127+
proxy.Method(42);
128+
129+
Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
130+
}
131+
132+
[Test]
133+
public void Generic_protected_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
134+
{
135+
var recorder = new InvocationTypeRecorder();
136+
137+
var proxy = generator.CreateClassProxy<WithGenericProtectedVirtualMethod>(recorder);
138+
proxy.InvokeMethod(42);
139+
140+
Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
141+
}
142+
55143
public interface IWithNonGenericMethod
56144
{
57145
void Method();
@@ -62,6 +150,50 @@ public interface IWithGenericMethod
62150
void Method<T>(T arg);
63151
}
64152

153+
public abstract class WithNonGenericAbstractMethod
154+
{
155+
public abstract void Method();
156+
}
157+
158+
public abstract class WithNonGenericProtectedAbstractMethod
159+
{
160+
protected abstract void Method();
161+
public void InvokeMethod() => Method();
162+
}
163+
164+
public class WithNonGenericVirtualMethod
165+
{
166+
public virtual void Method() { }
167+
}
168+
169+
public class WithNonGenericProtectedVirtualMethod
170+
{
171+
protected virtual void Method() { }
172+
public void InvokeMethod() => Method();
173+
}
174+
175+
public abstract class WithGenericAbstractMethod
176+
{
177+
public abstract void Method<T>(T arg);
178+
}
179+
180+
public abstract class WithGenericProtectedAbstractMethod
181+
{
182+
protected abstract void Method<T>(T arg);
183+
public void InvokeMethod<T>(T arg) => Method(arg);
184+
}
185+
186+
public class WithGenericVirtualMethod
187+
{
188+
public virtual void Method<T>(T arg) { }
189+
}
190+
191+
public class WithGenericProtectedVirtualMethod
192+
{
193+
protected virtual void Method<T>(T arg) { }
194+
public void InvokeMethod<T>(T arg) => Method(arg);
195+
}
196+
65197
private sealed class InvocationTypeRecorder : IInterceptor
66198
{
67199
public Type InvocationType { get; private set; }

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class)
172172

173173
private Type GetInvocationType(MetaMethod method, ClassEmitter @class)
174174
{
175+
if (!method.HasTarget)
176+
{
177+
// We do not need to generate a custom invocation type because no custom implementation
178+
// for `InvokeMethodOnTarget` will be needed (proceeding to target isn't possible here):
179+
return typeof(InheritanceInvocationWithoutTarget);
180+
}
181+
175182
// NOTE: No caching since invocation is tied to this specific proxy type via its invocation method
176183
return BuildInvocationType(method, @class);
177184
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2004-2022 Castle Project - http://www.castleproject.org/
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace Castle.DynamicProxy.Internal
16+
{
17+
using System;
18+
using System.ComponentModel;
19+
using System.Diagnostics;
20+
using System.Reflection;
21+
22+
#if FEATURE_SERIALIZATION
23+
[Serializable]
24+
#endif
25+
[EditorBrowsable(EditorBrowsableState.Never)]
26+
public sealed class InheritanceInvocationWithoutTarget : InheritanceInvocation
27+
{
28+
public InheritanceInvocationWithoutTarget(Type targetType, object proxy, IInterceptor[] interceptors, MethodInfo proxiedMethod, object[] arguments)
29+
: base(targetType, proxy, interceptors, proxiedMethod, arguments)
30+
{
31+
Debug.Assert(proxiedMethod.IsAbstract, $"{nameof(InheritanceInvocationWithoutTarget)} does not support non-abstract methods.");
32+
}
33+
34+
protected override void InvokeMethodOnTarget() => ThrowOnNoTarget();
35+
}
36+
}

0 commit comments

Comments
 (0)