Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix trim warnings in 'AdaptiveFromAbiHelper' #1566

Merged
merged 4 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 74 additions & 12 deletions src/WinRT.Runtime/Projections/Bindable.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,11 @@ internal static class IBindableVectorView_Delegates
}

namespace ABI.System.Collections
{
{
using global::Microsoft.UI.Xaml.Interop;
using global::System;
using global::System;
using global::System.Diagnostics.CodeAnalysis;
using global::System.Reflection;
using global::System.Runtime.CompilerServices;

#if EMBED
Expand All @@ -396,22 +398,82 @@ internal unsafe interface IEnumerable : global::System.Collections.IEnumerable,
#pragma warning disable CA2257 // This member is a type (so it cannot be invoked)
public sealed class AdaptiveFromAbiHelper : FromAbiHelper, global::System.Collections.IEnumerable
#pragma warning restore CA2257
{
private readonly Func<IWinRTObject, global::System.Collections.IEnumerator> _enumerator;
{
/// <summary>
/// The cached <see cref="IEnumerable{T}.GetEnumerator"/> method.
/// </summary>
private static readonly MethodInfo EnumerableOfTGetEnumerator = typeof(IEnumerable<>).GetMethod("GetEnumerator");

#if NET8_0_OR_GREATER
private readonly MethodInvoker _enumerator;
#else
private readonly MethodInfo _enumerator;
#endif

public AdaptiveFromAbiHelper(Type runtimeType, IWinRTObject winRTObject)
:base(winRTObject)
{
Type enumGenericType = (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(global::System.Collections.Generic.IEnumerable<>)) ?
runtimeType : runtimeType.GetInterface("System.Collections.Generic.IEnumerable`1");
if(enumGenericType != null)
{
var getEnumerator = enumGenericType.GetMethod("GetEnumerator");
_enumerator = (IWinRTObject obj) => (global::System.Collections.IEnumerator)getEnumerator.Invoke(obj, null);
Type enumGenericType;

// First, look for and get the IEnumerable<> interface implemented by this type, if one exists. The scenario is, imagine you
// got an 'IList<string>' from somewhere, and then you use LINQ with it. What LINQ does is it converts both to object. Then
// it does a cast to 'IEnumerable'. At this point, you are trying an IDIC cast for 'IEnumerable' and asking the IDIC 'IEnumerable'
// interface to handle it. The question to answer is, what version of 'IEnumerable' is it. Is it the one provided by 'IList<string>'
// or is it the one that maps to 'IBindableIterable'. We actually don't know at this point which one to use, especially given the
// 'IBindableIterable' interface may not even be implemented on the native object, as it was an 'IList<string>' originally. This is
// also why we can't just check whether the object implements 'IEnumerable' and just call 'GetEnumerator()' on that.
if (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
enumGenericType = runtimeType;
}
else
{
[SuppressMessage("Trimming", "IL2070", Justification =
"""
'SomeType.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>)' is safe,
provided you obtained someType from something like an analyzable 'Type.GetType' or 'object.GetType'
(i.e. it is safe when the type you're asking about can exist on the GC heap as allocated).
""")]
[MethodImpl(MethodImplOptions.NoInlining)]
static Type GetEnumerableOfTInterface(Type runtimeType)
{
foreach (Type interfaceType in runtimeType.GetInterfaces())
{
if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
{
return interfaceType;
}
}

return null;
}

enumGenericType = GetEnumerableOfTInterface(runtimeType);
}

var methodInfo = (MethodInfo)enumGenericType?.GetMemberWithSameMetadataDefinitionAs(EnumerableOfTGetEnumerator);

#if NET8_0_OR_GREATER
_enumerator = methodInfo is null ? null : MethodInvoker.Create(methodInfo);
#else
_enumerator = methodInfo;
#endif
}

public override global::System.Collections.IEnumerator GetEnumerator() => _enumerator != null ? _enumerator(_winrtObject) : base.GetEnumerator();
public override IEnumerator GetEnumerator()
{
if (_enumerator is not null)
{
// The method returns IEnumerator<>, which implements IEnumerator
#if NET8_0_OR_GREATER
return Unsafe.As<IEnumerator>(_enumerator.Invoke(_winrtObject));
#else
return Unsafe.As<IEnumerator>(_enumerator.Invoke(_winrtObject, null));
#endif
}

return base.GetEnumerator();
}
}

#pragma warning disable CA2257 // This member is a type (so it cannot be invoked)
Expand Down Expand Up @@ -547,7 +609,7 @@ private static FromAbiHelper _AbiHelper(IWinRTObject _this)
internal
#else
public
#endif
#endif
static class IEnumerable_Delegates
{
public unsafe delegate int First_0(IntPtr thisPtr, IntPtr* result);
Expand Down
2 changes: 1 addition & 1 deletion src/WinRT.Runtime/TypeNameSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ private static Type FindTypeByNameCore(string runtimeClassName, Type[] genericTy
return null;

#if NET
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Calls to MakeGenericType are done with reference types")]
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Calls to MakeGenericType are done with reference types.")]
#endif
static Type ResolveGenericType(Type resolvedType, Type[] genericTypes, string runtimeClassName)
{
Expand Down