Skip to content

Commit

Permalink
Code refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
heku committed Sep 7, 2023
1 parent b56201f commit 5b47d41
Show file tree
Hide file tree
Showing 31 changed files with 396 additions and 396 deletions.
31 changes: 10 additions & 21 deletions Kunet.AsyncInterceptor.PerfTests/AdaptersFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,24 @@ public class AdaptersFactoryTests
public IInvocation Invocation { get; } = Mock.Of<IInvocation>(x => x.Method.ReturnType == typeof(Task<string>));

[Benchmark]
public void NewFactory() => AdaptersFactory.TryCreate(Invocation, out _, out _);
public void NewFactory() => AsyncAdapter.TryCreate(Invocation, out _);

[Benchmark]
public void OldFactory() => LegacyFactory.TryCreate(Invocation, out _, out _);
public void OldFactory() => LegacyFactory.TryCreate(Invocation, out _);

internal static class LegacyFactory
{
public static bool TryCreate(IInvocation invocation, out IAsyncTaskBuilder builder, out IAsyncInvocation asyncInvocation)
public static bool TryCreate(IInvocation invocation, out IAsyncAdapter adapter)
{
var returnType = invocation.Method.ReturnType;
if (returnType == typeof(Task))
{
builder = new AsyncTaskBuilderOfTask();
asyncInvocation = new AsyncInvocationOfTask(invocation);
adapter = new AsyncAdapterOfTask(invocation);
return true;
}
if (returnType == typeof(ValueTask))
{
builder = new AsyncTaskBuilderOfValueTask();
asyncInvocation = new AsyncInvocationOfValueTask(invocation);
adapter = new AsyncAdapterOfValueTask(invocation);
return true;
}
if (returnType.IsGenericType && returnType.GenericTypeArguments.Length == 1)
Expand All @@ -40,32 +38,23 @@ public static bool TryCreate(IInvocation invocation, out IAsyncTaskBuilder build
var argumentType = returnType.GenericTypeArguments[0];
if (genericType == typeof(Task<>))
{
builder = CreateAsyncTaskBuilder(typeof(AsyncTaskBuilderOfTask<>), argumentType);
asyncInvocation = CreateAsyncInvocation(typeof(AsyncInvocationOfTask<>), argumentType, invocation);
adapter = CreateAsyncAdapter(typeof(AsyncAdapterOfTask<>), argumentType, invocation);
return true;
}
if (genericType == typeof(ValueTask<>))
{
builder = CreateAsyncTaskBuilder(typeof(AsyncTaskBuilderOfValueTask<>), argumentType);
asyncInvocation = CreateAsyncInvocation(typeof(AsyncInvocationOfValueTask<>), argumentType, invocation);
adapter = CreateAsyncAdapter(typeof(AsyncAdapterOfValueTask<>), argumentType, invocation);
return true;
}
}
builder = null;
asyncInvocation = null;
adapter = null;
return false;
}

private static IAsyncTaskBuilder CreateAsyncTaskBuilder(Type genericType, Type argumentType)
private static IAsyncAdapter CreateAsyncAdapter(Type genericType, Type argumentType, IInvocation invocation)
{
var closedType = genericType.MakeGenericType(argumentType);
return (IAsyncTaskBuilder)Activator.CreateInstance(closedType);
}

private static IAsyncInvocation CreateAsyncInvocation(Type genericType, Type argumentType, IInvocation invocation)
{
var closedType = genericType.MakeGenericType(argumentType);
return (IAsyncInvocation)Activator.CreateInstance(closedType, invocation);
return (IAsyncAdapter)Activator.CreateInstance(closedType, invocation);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
<PackageReference Include="Castle.Core" Version="5.0.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.7" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="stakx.DynamicProxy.AsyncInterceptor" Version="0.1.0" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion Kunet.AsyncInterceptor.Tests/AsyncInterceptorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public async Task InterceptByManyReturnValueFormattersTest()
public async Task InterceptCustomTaskLikeReturnValueExample()
{
// MyTask<T> is an example custom task-like type
AdaptersFactory.Register(typeof(MyTask<>), typeof(AsyncTaskBuilderOfMyTask<>), typeof(AsyncInvocationOfMyTask<>));
AsyncAdapter.Register(typeof(MyTask<>), typeof(AsyncAdapterOfMyTask<>));

var target = Mock.Of<IGet>(x =>
x.GetTaskLikeType<string>() == new MyTask<string>("value")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Castle.DynamicProxy;

namespace Kunet.AsyncInterceptor.Tests;

// You must provide this to set AsyncResult from Invocation.ReturnValue
// You must provide this to adapt your custom TaskMethodBuilder to IAsyncTaskBuilder

internal sealed class AsyncAdapterOfMyTask<T> : AsyncAdapter
{
private readonly MyTaskMethodBuilder<T> _builder = MyTaskMethodBuilder<T>.Create();

public AsyncAdapterOfMyTask(IInvocation invocation) : base(invocation) => Task = _builder.Task;

protected override async ValueTask SetAsyncResult() => AsyncResult = await (MyTask<T>)Invocation.ReturnValue;

public override object Task { get; }

public override void Start<TStateMachine>(ref TStateMachine stateMachine) => _builder.Start(ref stateMachine);

public override void SetException(Exception ex) => _builder.SetException(ex);

public override void SetResult(object result) => _builder.SetResult((T)result);

public override void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) => _builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ public class MyTaskMethodBuilder<T>
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine => stateMachine.MoveNext();

#pragma warning disable IDE0060 // Remove unused parameter
public void SetStateMachine(IAsyncStateMachine stateMachine)
#pragma warning restore IDE0060 // Remove unused parameter
public void SetStateMachine(IAsyncStateMachine _)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
74 changes: 0 additions & 74 deletions Kunet.AsyncInterceptor/AdaptersFactory.cs

This file was deleted.

77 changes: 77 additions & 0 deletions Kunet.AsyncInterceptor/AsyncAdapter.Factory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Castle.DynamicProxy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Kunet.AsyncInterceptor;

public partial class AsyncAdapter
{
internal static readonly Dictionary<Type, Func<IInvocation, AsyncAdapter>> FactoryCache = new(); // Task -> (invocation => new AsyncAdapterOfTask(invocation))
internal static readonly Dictionary<Type, Type> OpenGenericTypesRegistration = new(); // Task<T> -> AsyncAdapterOfTask<T>

static AsyncAdapter()
{
Register<Task>(x => new AsyncAdapterOfTask(x)); // Task
Register<ValueTask>(x => new AsyncAdapterOfValueTask(x)); // ValueTask
Register(typeof(Task<>), typeof(AsyncAdapterOfTask<>)); // Task<T>
Register(typeof(ValueTask<>), typeof(AsyncAdapterOfValueTask<>)); // ValueTask<T>
}

public static void Register<T>(Func<IInvocation, AsyncAdapter> factory) => FactoryCache[typeof(T)] = factory;

public static void Register(Type openGenericReturnType, Type openGenericAsyncAdapterType) => OpenGenericTypesRegistration[openGenericReturnType] = openGenericAsyncAdapterType;

internal static bool TryCreate(IInvocation invocation, out AsyncAdapter adapter)
{
var returnType = invocation.Method.ReturnType;
if (FactoryCache.TryGetValue(returnType, out var factory))
{
adapter = factory.Invoke(invocation);
return true;
}
if (returnType.IsGenericType && returnType.GenericTypeArguments.Length == 1)
{
var openGenericReturnType = returnType.GetGenericTypeDefinition();
if (OpenGenericTypesRegistration.TryGetValue(openGenericReturnType, out var openGenericAsyncAdapterType))
{
factory = CreateFactory(openGenericAsyncAdapterType, returnType.GenericTypeArguments);
FactoryCache[returnType] = factory;
adapter = factory.Invoke(invocation);
return true;
}
}
var builderType =
(Attribute.GetCustomAttribute(invocation.Method, typeof(AsyncMethodBuilderAttribute), false) as AsyncMethodBuilderAttribute)?.BuilderType ??
(Attribute.GetCustomAttribute(invocation.Method.ReturnType, typeof(AsyncMethodBuilderAttribute), false) as AsyncMethodBuilderAttribute)?.BuilderType;
if (builderType is not null)
{
if (builderType.IsGenericType is false)
{
adapter = new AsyncAdapterFallback(builderType, invocation);
return true;
}
if (builderType.IsGenericType && returnType.IsGenericType && returnType.GenericTypeArguments.Length == 1)
{
builderType = builderType.MakeGenericType(returnType.GenericTypeArguments);
adapter = new AsyncAdapterFallback(builderType, invocation);
return true;
}
}
adapter = null;
return false;
}

private static Func<IInvocation, AsyncAdapter> CreateFactory(Type openGenericReturnType, Type[] typeArguments)
{
var adapterType = openGenericReturnType.MakeGenericType(typeArguments);
var adapterCtor = adapterType.GetConstructors().Single();
var parameter = Expression.Parameter(typeof(IInvocation));
var newInvocation = Expression.New(adapterCtor, parameter);

return (Func<IInvocation, AsyncAdapter>)Expression.Lambda(newInvocation, parameter).Compile();
}
}
51 changes: 51 additions & 0 deletions Kunet.AsyncInterceptor/AsyncAdapter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Castle.DynamicProxy;
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace Kunet.AsyncInterceptor;

public abstract partial class AsyncAdapter : IAsyncAdapter
{
#region IAsyncInvocation

private readonly IInvocationProceedInfo _proceed;

protected AsyncAdapter(IInvocation invocation)
{
Invocation = invocation;
_proceed = invocation.CaptureProceedInfo();
}

public IInvocation Invocation { get; }

public object AsyncResult { get; set; }

public async ValueTask ProceedAsync()
{
_proceed.Invoke(); // Invocation.ReturnValue = NEXT()
if (Invocation.ReturnValue is not null)
{
await SetAsyncResult().ConfigureAwait(false); // AsyncResult = await Invocation.ReturnValue
}
}

/// <summary>
/// <c>AsyncResult = <see langword="await"/> Invocation.ReturnValue</c>
/// </summary>
protected abstract ValueTask SetAsyncResult();

#endregion IAsyncInvocation

public abstract object Task { get; }

public abstract void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine;

public abstract void SetResult(object result);

public abstract void SetException(Exception exception);

public abstract void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine;
}
Loading

0 comments on commit 5b47d41

Please sign in to comment.