Use dynamically compiled factory instead of Activator.CreateInstance in TypedClientBuilder (#14615)
This commit is contained in:
parent
d97022b214
commit
f31ce2de0f
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
||||||
|
{
|
||||||
|
public class TypedClientBuilderBenchmark
|
||||||
|
{
|
||||||
|
private static readonly IClientProxy Dummy = new DummyProxy();
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public ITestClient Build()
|
||||||
|
{
|
||||||
|
return TypedClientBuilder<ITestClient>.Build(Dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ITestClient { }
|
||||||
|
|
||||||
|
private class DummyProxy : IClientProxy
|
||||||
|
{
|
||||||
|
public Task SendCoreAsync(string method, object[] args, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
|
||||||
private static readonly PropertyInfo CancellationTokenNoneProperty = typeof(CancellationToken).GetProperty("None", BindingFlags.Public | BindingFlags.Static);
|
private static readonly PropertyInfo CancellationTokenNoneProperty = typeof(CancellationToken).GetProperty("None", BindingFlags.Public | BindingFlags.Static);
|
||||||
|
|
||||||
|
private static readonly ConstructorInfo ObjectConstructor = typeof(object).GetConstructors().Single();
|
||||||
|
|
||||||
|
private static readonly Type[] ParameterTypes = new Type[] { typeof(IClientProxy) };
|
||||||
|
|
||||||
public static T Build(IClientProxy proxy)
|
public static T Build(IClientProxy proxy)
|
||||||
{
|
{
|
||||||
return _builder.Value(proxy);
|
return _builder.Value(proxy);
|
||||||
|
|
@ -40,20 +44,24 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule(ClientModuleName);
|
var moduleBuilder = assemblyBuilder.DefineDynamicModule(ClientModuleName);
|
||||||
var clientType = GenerateInterfaceImplementation(moduleBuilder);
|
var clientType = GenerateInterfaceImplementation(moduleBuilder);
|
||||||
|
|
||||||
return proxy => (T)Activator.CreateInstance(clientType, proxy);
|
var factoryMethod = clientType.GetMethod(nameof(Build), BindingFlags.Public | BindingFlags.Static);
|
||||||
|
return (Func<IClientProxy, T>)factoryMethod.CreateDelegate(typeof(Func<IClientProxy, T>));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Type GenerateInterfaceImplementation(ModuleBuilder moduleBuilder)
|
private static Type GenerateInterfaceImplementation(ModuleBuilder moduleBuilder)
|
||||||
{
|
{
|
||||||
var type = moduleBuilder.DefineType(
|
var name = ClientModuleName + "." + typeof(T).Name + "Impl";
|
||||||
ClientModuleName + "." + typeof(T).Name + "Impl",
|
|
||||||
TypeAttributes.Public,
|
|
||||||
typeof(Object),
|
|
||||||
new[] { typeof(T) });
|
|
||||||
|
|
||||||
var proxyField = type.DefineField("_proxy", typeof(IClientProxy), FieldAttributes.Private);
|
var type = moduleBuilder.DefineType(name, TypeAttributes.Public, typeof(object), new[] { typeof(T) });
|
||||||
|
|
||||||
BuildConstructor(type, proxyField);
|
var proxyField = type.DefineField("_proxy", typeof(IClientProxy), FieldAttributes.Private | FieldAttributes.InitOnly);
|
||||||
|
|
||||||
|
var ctor = BuildConstructor(type, proxyField);
|
||||||
|
|
||||||
|
// Because a constructor doesn't return anything, it can't be wrapped in a
|
||||||
|
// delegate directly, so we emit a factory method that just takes the IClientProxy,
|
||||||
|
// invokes the constructor (using newobj) and returns the new instance of type T.
|
||||||
|
BuildFactoryMethod(type, ctor);
|
||||||
|
|
||||||
foreach (var method in GetAllInterfaceMethods(typeof(T)))
|
foreach (var method in GetAllInterfaceMethods(typeof(T)))
|
||||||
{
|
{
|
||||||
|
|
@ -79,27 +87,23 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BuildConstructor(TypeBuilder type, FieldInfo proxyField)
|
private static ConstructorInfo BuildConstructor(TypeBuilder type, FieldInfo proxyField)
|
||||||
{
|
{
|
||||||
var method = type.DefineMethod(".ctor", System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.HideBySig);
|
var ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, ParameterTypes);
|
||||||
|
|
||||||
var ctor = typeof(object).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
|
var generator = ctor.GetILGenerator();
|
||||||
null, new Type[] { }, null);
|
|
||||||
|
|
||||||
method.SetReturnType(typeof(void));
|
|
||||||
method.SetParameters(typeof(IClientProxy));
|
|
||||||
|
|
||||||
var generator = method.GetILGenerator();
|
|
||||||
|
|
||||||
// Call object constructor
|
// Call object constructor
|
||||||
generator.Emit(OpCodes.Ldarg_0);
|
generator.Emit(OpCodes.Ldarg_0);
|
||||||
generator.Emit(OpCodes.Call, ctor);
|
generator.Emit(OpCodes.Call, ObjectConstructor);
|
||||||
|
|
||||||
// Assign constructor argument to the proxyField
|
// Assign constructor argument to the proxyField
|
||||||
generator.Emit(OpCodes.Ldarg_0); // type
|
generator.Emit(OpCodes.Ldarg_0); // type
|
||||||
generator.Emit(OpCodes.Ldarg_1); // type proxyfield
|
generator.Emit(OpCodes.Ldarg_1); // type proxyfield
|
||||||
generator.Emit(OpCodes.Stfld, proxyField); // type.proxyField = proxyField
|
generator.Emit(OpCodes.Stfld, proxyField); // type.proxyField = proxyField
|
||||||
generator.Emit(OpCodes.Ret);
|
generator.Emit(OpCodes.Ret);
|
||||||
|
|
||||||
|
return ctor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo, FieldInfo proxyField)
|
private static void BuildMethod(TypeBuilder type, MethodInfo interfaceMethodInfo, FieldInfo proxyField)
|
||||||
|
|
@ -187,6 +191,17 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
generator.Emit(OpCodes.Ret); // Return the Task returned by 'invokeMethod'
|
generator.Emit(OpCodes.Ret); // Return the Task returned by 'invokeMethod'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void BuildFactoryMethod(TypeBuilder type, ConstructorInfo ctor)
|
||||||
|
{
|
||||||
|
var method = type.DefineMethod(nameof(Build), MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, typeof(T), ParameterTypes);
|
||||||
|
|
||||||
|
var generator = method.GetILGenerator();
|
||||||
|
|
||||||
|
generator.Emit(OpCodes.Ldarg_0); // Load the IClientProxy argument onto the stack
|
||||||
|
generator.Emit(OpCodes.Newobj, ctor); // Call the generated constructor with the proxy
|
||||||
|
generator.Emit(OpCodes.Ret); // Return the typed client
|
||||||
|
}
|
||||||
|
|
||||||
private static void VerifyInterface(Type interfaceType)
|
private static void VerifyInterface(Type interfaceType)
|
||||||
{
|
{
|
||||||
if (!interfaceType.IsInterface)
|
if (!interfaceType.IsInterface)
|
||||||
|
|
@ -206,7 +221,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
|
||||||
foreach (var method in interfaceType.GetMethods())
|
foreach (var method in interfaceType.GetMethods())
|
||||||
{
|
{
|
||||||
VerifyMethod(interfaceType, method);
|
VerifyMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var parent in interfaceType.GetInterfaces())
|
foreach (var parent in interfaceType.GetInterfaces())
|
||||||
|
|
@ -215,7 +230,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void VerifyMethod(Type interfaceType, MethodInfo interfaceMethod)
|
private static void VerifyMethod(MethodInfo interfaceMethod)
|
||||||
{
|
{
|
||||||
if (interfaceMethod.ReturnType != typeof(Task))
|
if (interfaceMethod.ReturnType != typeof(Task))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue