Пример #1
0
        private ObjectMethodExecutorCompiledFastClosure(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;
        }
        public static bool IsTypeAwaitable(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);
        }
        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 Expression[paramInfos.Length];

            for (var i = 0; i < paramInfos.Length; i++)
            {
                parameters[i] = Expression.Convert(Expression.ArrayIndex(parametersParameter, Expression.Constant(i)),
                                                   paramInfos[i].ParameterType);
            }

            // 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)
                                               .TryCompileWithoutClosure <Func <object, object> >();

            // 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).TryCompileWithoutClosure <Func <object, bool> >();

            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).TryCompileWithoutClosure <Func <object, object> >();
            }
            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).TryCompileWithoutClosure <Func <object, object> >();
            }

            // 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).TryCompileWithoutClosure <Action <object, Action> >();

            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).TryCompileWithoutClosure <Action <object, Action> >();
            }

            // 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 getAwaiterFuncConst        = Expression.Constant(getAwaiterFunc);
            var isCompletedFuncConst       = Expression.Constant(isCompletedFunc);
            var getResultFuncConst         = Expression.Constant(getResultFunc);
            var onCompletedFuncConst       = Expression.Constant(onCompletedFunc);
            var unsafeOnCompletedFuncConst = Expression.Constant(unsafeOnCompletedFunc, typeof(Action <object, Action>));

            var returnValueExpression = Expression.New(
                _objectMethodExecutorAwaitableConstructor,
                Expression.Convert(coercedMethodCall, typeof(object)),
                getAwaiterFuncConst,
                isCompletedFuncConst,
                getResultFuncConst,
                onCompletedFuncConst,
                unsafeOnCompletedFuncConst);

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

            var closure = ExpressionCompiler.Closure.Create(
                getAwaiterFunc,
                isCompletedFunc,
                getResultFunc,
                onCompletedFunc,
                unsafeOnCompletedFunc);

            return(lambda.TryCompileWithPreCreatedClosure <MethodExecutorAsync>(
                       closure,
                       getAwaiterFuncConst,
                       isCompletedFuncConst,
                       getResultFuncConst,
                       onCompletedFuncConst,
                       unsafeOnCompletedFuncConst));
        }