These are chat archives for JasperFx/jasper

7th
Jul 2016
Jeremy D. Miller
@jeremydmiller
Jul 07 2016 20:59
@/all The FubuMVC reboot to “Jasper” is actively underway, finally. The first intermediate step is to make the current FubuMVC 3 codebase be “async by default” by changing the old IActionBehavior interface to:
public interface IActionBehavior
{
    Task Invoke();
    Task InvokePartial();
}
and just to feel like I accomplished something today (and I did dammit), the following code actually works:
public class ActionInvocation<TEndpoint>
{
    private static readonly MethodInfo ConfigureAwaitMethod = typeof(Task).GetMethod(nameof(Task.ConfigureAwait),
        new Type[] {typeof(bool)});

    private static readonly MethodInfo FubuRequestSet = typeof(IFubuRequest).GetMethod(nameof(IFubuRequest.Set),
        new Type[] {typeof(Type), typeof(object)});

    private readonly Func<TEndpoint, IFubuRequest, Task> _action;


    public ActionInvocation(MethodInfo method)
    {
        var request = Expression.Parameter(typeof(IFubuRequest), "request");
        var endpoint = Expression.Parameter(typeof(TEndpoint), "endpoint");

        var inputs = method.GetParameters().Select(param => buildExpressionForParameter(request, param));

        Expression body = Expression.Call(endpoint, method, inputs);

        if (method.ReturnType != typeof(Task) && method.ReturnType.CanBeCastTo<Task>())
        {
            var outputType = method.ReturnType.GetGenericArguments().Single();

            var awaiterMethod = method.ReturnType.GetMethod(nameof(Task.GetAwaiter));
            body = Expression.Call(body, awaiterMethod);

            var resultMethod = awaiterMethod.ReturnType.GetMethod(nameof(TaskAwaiter.GetResult));
            body = Expression.Call(body, resultMethod);

            body = setRequestExpression(outputType, request, body);

        }
        else if (method.ReturnType != typeof(void) && method.ReturnType != typeof(Task))
        {
            body = setRequestExpression(method.ReturnType, request, body);
        }


        if (!body.Type.CanBeCastTo<Task>())
        {
            var returnStatement = Expression.Constant(Task.CompletedTask);
            body = Expression.Block(body, returnStatement);
        }


        var lambda = Expression.Lambda<Func<TEndpoint, IFubuRequest, Task>>(body, endpoint, request);

        _action = lambda.Compile();
    }

    private Expression setRequestExpression(Type outputType, ParameterExpression request, Expression value)
    {
        return Expression.Call(request, FubuRequestSet, Expression.Constant(outputType), value);
    }

    private Expression buildExpressionForParameter(ParameterExpression request, ParameterInfo parameter)
    {
        var method = typeof(IFubuRequest).GetMethod(nameof(IFubuRequest.Get), new Type[0]).MakeGenericMethod(parameter.ParameterType);

        return Expression.Call(request, method);
    }

    public Task Invoke(IFubuRequest request, TEndpoint endpoint)
    {
        return _action(endpoint, request);
    }
}