Пример #1
0
    public static bool IsTypeAwaitable(
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type,
        out CoercedAwaitableInfo info)
    {
        if (AwaitableInfo.IsTypeAwaitable(type, out var directlyAwaitableInfo))
        {
            info = new CoercedAwaitableInfo(directlyAwaitableInfo);
            return(true);
        }

        // It's not directly awaitable, but maybe we can coerce it.
        // Currently we support coercing FSharpAsync<T>.
        if (ObjectMethodExecutorFSharpSupport.TryBuildCoercerFromFSharpAsyncToAwaitable(type,
                                                                                        out var coercerExpression,
                                                                                        out var coercerResultType))
        {
            if (AwaitableInfo.IsTypeAwaitable(coercerResultType, out var coercedAwaitableInfo))
            {
                info = new CoercedAwaitableInfo(coercerExpression, coercerResultType, coercedAwaitableInfo);
                return(true);
            }
        }

        info = default(CoercedAwaitableInfo);
        return(false);
    }
Пример #2
0
        public static bool IsAsyncMethodInfo([NotNull] this MethodInfo methodInfo)
        {
            Check.NotNull(methodInfo, nameof(methodInfo));
            var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(methodInfo.ReturnType, out var coercedAwaitableInfo);

            return(isAwaitable);
        }
Пример #3
0
    private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]?parameterDefaultValues)
    {
        if (methodInfo == null)
        {
            throw new ArgumentNullException(nameof(methodInfo));
        }

        MethodInfo       = methodInfo;
        MethodParameters = methodInfo.GetParameters();
        TargetTypeInfo   = targetTypeInfo;
        MethodReturnType = methodInfo.ReturnType;

        var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(MethodReturnType, out var coercedAwaitableInfo);

        IsMethodAsync   = isAwaitable;
        AsyncResultType = isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : null;

        // Upstream code may prefer to use the sync-executor even for async methods, because if it knows
        // that the result is a specific Task<T> where T is known, then it can directly cast to that type
        // and await it without the extra heap allocations involved in the _executorAsync code path.
        _executor = GetExecutor(methodInfo, targetTypeInfo);

        if (IsMethodAsync)
        {
            _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo, coercedAwaitableInfo);
        }

        _parameterDefaultValues = parameterDefaultValues;
    }
Пример #4
0
        public static Type GetReturnType(this MethodInfo methodInfo)
        {
            Check.NotNull(methodInfo, nameof(methodInfo));
            var isAwaitable = CoercedAwaitableInfo.IsTypeAwaitable(methodInfo.ReturnType, out var coercedAwaitableInfo);

            return(isAwaitable ? coercedAwaitableInfo.AwaitableInfo.ResultType : methodInfo.ReturnType);
        }
Пример #5
0
    private static MethodExecutorAsync GetExecutorAsync(
        MethodInfo methodInfo,
        TypeInfo targetTypeInfo,
        CoercedAwaitableInfo coercedAwaitableInfo)
    {
        // Parameters to executor
        var targetParameter     = Expression.Parameter(typeof(object), "target");
        var parametersParameter = Expression.Parameter(typeof(object[]), "parameters");

        // Build parameter list
        var paramInfos = methodInfo.GetParameters();
        var parameters = new List <Expression>(paramInfos.Length);

        for (int i = 0; i < paramInfos.Length; i++)
        {
            var paramInfo = paramInfos[i];
            var valueObj  = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));
            var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType);

            // valueCast is "(Ti) parameters[i]"
            parameters.Add(valueCast);
        }

        // Call method
        var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType());
        var methodCall   = Expression.Call(instanceCast, methodInfo, parameters);

        // Using the method return value, construct an ObjectMethodExecutorAwaitable based on
        // the info we have about its implementation of the awaitable pattern. Note that all
        // the funcs/actions we construct here are precompiled, so that only one instance of
        // each is preserved throughout the lifetime of the ObjectMethodExecutor.

        // var getAwaiterFunc = (object awaitable) =>
        //     (object)((CustomAwaitableType)awaitable).GetAwaiter();
        var customAwaitableParam         = Expression.Parameter(typeof(object), "awaitable");
        var awaitableInfo                = coercedAwaitableInfo.AwaitableInfo;
        var postCoercionMethodReturnType = coercedAwaitableInfo.CoercerResultType ?? methodInfo.ReturnType;
        var getAwaiterFunc               = Expression.Lambda <Func <object, object> >(
            Expression.Convert(
                Expression.Call(
                    Expression.Convert(customAwaitableParam, postCoercionMethodReturnType),
                    awaitableInfo.GetAwaiterMethod),
                typeof(object)),
            customAwaitableParam).Compile();

        // var isCompletedFunc = (object awaiter) =>
        //     ((CustomAwaiterType)awaiter).IsCompleted;
        var isCompletedParam = Expression.Parameter(typeof(object), "awaiter");
        var isCompletedFunc  = Expression.Lambda <Func <object, bool> >(
            Expression.MakeMemberAccess(
                Expression.Convert(isCompletedParam, awaitableInfo.AwaiterType),
                awaitableInfo.AwaiterIsCompletedProperty),
            isCompletedParam).Compile();

        var getResultParam = Expression.Parameter(typeof(object), "awaiter");
        Func <object, object> getResultFunc;

        if (awaitableInfo.ResultType == typeof(void))
        {
            // var getResultFunc = (object awaiter) =>
            // {
            //     ((CustomAwaiterType)awaiter).GetResult(); // We need to invoke this to surface any exceptions
            //     return (object)null;
            // };
            getResultFunc = Expression.Lambda <Func <object, object> >(
                Expression.Block(
                    Expression.Call(
                        Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
                        awaitableInfo.AwaiterGetResultMethod),
                    Expression.Constant(null)
                    ),
                getResultParam).Compile();
        }
        else
        {
            // var getResultFunc = (object awaiter) =>
            //     (object)((CustomAwaiterType)awaiter).GetResult();
            getResultFunc = Expression.Lambda <Func <object, object> >(
                Expression.Convert(
                    Expression.Call(
                        Expression.Convert(getResultParam, awaitableInfo.AwaiterType),
                        awaitableInfo.AwaiterGetResultMethod),
                    typeof(object)),
                getResultParam).Compile();
        }

        // var onCompletedFunc = (object awaiter, Action continuation) => {
        //     ((CustomAwaiterType)awaiter).OnCompleted(continuation);
        // };
        var onCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
        var onCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
        var onCompletedFunc   = Expression.Lambda <Action <object, Action> >(
            Expression.Call(
                Expression.Convert(onCompletedParam1, awaitableInfo.AwaiterType),
                awaitableInfo.AwaiterOnCompletedMethod,
                onCompletedParam2),
            onCompletedParam1,
            onCompletedParam2).Compile();

        Action <object, Action>?unsafeOnCompletedFunc = null;

        if (awaitableInfo.AwaiterUnsafeOnCompletedMethod != null)
        {
            // var unsafeOnCompletedFunc = (object awaiter, Action continuation) => {
            //     ((CustomAwaiterType)awaiter).UnsafeOnCompleted(continuation);
            // };
            var unsafeOnCompletedParam1 = Expression.Parameter(typeof(object), "awaiter");
            var unsafeOnCompletedParam2 = Expression.Parameter(typeof(Action), "continuation");
            unsafeOnCompletedFunc = Expression.Lambda <Action <object, Action> >(
                Expression.Call(
                    Expression.Convert(unsafeOnCompletedParam1, awaitableInfo.AwaiterType),
                    awaitableInfo.AwaiterUnsafeOnCompletedMethod,
                    unsafeOnCompletedParam2),
                unsafeOnCompletedParam1,
                unsafeOnCompletedParam2).Compile();
        }

        // If we need to pass the method call result through a coercer function to get an
        // awaitable, then do so.
        var coercedMethodCall = coercedAwaitableInfo.RequiresCoercion
            ? Expression.Invoke(coercedAwaitableInfo.CoercerExpression, methodCall)
            : (Expression)methodCall;

        // return new ObjectMethodExecutorAwaitable(
        //     (object)coercedMethodCall,
        //     getAwaiterFunc,
        //     isCompletedFunc,
        //     getResultFunc,
        //     onCompletedFunc,
        //     unsafeOnCompletedFunc);
        var returnValueExpression = Expression.New(
            _objectMethodExecutorAwaitableConstructor,
            Expression.Convert(coercedMethodCall, typeof(object)),
            Expression.Constant(getAwaiterFunc),
            Expression.Constant(isCompletedFunc),
            Expression.Constant(getResultFunc),
            Expression.Constant(onCompletedFunc),
            Expression.Constant(unsafeOnCompletedFunc, typeof(Action <object, Action>)));

        var lambda = Expression.Lambda <MethodExecutorAsync>(returnValueExpression, targetParameter, parametersParameter);

        return(lambda.Compile());
    }