FSharpAsyncAwaitAdapter
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace NUnit.Framework.Internal
{
[NullableContext(1)]
[Nullable(0)]
internal static class FSharpAsyncAwaitAdapter
{
[Nullable(0)]
private sealed class AsyncInfo
{
public Type FSharpAsyncTypeDefinition { get; }
public Type ResultType { get; }
public AsyncInfo(Type fSharpAsyncTypeDefinition, Type resultType)
{
FSharpAsyncTypeDefinition = fSharpAsyncTypeDefinition;
ResultType = resultType;
}
}
[Nullable(2)]
private static MethodInfo _startImmediateAsTaskMethod;
public static bool IsAwaitable(Type awaitableType)
{
return GetAsyncInfo(awaitableType) != null;
}
[return: Nullable(2)]
public static Type GetResultType(Type awaitableType)
{
return GetAsyncInfo(awaitableType)?.ResultType;
}
[return: Nullable(2)]
private static AsyncInfo GetAsyncInfo(Type asyncType)
{
if ((object)asyncType == null)
return null;
if (!asyncType.IsGenericType)
return null;
Type genericTypeDefinition = asyncType.GetGenericTypeDefinition();
if (genericTypeDefinition.FullName != "Microsoft.FSharp.Control.FSharpAsync`1")
return null;
return new AsyncInfo(genericTypeDefinition, asyncType.GetGenericArguments()[0]);
}
[return: Nullable(2)]
public static AwaitAdapter TryCreate(object awaitable)
{
if (awaitable == null)
return null;
AsyncInfo info = GetAsyncInfo(awaitable.GetType());
if (info == null)
return null;
if ((object)_startImmediateAsTaskMethod == null) {
Type type = info.FSharpAsyncTypeDefinition.Assembly.GetType("Microsoft.FSharp.Control.FSharpAsync");
if ((object)type == null)
throw new InvalidOperationException("Cannot find non-generic FSharpAsync type in the same assembly as the generic one.");
_startImmediateAsTaskMethod = type.GetMethods(BindingFlags.Static | BindingFlags.Public).Single(delegate(MethodInfo method) {
if (method.Name != "StartImmediateAsTask")
return false;
Type[] genericArguments = method.GetGenericArguments();
if (genericArguments.Length != 1)
return false;
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length != 2)
return false;
if (parameters[0].ParameterType != info.FSharpAsyncTypeDefinition.MakeGenericType(genericArguments[0]))
return false;
if (parameters[1].ParameterType.IsFSharpOption(out Type someType))
return someType.FullName == "System.Threading.CancellationToken";
return false;
});
}
object awaitable2 = _startImmediateAsTaskMethod.MakeGenericMethod(info.ResultType).Invoke(null, new object[2] {
awaitable,
null
});
return AwaitAdapter.FromAwaitable(awaitable2);
}
}
}