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)); }