Skip to content

Commit 5aa18ed

Browse files
committed
Use standard invocation type for class proxy methods w/o target
1 parent d0c6e38 commit 5aa18ed

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

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)