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);
        }
 public CoercedAwaitableInfo(AwaitableInfo awaitableInfo)
 {
     AwaitableInfo     = awaitableInfo;
     CoercerExpression = null;
     CoercerResultType = null;
 }
 public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType, AwaitableInfo coercedAwaitableInfo)
 {
     CoercerExpression = coercerExpression;
     CoercerResultType = coercerResultType;
     AwaitableInfo     = coercedAwaitableInfo;
 }
        public static bool IsTypeAwaitable(Type type, out AwaitableInfo awaitableInfo)
        {
            // Based on Roslyn code: http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Shared/Extensions/ISymbolExtensions.cs,db4d48ba694b9347

            // Awaitable must have method matching "object GetAwaiter()"
            var getAwaiterMethod = type.GetRuntimeMethods().FirstOrDefault(m =>
                                                                           m.Name.Equals("GetAwaiter", StringComparison.OrdinalIgnoreCase) &&
                                                                           m.GetParameters().Length == 0 &&
                                                                           m.ReturnType != null);

            if (getAwaiterMethod == null)
            {
                awaitableInfo = default(AwaitableInfo);
                return(false);
            }

            var awaiterType = getAwaiterMethod.ReturnType;

            // Awaiter must have property matching "bool IsCompleted { get; }"
            var isCompletedProperty = awaiterType.GetRuntimeProperties().FirstOrDefault(p =>
                                                                                        p.Name.Equals("IsCompleted", StringComparison.OrdinalIgnoreCase) &&
                                                                                        p.PropertyType == typeof(bool) &&
                                                                                        p.GetMethod != null);

            if (isCompletedProperty == null)
            {
                awaitableInfo = default(AwaitableInfo);
                return(false);
            }

            // Awaiter must implement INotifyCompletion
            var awaiterInterfaces           = awaiterType.GetInterfaces();
            var implementsINotifyCompletion = awaiterInterfaces.Any(t => t == typeof(INotifyCompletion));

            if (!implementsINotifyCompletion)
            {
                awaitableInfo = default(AwaitableInfo);
                return(false);
            }

            // INotifyCompletion supplies a method matching "void OnCompleted(Action action)"
            var onCompletedMethod = typeof(INotifyCompletion).GetRuntimeMethods().Single(m =>
                                                                                         m.Name.Equals("OnCompleted", StringComparison.OrdinalIgnoreCase) &&
                                                                                         m.ReturnType == typeof(void) &&
                                                                                         m.GetParameters().Length == 1 &&
                                                                                         m.GetParameters()[0].ParameterType == typeof(Action));

            // Awaiter optionally implements ICriticalNotifyCompletion
            var        implementsICriticalNotifyCompletion = awaiterInterfaces.Any(t => t == typeof(ICriticalNotifyCompletion));
            MethodInfo unsafeOnCompletedMethod;

            if (implementsICriticalNotifyCompletion)
            {
                // ICriticalNotifyCompletion supplies a method matching "void UnsafeOnCompleted(Action action)"
                unsafeOnCompletedMethod = typeof(ICriticalNotifyCompletion).GetRuntimeMethods().Single(m =>
                                                                                                       m.Name.Equals("UnsafeOnCompleted", StringComparison.OrdinalIgnoreCase) &&
                                                                                                       m.ReturnType == typeof(void) &&
                                                                                                       m.GetParameters().Length == 1 &&
                                                                                                       m.GetParameters()[0].ParameterType == typeof(Action));
            }
            else
            {
                unsafeOnCompletedMethod = null;
            }

            // Awaiter must have method matching "void GetResult" or "T GetResult()"
            var getResultMethod = awaiterType.GetRuntimeMethods().FirstOrDefault(m =>
                                                                                 m.Name.Equals("GetResult") &&
                                                                                 m.GetParameters().Length == 0);

            if (getResultMethod == null)
            {
                awaitableInfo = default(AwaitableInfo);
                return(false);
            }

            awaitableInfo = new AwaitableInfo(
                awaiterType,
                isCompletedProperty,
                getResultMethod,
                onCompletedMethod,
                unsafeOnCompletedMethod,
                getResultMethod.ReturnType,
                getAwaiterMethod);
            return(true);
        }