-
Notifications
You must be signed in to change notification settings - Fork 470
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #573 from stakx/interface-proxy-type-generation-sp…
…eedup Speed up interface proxy w/o target type generation significantly
- Loading branch information
Showing
8 changed files
with
183 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/Castle.Core.Tests/DynamicProxy.Tests/InvocationTypeReuseTestCase.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright 2004-2021 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.Tests | ||
{ | ||
using System; | ||
|
||
using Castle.DynamicProxy.Internal; | ||
|
||
using NUnit.Framework; | ||
|
||
/// <summary> | ||
/// This fixture checks which <see cref="IInvocation"/> types get used for proxied methods. | ||
/// Usually, DynamicProxy generates a separate implementation type per proxied method, but | ||
/// in some cases, it can reuse predefined implementation types. Because this is beneficial | ||
/// for runtime performance (as it reduces the amount of dynamic type generation performed), | ||
/// we want to ensure that those predefined types do in fact get picked when they should be. | ||
/// </summary> | ||
[TestFixture] | ||
public class InvocationTypeReuseTestCase : BasePEVerifyTestCase | ||
{ | ||
[Test] | ||
public void Non_generic_method_of_interface_proxy_without_target__uses__InterfaceMethodWithoutTargetInvocation() | ||
{ | ||
var recorder = new InvocationTypeRecorder(); | ||
|
||
var proxy = generator.CreateInterfaceProxyWithoutTarget<IWithNonGenericMethod>(recorder); | ||
proxy.Method(); | ||
|
||
Assert.AreEqual(typeof(InterfaceMethodWithoutTargetInvocation), recorder.InvocationType); | ||
} | ||
|
||
[Test] | ||
public void Generic_method_of_interface_proxy_without_target__uses__InterfaceMethodWithoutTargetInvocation() | ||
{ | ||
var recorder = new InvocationTypeRecorder(); | ||
|
||
var proxy = generator.CreateInterfaceProxyWithoutTarget<IWithGenericMethod>(recorder); | ||
proxy.Method(42); | ||
|
||
Assert.AreEqual(typeof(InterfaceMethodWithoutTargetInvocation), recorder.InvocationType); | ||
} | ||
|
||
public interface IWithNonGenericMethod | ||
{ | ||
void Method(); | ||
} | ||
|
||
public interface IWithGenericMethod | ||
{ | ||
void Method<T>(T arg); | ||
} | ||
|
||
private sealed class InvocationTypeRecorder : IInterceptor | ||
{ | ||
public Type InvocationType { get; private set; } | ||
|
||
public void Intercept(IInvocation invocation) | ||
{ | ||
InvocationType = invocation.GetType(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
src/Castle.Core/DynamicProxy/Internal/InterfaceMethodWithoutTargetInvocation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2004-2021 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 InterfaceMethodWithoutTargetInvocation : AbstractInvocation | ||
{ | ||
public InterfaceMethodWithoutTargetInvocation(object target, object proxy, IInterceptor[] interceptors, MethodInfo proxiedMethod, object[] arguments) | ||
: base(proxy, interceptors, proxiedMethod, arguments) | ||
{ | ||
// This invocation type is suitable for interface method invocations that cannot proceed | ||
// to a target, i.e. where `InvokeMethodOnTarget` will always throw: | ||
|
||
Debug.Assert(target == null, $"{nameof(InterfaceMethodWithoutTargetInvocation)} does not support targets."); | ||
Debug.Assert(proxiedMethod.IsAbstract, $"{nameof(InterfaceMethodWithoutTargetInvocation)} does not support non-abstract methods."); | ||
|
||
// Why this restriction? Because it greatly benefits proxy type generation performance. | ||
// | ||
// For invocations that can proceed to a target, `InvokeMethodOnTarget`'s implementation | ||
// depends on the target method's signature. Because of this, DynamicProxy needs to | ||
// dynamically generate a separate invocation type per such method. Type generation is | ||
// always expensive... that is, slow. | ||
// | ||
// However, if it is known that `InvokeMethodOnTarget` won't forward, but throw, | ||
// no custom (dynamically generated) invocation type is needed at all, and we can use | ||
// this unspecific invocation type instead. | ||
} | ||
|
||
// The next three properties mimick the behavior seen with an interface proxy without target. | ||
// (This is why this type's name starts with `Interface`.) A similar type could be written | ||
// for class proxies without target, but the values returned here would be different. | ||
|
||
public override object InvocationTarget => null; | ||
|
||
public override MethodInfo MethodInvocationTarget => null; | ||
|
||
public override Type TargetType => null; | ||
|
||
protected override void InvokeMethodOnTarget() => ThrowOnNoTarget(); | ||
} | ||
} |