Skip to content

Commit

Permalink
Enable IsAotCompatible for CsWinRT, fix all AOT warnings (#1463)
Browse files Browse the repository at this point in the history
* Guard MGT(Type) calls on NativeAOT

* Set IsAotCompatible, treat AOT warnings as errors

* Add throw paths on AOT for MarshalNonBlittable<T>

* Make ManagedIPropertyValueImpl.UnboxValue AOT-safe

* Fix all AOT warnings in WinRT.Runtime

* Centralize [RDC] messages in TrimmingAttributeMessages

* Fail build for AOT warnings in authoring test project

* Replace Marshal.SizeOf<T>() with sizeof(T) on .NET 6+

* Address issues discovered after fixing AOT warnings (#1511)

* Fix build breaks from AOT warning fixes

* Improvements to GUID helper type lookup to avoid needing instantiated generic type

* Move around where helper type is initialized to handle more scenarios

* Remove commented code

* Fix IL2073 warning in 'FindHelperType'

* Centralize attribute constants in 'AttributeMessages'

* Fix nullable and IPropertySet scenarios (#1519)

* Experiment with changes to avoid nullable.value going down the helper type route

* Fix build

* Fix nullable structs and delegates

* Fix

* Fix tests

* Fix issue where the class implementing the interface can be trimmed and we rely on IDIC cast to create an instance of the interface.  But that doesn't work with the interface inherits generic interfaces on AOT.  This addresses that by making sure there is a fallback class available to use for the interface.

* Fix warning

* Improvements to nullable scenario (caching and removing old code paths)

* Move where we do generic type initialization for derived generic interfaces impl class

* Fix issue with Interface<> missing in impl classes by converting most of the remaining ones using it to the new format

* Add test

* PR feedback

* PR feedback for IID

---------

Co-authored-by: Manodasan Wignarajah <[email protected]>
  • Loading branch information
Sergio0694 and manodasanW authored Mar 13, 2024
1 parent 4638ab1 commit 9486d45
Show file tree
Hide file tree
Showing 45 changed files with 1,501 additions and 794 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PreprocessorDefinitions>X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>X64;NDEBUG;AOT;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
Expand Down
3 changes: 3 additions & 0 deletions src/Tests/AuthoringConsumptionTest/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ TEST(AuthoringTest, Arrays)
EXPECT_EQ(arr2[idx], idx + 1);
}

// Array marshaling on AOT needs dynamic code.
#ifndef AOT
std::array<BasicStruct, 2> basicStructArr;
basicStructArr[0] = basicClass.GetBasicStruct();
basicStructArr[1].X = 4;
Expand All @@ -272,6 +274,7 @@ TEST(AuthoringTest, Arrays)
EXPECT_EQ(result[1].X, basicStructArr[1].X);
EXPECT_EQ(result[1].Y, basicStructArr[1].Y);
EXPECT_EQ(result[1].Value, basicStructArr[1].Value);
#endif
}

TEST(AuthoringTest, CustomTypes)
Expand Down
10 changes: 8 additions & 2 deletions src/Tests/AuthoringTest/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@
it we can also further validate that the self-contained authoring scenario works correctly
-->
<PropertyGroup Condition="'$(Configuration)' == 'Release' and '$(Platform)' == 'x64'">
<IsAotCompatible>true</IsAotCompatible>
<PublishAot>true</PublishAot>
<NativeLib>Shared</NativeLib>
<SelfContained>true</SelfContained>

<!--
Treat all AOT warnings as errors. We want to make sure that we really are not producing any AOT
warnings anywhere (from ILC, not just from the analyzers) when publishing this project. This means
we must validate there's no AOT warnings coming from WinRT.Runtime nor from generated code here.
Keep in sync with: https://learn.microsoft.com/dotnet/core/deploying/native-aot/fixing-warnings.
-->
<WarningsAsErrors>$(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056</WarningsAsErrors>
</PropertyGroup>

</Project>
11 changes: 9 additions & 2 deletions src/Tests/FunctionalTests/JsonValueFunctionCalls/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Windows.Foundation;
using System;
using Windows.Foundation;

// Static function calls and create RCW for existing object.
IStringable[] a = new IStringable[] {
Expand All @@ -18,4 +19,10 @@
// Class function call
result += (int)(a[1] as Windows.Data.Json.JsonValue).GetNumber();

return result == 16 ? 100 : 101;
var enumVal = TestComponentCSharp.Class.BoxedEnum;
if (enumVal is TestComponentCSharp.EnumValue val && val == TestComponentCSharp.EnumValue.Two)
{
result += 1;
}

return result == 17 ? 100 : 101;
5 changes: 5 additions & 0 deletions src/Tests/TestComponentCSharp/TestComponentCSharp.idl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ namespace TestComponentCSharp
static Int32 NumObjects{ get; };
}

interface IDerivedGenericInterface requires Microsoft.UI.Xaml.Input.ICommand, Microsoft.UI.Xaml.Interop.INotifyCollectionChanged, Windows.Foundation.Collections.IPropertySet
{
Int32 Number;
}

interface ISingleton
{
Int32 IntProperty;
Expand Down
5 changes: 5 additions & 0 deletions src/WinRT.Runtime/AttributeMessages.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,10 @@ internal static class AttributeMessages
/// Message for a generic deprecated message that's annotated with <see cref="System.ObsoleteAttribute"/>.
/// </summary>
public const string GenericDeprecatedMessage = "This method is deprecated and will be removed in a future release.";

/// <summary>
/// Message for marshalling or generic code requiring <see cref="System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute"/>.
/// </summary>
public const string MarshallingOrGenericInstantiationsRequiresDynamicCode = "The necessary marshalling code or generic instantiations might not be available.";
}
}
98 changes: 68 additions & 30 deletions src/WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,15 @@ internal static ObjectReference<T> GetObjectReferenceForInterface<T>(IntPtr exte

public static void RegisterAuthoringMetadataTypeLookup(Func<Type, Type> authoringMetadataTypeLookup) => TypeExtensions.RegisterAuthoringMetadataTypeLookup(authoringMetadataTypeLookup);

public static void RegisterHelperType(
Type type,
#if NET
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.PublicNestedTypes |
DynamicallyAccessedMemberTypes.PublicFields)]
#endif
Type helperType) => TypeExtensions.HelperTypeCache.TryAdd(type, helperType);

internal static List<ComInterfaceEntry> GetInterfaceTableEntries(Type type)
{
var entries = new List<ComInterfaceEntry>();
Expand Down Expand Up @@ -249,7 +258,9 @@ static bool GetHasCustomIMarshalInterface(List<ComInterfaceEntry> entries)
}

if (iface.IsConstructedGenericType
#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
&& Projections.TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, null, out var compatibleIfaces))
#pragma warning restore IL3050
{
foreach (var compatibleIface in compatibleIfaces)
{
Expand Down Expand Up @@ -433,13 +444,13 @@ private static Func<IntPtr, object> CreateDelegateFactory(Type type)

public static bool RegisterDelegateFactory(Type implementationType, Func<IntPtr, object> delegateFactory) => DelegateFactoryCache.TryAdd(implementationType, delegateFactory);

private static Func<IInspectable, object> CreateNullableTFactory(Type implementationType)
internal static Func<IInspectable, object> CreateNullableTFactory(Type implementationType)
{
var getValueMethod = implementationType.GetHelperType().GetMethod("GetValue", BindingFlags.Static | BindingFlags.Public);
return (IInspectable obj) => getValueMethod.Invoke(null, new[] { obj });
}

private static Func<IInspectable, object> CreateAbiNullableTFactory(
internal static Func<IInspectable, object> CreateAbiNullableTFactory(
#if NET
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
#endif
Expand All @@ -465,7 +476,7 @@ private static Func<IInspectable, object> CreateArrayFactory(Type implementation
// This is done to avoid pointer reuse until GC cleans up the boxed object
private static readonly ConditionalWeakTable<object, IInspectable> _boxedValueReferenceCache = new();

private static Func<IInspectable, object> CreateReferenceCachingFactory(Func<IInspectable, object> internalFactory)
internal static Func<IInspectable, object> CreateReferenceCachingFactory(Func<IInspectable, object> internalFactory)
{
return inspectable =>
{
Expand Down Expand Up @@ -502,46 +513,37 @@ internal static Func<IInspectable, object> CreateTypedRcwFactory(Type implementa
return (IInspectable obj) => obj;
}

if (implementationType == typeof(ABI.System.Nullable_string))
if (implementationType == typeof(string) ||
implementationType == typeof(Type) ||
implementationType == typeof(Exception) ||
implementationType.IsDelegate())
{
return CreateReferenceCachingFactory(ABI.System.Nullable_string.GetValue);
}
else if (implementationType == typeof(ABI.System.Nullable_Type))
{
return CreateReferenceCachingFactory(ABI.System.Nullable_Type.GetValue);
}
else if (implementationType == typeof(ABI.System.Nullable_Exception))
{
return CreateReferenceCachingFactory(ABI.System.Nullable_Exception.GetValue);
}

var customHelperType = Projections.FindCustomHelperTypeMapping(implementationType, true);
if (customHelperType != null)
{
return CreateReferenceCachingFactory(CreateCustomTypeMappingFactory(customHelperType));
}

if (implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>))
{
return CreateReferenceCachingFactory(CreateKeyValuePairFactory(implementationType));
return ABI.System.NullableType.GetValueFactory(implementationType);
}

if (implementationType.IsValueType)
{
if (implementationType.IsNullableT())
if (implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>))
{
return CreateReferenceCachingFactory(CreateNullableTFactory(implementationType));
return CreateReferenceCachingFactory(CreateKeyValuePairFactory(implementationType));
}
else if (implementationType.IsNullableT())
{
return ABI.System.NullableType.GetValueFactory(implementationType.GetGenericArguments()[0]);
}
else
{
return CreateReferenceCachingFactory(CreateNullableTFactory(typeof(System.Nullable<>).MakeGenericType(implementationType)));
return ABI.System.NullableType.GetValueFactory(implementationType);
}
}
else if (implementationType.IsAbiNullableDelegate())

var customHelperType = Projections.FindCustomHelperTypeMapping(implementationType, true);
if (customHelperType != null)
{
return CreateReferenceCachingFactory(CreateAbiNullableTFactory(implementationType));
return CreateReferenceCachingFactory(CreateCustomTypeMappingFactory(customHelperType));
}
else if (implementationType.IsIReferenceArray())

if (implementationType.IsIReferenceArray())
{
return CreateReferenceCachingFactory(CreateArrayFactory(implementationType));
}
Expand All @@ -555,6 +557,14 @@ internal static Type GetRuntimeClassForTypeCreation(IInspectable inspectable, Ty
Type implementationType = null;
if (!string.IsNullOrEmpty(runtimeClassName))
{
// Check if this is a nullable type where there are no references to the nullable version, but
// there is to the actual type.
if (runtimeClassName.StartsWith("Windows.Foundation.IReference`1<", StringComparison.Ordinal))
{
// runtimeClassName is of format Windows.Foundation.IReference`1<type>.
return TypeNameSupport.FindRcwTypeByNameCached(runtimeClassName.Substring(32, runtimeClassName.Length - 33));
}

implementationType = TypeNameSupport.FindRcwTypeByNameCached(runtimeClassName);
}

Expand Down Expand Up @@ -778,12 +788,21 @@ private static ComInterfaceEntry ProvideIReference(Type type)
}
if (type.IsDelegate())
{
#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException($"Cannot provide IReference`1 support for delegate type '{type}'.");
}
#endif

#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
var delegateHelperType = typeof(ABI.System.Nullable_Delegate<>).MakeGenericType(type);
return new ComInterfaceEntry
{
IID = global::WinRT.GuidGenerator.GetIID(delegateHelperType),
Vtable = delegateHelperType.GetAbiToProjectionVftblPtr()
};
#pragma warning restore IL3050
}
if (type == typeof(System.Numerics.Matrix3x2))
{
Expand Down Expand Up @@ -850,11 +869,20 @@ private static ComInterfaceEntry ProvideIReference(Type type)
};
}

#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException($"Cannot provide IReference`1 support for type '{type}'.");
}
#endif

#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
return new ComInterfaceEntry
{
IID = global::WinRT.GuidGenerator.GetIID(typeof(ABI.System.Nullable<>).MakeGenericType(type)),
Vtable = typeof(BoxedValueIReferenceImpl<>).MakeGenericType(type).GetAbiToProjectionVftblPtr()
};
#pragma warning restore IL3050
}

private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType)
Expand Down Expand Up @@ -1060,11 +1088,21 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType)
Vtable = BoxedArrayIReferenceArrayImpl<System.Exception>.AbiToProjectionVftablePtr
};
}

#if NET
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException($"Cannot provide IReferenceArray`1 support for element type '{type}'.");
}
#endif

#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
return new ComInterfaceEntry
{
IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray<>).MakeGenericType(type)),
Vtable = (IntPtr)typeof(BoxedArrayIReferenceArrayImpl<>).MakeGenericType(type).GetAbiToProjectionVftblPtr()
};
#pragma warning restore IL3050
}

internal sealed class InspectableInfo
Expand Down
10 changes: 10 additions & 0 deletions src/WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,14 @@ private static Func<IInspectable, object> CreateFactoryForImplementationType(str
{
if (implementationType.IsGenericType)
{
if (!RuntimeFeature.IsDynamicCodeCompiled)
{
throw new NotSupportedException($"Cannot create an RCW factory for implementation type '{implementationType}'.");
}

#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273
Type genericImplType = GetGenericImplType(implementationType);
#pragma warning restore IL3050
if (genericImplType != null)
{
var createRcw = genericImplType.GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(IInspectable) }, null);
Expand Down Expand Up @@ -252,6 +259,9 @@ static Func<IInspectable, object> CreateRcwFallback(Type implementationType)
return (IInspectable obj) => constructor.Invoke(new[] { obj.ObjRef });
}

#if NET8_0_OR_GREATER
[RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)]
#endif
[return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
static Type GetGenericImplType(Type implementationType)
{
Expand Down
6 changes: 4 additions & 2 deletions src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ static partial class ComWrappersSupport
public static T CreateRcwForComObject<T>(IntPtr ptr)
{
return CreateRcwForComObject<T>(ptr, true);
}
}

internal static Func<IInspectable, object> GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, classType => CreateTypedRcwFactory(classType));

private static T CreateRcwForComObject<T>(IntPtr ptr, bool tryUseCache)
{
Expand All @@ -56,7 +58,7 @@ private static T CreateRcwForComObject<T>(IntPtr ptr, bool tryUseCache)
if (typeof(T).IsSealed)
{
runtimeWrapper = TypedObjectFactoryCacheForType.GetOrAdd(typeof(T), classType => CreateTypedRcwFactory(classType))(inspectable);
runtimeWrapper = GetTypedRcwFactory(typeof(T))(inspectable);
}
else
{
Expand Down
Loading

0 comments on commit 9486d45

Please sign in to comment.