Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

Enhancements:
- Support for covariant method returns (@stakx, #619)
- 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)

Bugfixes:
- DynamicProxy emits invalid metadata for redeclared event (@stakx, #590)
Expand Down
6 changes: 6 additions & 0 deletions ref/Castle.Core-net462.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,12 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
protected abstract override void InvokeMethodOnTarget() { }
}
[System.Serializable]
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
{
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
protected override void InvokeMethodOnTarget() { }
}
[System.Serializable]
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
{
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
Expand Down
5 changes: 5 additions & 0 deletions ref/Castle.Core-net6.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2742,6 +2742,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
public override System.Type TargetType { get; }
protected abstract override void InvokeMethodOnTarget() { }
}
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
{
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
protected override void InvokeMethodOnTarget() { }
}
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
{
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
Expand Down
5 changes: 5 additions & 0 deletions ref/Castle.Core-netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2740,6 +2740,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
public override System.Type TargetType { get; }
protected abstract override void InvokeMethodOnTarget() { }
}
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
{
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
protected override void InvokeMethodOnTarget() { }
}
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
{
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
Expand Down
5 changes: 5 additions & 0 deletions ref/Castle.Core-netstandard2.1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2740,6 +2740,11 @@ protected InheritanceInvocation(System.Type targetType, object proxy, Castle.Dyn
public override System.Type TargetType { get; }
protected abstract override void InvokeMethodOnTarget() { }
}
public sealed class InheritanceInvocationWithoutTarget : Castle.DynamicProxy.Internal.InheritanceInvocation
{
public InheritanceInvocationWithoutTarget(System.Type targetType, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
protected override void InvokeMethodOnTarget() { }
}
public sealed class InterfaceMethodWithoutTargetInvocation : Castle.DynamicProxy.AbstractInvocation
{
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, Castle.DynamicProxy.IInterceptor[] interceptors, System.Reflection.MethodInfo proxiedMethod, object[] arguments) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,94 @@ public void Generic_method_of_interface_proxy_without_target__uses__InterfaceMet
Assert.AreEqual(typeof(InterfaceMethodWithoutTargetInvocation), recorder.InvocationType);
}

[Test]
public void Non_generic_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithNonGenericAbstractMethod>(recorder);
proxy.Method();

Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Non_generic_protected_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithNonGenericProtectedAbstractMethod>(recorder);
proxy.InvokeMethod();

Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Non_generic_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithNonGenericVirtualMethod>(recorder);
proxy.Method();

Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Non_generic_protected_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithNonGenericProtectedVirtualMethod>(recorder);
proxy.InvokeMethod();

Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Generic_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithGenericAbstractMethod>(recorder);
proxy.Method(42);

Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Generic_protected_abstract_method_of_class_proxy__uses__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithGenericProtectedAbstractMethod>(recorder);
proxy.InvokeMethod(42);

Assert.AreEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Generic_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithGenericVirtualMethod>(recorder);
proxy.Method(42);

Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

[Test]
public void Generic_protected_virtual_method_of_class_proxy__does_not_use__InheritanceInvocationWithoutTarget()
{
var recorder = new InvocationTypeRecorder();

var proxy = generator.CreateClassProxy<WithGenericProtectedVirtualMethod>(recorder);
proxy.InvokeMethod(42);

Assert.AreNotEqual(typeof(InheritanceInvocationWithoutTarget), recorder.InvocationType);
}

public interface IWithNonGenericMethod
{
void Method();
Expand All @@ -62,6 +150,50 @@ public interface IWithGenericMethod
void Method<T>(T arg);
}

public abstract class WithNonGenericAbstractMethod
{
public abstract void Method();
}

public abstract class WithNonGenericProtectedAbstractMethod
{
protected abstract void Method();
public void InvokeMethod() => Method();
}

public class WithNonGenericVirtualMethod
{
public virtual void Method() { }
}

public class WithNonGenericProtectedVirtualMethod
{
protected virtual void Method() { }
public void InvokeMethod() => Method();
}

public abstract class WithGenericAbstractMethod
{
public abstract void Method<T>(T arg);
}

public abstract class WithGenericProtectedAbstractMethod
{
protected abstract void Method<T>(T arg);
public void InvokeMethod<T>(T arg) => Method(arg);
}

public class WithGenericVirtualMethod
{
public virtual void Method<T>(T arg) { }
}

public class WithGenericProtectedVirtualMethod
{
protected virtual void Method<T>(T arg) { }
public void InvokeMethod<T>(T arg) => Method(arg);
}

private sealed class InvocationTypeRecorder : IInterceptor
{
public Type InvocationType { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,13 @@ private Type GetDelegateType(MetaMethod method, ClassEmitter @class)

private Type GetInvocationType(MetaMethod method, ClassEmitter @class)
{
if (!method.HasTarget)
{
// We do not need to generate a custom invocation type because no custom implementation
// for `InvokeMethodOnTarget` will be needed (proceeding to target isn't possible here):
return typeof(InheritanceInvocationWithoutTarget);
}

// NOTE: No caching since invocation is tied to this specific proxy type via its invocation method
return BuildInvocationType(method, @class);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2004-2022 Castle Project - http://www.castleproject.org/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Castle.DynamicProxy.Internal
{
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;

#if FEATURE_SERIALIZATION
[Serializable]
#endif
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class InheritanceInvocationWithoutTarget : InheritanceInvocation
{
public InheritanceInvocationWithoutTarget(Type targetType, object proxy, IInterceptor[] interceptors, MethodInfo proxiedMethod, object[] arguments)
: base(targetType, proxy, interceptors, proxiedMethod, arguments)
{
Debug.Assert(proxiedMethod.IsAbstract, $"{nameof(InheritanceInvocationWithoutTarget)} does not support non-abstract methods.");
}

protected override void InvokeMethodOnTarget() => ThrowOnNoTarget();
}
}