[Generator] Refactor AsyncMethodInfo, set nullability and add collection extensions. (#17538)

Several changes:

- Refactored AsyncMethodInfo and move the collection extensions out of
the Generator class.
- Added tests for the collection extension methods.
- Fix a mistake/bug in which Last was used instead of LastOrDefault
(funny comment was close to the right reason).

---------

Co-authored-by: GitHub Actions Autoformatter <github-actions-autoformatter@xamarin.com>
This commit is contained in:
Manuel de la Pena 2023-02-16 08:47:28 -05:00 коммит произвёл GitHub
Родитель be1bee04b4
Коммит c744a8bbc7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 127 добавлений и 87 удалений

Просмотреть файл

@ -0,0 +1,55 @@
using System;
using System.Linq;
using System.Reflection;
#nullable enable
class AsyncMethodInfo : MemberInformation {
public ParameterInfo [] AsyncInitialParams { get; }
public ParameterInfo [] AsyncCompletionParams { get; }
public bool HasNSError { get; }
public bool IsVoidAsync { get; }
public bool IsSingleArgAsync { get; }
public MethodInfo MethodInfo { get; }
public AsyncMethodInfo (Generator generator, IMemberGatherer gather, Type type, MethodInfo mi, Type categoryExtensionType, bool isExtensionMethod)
: base (generator, gather, mi, type, categoryExtensionType, false, isExtensionMethod)
{
this.MethodInfo = mi;
this.AsyncInitialParams = mi.GetParameters ().DropLast ();
var lastType = mi.GetParameters ().Last ().ParameterType;
if (!lastType.IsSubclassOf (generator.TypeManager.System_Delegate))
throw new BindingException (1036, true, mi.DeclaringType?.FullName, mi.Name, lastType.FullName);
var cbParams = lastType.GetMethod ("Invoke")?.GetParameters () ?? Array.Empty<ParameterInfo> ();
AsyncCompletionParams = cbParams;
if (cbParams.LastOrDefault ()?.ParameterType.Name == "NSError") {
HasNSError = true;
cbParams = cbParams.DropLast ();
}
IsVoidAsync = cbParams.Length == 0;
IsSingleArgAsync = cbParams.Length == 1;
}
public string GetUniqueParamName (string suggestion)
{
while (true) {
bool next = false;
foreach (var pi in AsyncCompletionParams) {
if (pi.Name == suggestion) {
next = true;
break;
}
}
if (!next)
return suggestion;
suggestion = "_" + suggestion;
}
}
}

Просмотреть файл

@ -576,10 +576,3 @@ public class AttributeManager {
}
}
public static class EnumerableExtensions {
public static IEnumerable<T> Yield<T> (this T item)
{
yield return item;
}
}

Просмотреть файл

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
#nullable enable
public static class CollectionsExtensions {
public static T [] DropLast<T> (this T [] arr)
{
if (arr.Length == 0)
return Array.Empty<T> ();
T [] res = new T [arr.Length - 1];
Array.Copy (arr, res, res.Length);
return res;
}
public static IEnumerable<T> Yield<T> (this T item)
{
yield return item;
}
}

Просмотреть файл

@ -4708,76 +4708,20 @@ public partial class Generator : IMemberGatherer {
print ("}}\n", pi.Name.GetSafeParamName ());
}
class AsyncMethodInfo : MemberInformation {
public ParameterInfo [] async_initial_params, async_completion_params;
public bool has_nserror, is_void_async, is_single_arg_async;
public MethodInfo MethodInfo;
public AsyncMethodInfo (Generator generator, IMemberGatherer gather, Type type, MethodInfo mi, Type category_extension_type, bool is_extension_method)
: base (generator, gather, mi, type, category_extension_type, false, is_extension_method)
{
this.MethodInfo = mi;
this.async_initial_params = Generator.DropLast (mi.GetParameters ());
var lastType = mi.GetParameters ().Last ().ParameterType;
if (!lastType.IsSubclassOf (generator.TypeManager.System_Delegate))
throw new BindingException (1036, true, mi.DeclaringType.FullName, mi.Name, lastType.FullName);
var cbParams = lastType.GetMethod ("Invoke").GetParameters ();
async_completion_params = cbParams;
// ?!? this fails: cbParams.Last ().ParameterType.Name == TypeManager.NSError
if (cbParams.Length > 0 && cbParams.Last ().ParameterType.Name == "NSError") {
has_nserror = true;
cbParams = Generator.DropLast (cbParams);
}
if (cbParams.Length == 0)
is_void_async = true;
if (cbParams.Length == 1)
is_single_arg_async = true;
}
public string GetUniqueParamName (string suggestion)
{
while (true) {
bool next = false;
foreach (var pi in async_completion_params) {
if (pi.Name == suggestion) {
next = true;
break;
}
}
if (!next)
return suggestion;
suggestion = "_" + suggestion;
}
}
}
public static T [] DropLast<T> (T [] arr)
{
T [] res = new T [arr.Length - 1];
Array.Copy (arr, res, res.Length);
return res;
}
string GetReturnType (AsyncMethodInfo minfo)
{
if (minfo.is_void_async)
if (minfo.IsVoidAsync)
return "Task";
var ttype = GetAsyncTaskType (minfo);
if (minfo.has_nserror && (ttype == "bool"))
if (minfo.HasNSError && (ttype == "bool"))
ttype = "Tuple<bool,NSError>";
return "Task<" + ttype + ">";
}
string GetAsyncTaskType (AsyncMethodInfo minfo)
{
if (minfo.is_single_arg_async)
return FormatType (minfo.type, minfo.async_completion_params [0].ParameterType);
if (minfo.IsSingleArgAsync)
return FormatType (minfo.type, minfo.AsyncCompletionParams [0].ParameterType);
var attr = AttributeManager.GetCustomAttribute<AsyncAttribute> (minfo.mi);
if (attr.ResultTypeName != null)
@ -4840,7 +4784,7 @@ public partial class Generator : IMemberGatherer {
minfo.GetVisibility (),
modifier,
GetReturnType (minfo),
MakeSignature (minfo, true, minfo.async_initial_params, extra),
MakeSignature (minfo, true, minfo.AsyncInitialParams, extra),
minfo.is_abstract ? ";" : "");
}
@ -4851,14 +4795,14 @@ public partial class Generator : IMemberGatherer {
var is_void = mi.ReturnType == TypeManager.System_Void;
// Print a error if any of the method parameters or handler parameters is ref/out, it should not be asyncified.
if (minfo.async_initial_params != null) {
foreach (var param in minfo.async_initial_params) {
if (minfo.AsyncInitialParams != null) {
foreach (var param in minfo.AsyncInitialParams) {
if (param.ParameterType.IsByRef) {
throw new BindingException (1062, true, original_minfo.type.Name, mi.Name);
}
}
}
foreach (var param in minfo.async_completion_params) {
foreach (var param in minfo.AsyncCompletionParams) {
if (param.ParameterType.IsByRef) {
throw new BindingException (1062, true, original_minfo.type.Name, mi.Name);
}
@ -4873,9 +4817,9 @@ public partial class Generator : IMemberGatherer {
var ttype = "bool";
var tuple = false;
if (!minfo.is_void_async) {
if (!minfo.IsVoidAsync) {
ttype = GetAsyncTaskType (minfo);
tuple = (minfo.has_nserror && (ttype == "bool"));
tuple = (minfo.HasNSError && (ttype == "bool"));
if (tuple)
ttype = "Tuple<bool,NSError>";
}
@ -4885,9 +4829,9 @@ public partial class Generator : IMemberGatherer {
AttributeManager.GetCustomAttribute<AsyncAttribute> (mi).PostNonResultSnippet == null;
print ("{6}{5}{4}{0}({1}{2}({3}) => {{",
mi.Name,
GetInvokeParamList (minfo.async_initial_params, false),
minfo.async_initial_params.Length > 0 ? ", " : "",
GetInvokeParamList (minfo.async_completion_params),
GetInvokeParamList (minfo.AsyncInitialParams, false),
minfo.AsyncInitialParams.Length > 0 ? ", " : "",
GetInvokeParamList (minfo.AsyncCompletionParams),
minfo.is_extension_method || minfo.is_category_extension ? "This." : string.Empty,
is_void || ignoreResult ? string.Empty : minfo.GetUniqueParamName ("result") + " = ",
is_void || ignoreResult ? string.Empty : (asyncKind == AsyncMethodKind.WithResultOutParameter ? string.Empty : "var ")
@ -4896,26 +4840,26 @@ public partial class Generator : IMemberGatherer {
indent++;
int nesting_level = 1;
if (minfo.has_nserror && !tuple) {
var var_name = minfo.async_completion_params.Last ().Name.GetSafeParamName (); ;
if (minfo.HasNSError && !tuple) {
var var_name = minfo.AsyncCompletionParams.Last ().Name.GetSafeParamName (); ;
print ("if ({0}_ != null)", var_name);
print ("\ttcs.SetException (new NSErrorException({0}_));", var_name);
print ("else");
++nesting_level; ++indent;
}
if (minfo.is_void_async)
if (minfo.IsVoidAsync)
print ("tcs.SetResult (true);");
else if (tuple) {
var cond_name = minfo.async_completion_params [0].Name;
var var_name = minfo.async_completion_params.Last ().Name;
var cond_name = minfo.AsyncCompletionParams [0].Name;
var var_name = minfo.AsyncCompletionParams.Last ().Name;
print ("tcs.SetResult (new Tuple<bool,NSError> ({0}_, {1}_));", cond_name, var_name);
} else if (minfo.is_single_arg_async)
print ("tcs.SetResult ({0}_!);", minfo.async_completion_params [0].Name);
} else if (minfo.IsSingleArgAsync)
print ("tcs.SetResult ({0}_!);", minfo.AsyncCompletionParams [0].Name);
else
print ("tcs.SetResult (new {0} ({1}));",
GetAsyncTaskType (minfo),
GetInvokeParamList (minfo.has_nserror ? DropLast (minfo.async_completion_params) : minfo.async_completion_params, true, true));
GetInvokeParamList (minfo.HasNSError ? minfo.AsyncCompletionParams.DropLast () : minfo.AsyncCompletionParams, true, true));
indent -= nesting_level;
if (is_void || ignoreResult)
print ("});");
@ -4930,10 +4874,10 @@ public partial class Generator : IMemberGatherer {
if (attr.ResultTypeName != null) {
if (minfo.has_nserror)
async_result_types.Add (new Tuple<string, ParameterInfo []> (attr.ResultTypeName, DropLast (minfo.async_completion_params)));
if (minfo.HasNSError)
async_result_types.Add (new Tuple<string, ParameterInfo []> (attr.ResultTypeName, minfo.AsyncCompletionParams.DropLast ()));
else
async_result_types.Add (new Tuple<string, ParameterInfo []> (attr.ResultTypeName, minfo.async_completion_params));
async_result_types.Add (new Tuple<string, ParameterInfo []> (attr.ResultTypeName, minfo.AsyncCompletionParams));
}
}

Просмотреть файл

@ -94,11 +94,13 @@
</None>
<Compile Include="..\src\error.cs" />
<Compile Include="..\src\ObjCRuntime\Stret.cs" />
<Compile Include="..\src\bgen\AsyncMethodInfo.cs" />
<Compile Include="..\src\bgen\AttributeConversionManager.cs" />
<Compile Include="..\src\bgen\AttributeManager.cs" />
<Compile Include="..\src\bgen\Attributes.cs" />
<Compile Include="..\src\bgen\AttributeFactory.cs" />
<Compile Include="..\src\bgen\BindingTouch.cs" />
<Compile Include="..\src\bgen\CollectionsExtensions.cs" />
<Compile Include="..\src\bgen\CustomAttributeDataExtensions.cs" />
<Compile Include="..\src\bgen\Enums.cs" />
<Compile Include="..\src\bgen\ExtensionMethods.cs" />

Просмотреть файл

@ -0,0 +1,23 @@
using System.Linq;
using NUnit.Framework;
namespace GeneratorTests {
[TestFixture]
public class CollectionsExtensionsTests {
[Test]
public void Yield ()
=> Assert.AreEqual (1, "test".Yield ().Count ());
[Test]
public void DropLast ()
{
var array = new [] { "first", "second", "last" };
var result = array.DropLast ();
Assert.AreEqual (array.Length - 1, result.Length, "Result Length");
Assert.False (result.Contains (array.Last ()), "Contains last item");
}
}
}

Просмотреть файл

@ -37,6 +37,7 @@
<PackageReference Include="MSBuild.StructuredLogger" Version="2.1.758" />
</ItemGroup>
<ItemGroup>
<Compile Include="CollectionsExtensionsTests.cs" />
<Compile Include="ErrorTests.cs" />
<Compile Include="BGenTool.cs" />
<Compile Include="..\common\ExecutionHelper.cs">