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 ConstructorInfo ObjectConstructor = typeof(object).GetConstructors().Single();
|
||||
|
||||
private static readonly Type[] ParameterTypes = new Type[] { typeof(IClientProxy) };
|
||||
|
||||
public static T Build(IClientProxy proxy)
|
||||
{
|
||||
return _builder.Value(proxy);
|
||||
|
|
@ -40,20 +44,24 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
|||
var moduleBuilder = assemblyBuilder.DefineDynamicModule(ClientModuleName);
|
||||
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)
|
||||
{
|
||||
var type = moduleBuilder.DefineType(
|
||||
ClientModuleName + "." + typeof(T).Name + "Impl",
|
||||
TypeAttributes.Public,
|
||||
typeof(Object),
|
||||
new[] { typeof(T) });
|
||||
var name = ClientModuleName + "." + typeof(T).Name + "Impl";
|
||||
|
||||
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)))
|
||||
{
|
||||
|
|
@ -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,
|
||||
null, new Type[] { }, null);
|
||||
|
||||
method.SetReturnType(typeof(void));
|
||||
method.SetParameters(typeof(IClientProxy));
|
||||
|
||||
var generator = method.GetILGenerator();
|
||||
var generator = ctor.GetILGenerator();
|
||||
|
||||
// Call object constructor
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
generator.Emit(OpCodes.Call, ctor);
|
||||
generator.Emit(OpCodes.Call, ObjectConstructor);
|
||||
|
||||
// Assign constructor argument to the proxyField
|
||||
generator.Emit(OpCodes.Ldarg_0); // type
|
||||
generator.Emit(OpCodes.Ldarg_1); // type proxyfield
|
||||
generator.Emit(OpCodes.Stfld, proxyField); // type.proxyField = proxyField
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
return ctor;
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!interfaceType.IsInterface)
|
||||
|
|
@ -206,7 +221,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
|||
|
||||
foreach (var method in interfaceType.GetMethods())
|
||||
{
|
||||
VerifyMethod(interfaceType, method);
|
||||
VerifyMethod(method);
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue