From bfe063c845329f21e452b84dcaccf834d516eccd Mon Sep 17 00:00:00 2001 From: Nick Stanton <nickstanton@microsoft.com> Date: Tue, 13 Dec 2022 11:28:59 -0700 Subject: [PATCH 1/4] Add warnings and annotations --- .../Components/src/Reflection/PropertySetter.cs | 4 ++++ src/Http/Routing/src/Matching/ILEmitTrieFactory.cs | 3 +++ src/Http/Routing/src/Matching/ILEmitTrieJumpTable.cs | 3 +++ src/Http/Routing/src/Matching/JumpTableBuilder.cs | 2 ++ .../src/Infrastructure/DotNetDispatcher.cs | 3 +++ .../DotNetObjectReferenceJsonConverterFactory.cs | 1 + .../src/Infrastructure/TaskGenericsUtil.cs | 7 +++++++ src/Shared/PropertyHelper/PropertyHelper.cs | 9 +++++++++ 8 files changed, 32 insertions(+) diff --git a/src/Components/Components/src/Reflection/PropertySetter.cs b/src/Components/Components/src/Reflection/PropertySetter.cs index a79c71ccbaf0..c65bea3231db 100644 --- a/src/Components/Components/src/Reflection/PropertySetter.cs +++ b/src/Components/Components/src/Reflection/PropertySetter.cs @@ -17,6 +17,10 @@ internal sealed class PropertySetter "ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The referenced methods don't have any DynamicallyAccessedMembers annotations. See https://github.com/mono/linker/issues/1727")] + [SuppressMessage( + "AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "The referenced methods are AOT safe with reference types.")] public PropertySetter(Type targetType, PropertyInfo property) { if (property.SetMethod == null) diff --git a/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs b/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs index 52af625d63c8..01f40f130c79 100644 --- a/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs +++ b/src/Http/Routing/src/Matching/ILEmitTrieFactory.cs @@ -4,6 +4,7 @@ #nullable disable using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -12,6 +13,7 @@ namespace Microsoft.AspNetCore.Routing.Matching; +[RequiresDynamicCode("ILEmitTrieFactory uses runtime IL generation.")] internal static class ILEmitTrieFactory { // The algorthm we use only works for ASCII text. If we find non-ASCII text in the input @@ -477,6 +479,7 @@ private sealed class Labels public Label ReturnNotAscii { get; set; } } + [RequiresDynamicCode("ILEmitTrieFactory uses runtime IL generation.")] private sealed class Methods { // Caching because the methods won't change, if we're being called once we're likely to diff --git a/src/Http/Routing/src/Matching/ILEmitTrieJumpTable.cs b/src/Http/Routing/src/Matching/ILEmitTrieJumpTable.cs index f382c2d562b7..b0e975d7fdc1 100644 --- a/src/Http/Routing/src/Matching/ILEmitTrieJumpTable.cs +++ b/src/Http/Routing/src/Matching/ILEmitTrieJumpTable.cs @@ -3,12 +3,15 @@ #nullable disable +using System.Diagnostics.CodeAnalysis; + namespace Microsoft.AspNetCore.Routing.Matching; // Uses generated IL to implement the JumpTable contract. This approach requires // a fallback jump table for two reasons: // 1. We compute the IL lazily to avoid taking up significant time when processing a request // 2. The generated IL only supports ASCII in the URL path +[RequiresDynamicCode("ILEmitTrieJumpTable uses runtime IL generation.")] internal sealed class ILEmitTrieJumpTable : JumpTable { private readonly int _defaultDestination; diff --git a/src/Http/Routing/src/Matching/JumpTableBuilder.cs b/src/Http/Routing/src/Matching/JumpTableBuilder.cs index 4ead4b3bb889..0c48d3b261c2 100644 --- a/src/Http/Routing/src/Matching/JumpTableBuilder.cs +++ b/src/Http/Routing/src/Matching/JumpTableBuilder.cs @@ -87,7 +87,9 @@ public static JumpTable Build(int defaultDestination, int exitDestination, (stri // Use the ILEmitTrieJumpTable if the IL is going to be compiled (not interpreted) if (RuntimeFeature.IsDynamicCodeCompiled) { +#pragma warning disable IL3050 // See https://github.com/dotnet/linker/issues/2715. return new ILEmitTrieJumpTable(defaultDestination, exitDestination, pathEntries, vectorize: null, fallback); +#pragma warning restore IL3050 } return fallback; diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index 5640fce75a5f..f24a1afb1578 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -375,6 +375,9 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, "ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "https://github.com/mono/linker/issues/1727")] + [SuppressMessage("AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "Methods being referenced do not have DynamicallyAccessedMembers.")] private static Task GetTaskByType(Type type, object obj) { var converterDelegate = _cachedConvertToTaskByType.GetOrAdd(type, (Type t, MethodInfo taskConverterMethodInfo) => diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs index b769b949d974..c7808a0fa145 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs @@ -22,6 +22,7 @@ public override bool CanConvert(Type typeToConvert) } [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "We expect that types used with DotNetObjectReference are retained.")] + [SuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "MakeGenericType is AOT safe for reference types.")] public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) { // System.Text.Json handles caching the converters per type on our behalf. No caching is required here. diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs index a03d87536db1..fc09ebf314c4 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Microsoft.JSInterop.Infrastructure; @@ -23,6 +24,9 @@ public static void SetTaskCompletionSourceException(object taskCompletionSource, public static Type GetTaskCompletionSourceResultType(object taskCompletionSource) => CreateResultSetter(taskCompletionSource).ResultType; + [SuppressMessage("AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "MakeGenericType is AOT safe for reference types.")] public static object? GetTaskResult(Task task) { var getter = _cachedResultGetters.GetOrAdd(task.GetType(), taskInstanceType => @@ -101,6 +105,9 @@ public void SetException(object tcs, Exception exception) } } + [SuppressMessage("AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "MakeGenericType is AOT safe for reference types.")] private static ITcsResultSetter CreateResultSetter(object taskCompletionSource) { return _cachedResultSetters.GetOrAdd(taskCompletionSource.GetType(), tcsType => diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index f0301fe7fcc0..44e23a3149a3 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -151,6 +151,7 @@ public static PropertyHelper[] GetProperties( /// A cached array of all public properties of the specified type. /// </returns> [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { @@ -167,6 +168,7 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static Func<object, object?> MakeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -187,6 +189,7 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static Func<object, object?> MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -198,6 +201,7 @@ public static PropertyHelper[] GetVisibleProperties( } [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API is not trim safe.")] private static Func<object, object?> MakeFastPropertyGetter( PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, @@ -242,6 +246,7 @@ public static PropertyHelper[] GetVisibleProperties( } [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API is not trim safe.")] private static Func<object, object?> MakeFastPropertyGetter( Type openGenericDelegateType, MethodInfo propertyGetMethod, @@ -271,6 +276,7 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. This only works for reference types. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static Action<object, object?> MakeFastPropertySetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -313,6 +319,7 @@ public static PropertyHelper[] GetVisibleProperties( /// faster when the same type is used multiple times with ObjectToDictionary. /// </remarks> [RequiresUnreferencedCode("Method uses reflection to generate the dictionary.")] + [RequiresDynamicCode("This API is not trim safe.")] public static IDictionary<string, object?> ObjectToDictionary(object? value) { if (value is IDictionary<string, object?> dictionary) @@ -402,6 +409,7 @@ private static void CallPropertySetter<TDeclaringType, TValue>( /// A cached array of all public properties of the specified type. /// </returns> [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( Type type, ConcurrentDictionary<Type, PropertyHelper[]>? allPropertiesCache, @@ -483,6 +491,7 @@ public static PropertyHelper[] GetVisibleProperties( /// </returns> // There isn't a way to represent trimmability requirements since for type since we unwrap nullable types. [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetProperties( Type type, ConcurrentDictionary<Type, PropertyHelper[]>? cache) From af375813bcae777994a8a9acf7bf3f09a933472b Mon Sep 17 00:00:00 2001 From: Nick Stanton <nickstanton@microsoft.com> Date: Tue, 20 Dec 2022 14:24:27 -0700 Subject: [PATCH 2/4] revert PropertyHelper changes --- src/Shared/PropertyHelper/PropertyHelper.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index 44e23a3149a3..f0301fe7fcc0 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -151,7 +151,6 @@ public static PropertyHelper[] GetProperties( /// A cached array of all public properties of the specified type. /// </returns> [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { @@ -168,7 +167,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static Func<object, object?> MakeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -189,7 +187,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static Func<object, object?> MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -201,7 +198,6 @@ public static PropertyHelper[] GetVisibleProperties( } [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API is not trim safe.")] private static Func<object, object?> MakeFastPropertyGetter( PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, @@ -246,7 +242,6 @@ public static PropertyHelper[] GetVisibleProperties( } [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API is not trim safe.")] private static Func<object, object?> MakeFastPropertyGetter( Type openGenericDelegateType, MethodInfo propertyGetMethod, @@ -276,7 +271,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. This only works for reference types. /// </remarks> [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static Action<object, object?> MakeFastPropertySetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -319,7 +313,6 @@ public static PropertyHelper[] GetVisibleProperties( /// faster when the same type is used multiple times with ObjectToDictionary. /// </remarks> [RequiresUnreferencedCode("Method uses reflection to generate the dictionary.")] - [RequiresDynamicCode("This API is not trim safe.")] public static IDictionary<string, object?> ObjectToDictionary(object? value) { if (value is IDictionary<string, object?> dictionary) @@ -409,7 +402,6 @@ private static void CallPropertySetter<TDeclaringType, TValue>( /// A cached array of all public properties of the specified type. /// </returns> [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( Type type, ConcurrentDictionary<Type, PropertyHelper[]>? allPropertiesCache, @@ -491,7 +483,6 @@ public static PropertyHelper[] GetVisibleProperties( /// </returns> // There isn't a way to represent trimmability requirements since for type since we unwrap nullable types. [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API is not trim safe.")] public static PropertyHelper[] GetProperties( Type type, ConcurrentDictionary<Type, PropertyHelper[]>? cache) From 8b265e7e008f3431a1a5ea4bc1486435e3cb094f Mon Sep 17 00:00:00 2001 From: Nick Stanton <nickstanton@microsoft.com> Date: Wed, 21 Dec 2022 14:09:17 -0700 Subject: [PATCH 3/4] Revert JSInterop changes --- .../src/Infrastructure/DotNetDispatcher.cs | 3 --- .../DotNetObjectReferenceJsonConverterFactory.cs | 1 - .../src/Infrastructure/TaskGenericsUtil.cs | 7 ------- 3 files changed, 11 deletions(-) diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs index f24a1afb1578..5640fce75a5f 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs @@ -375,9 +375,6 @@ private static (MethodInfo, Type[]) GetCachedMethodInfo(AssemblyKey assemblyKey, "ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "https://github.com/mono/linker/issues/1727")] - [SuppressMessage("AOT", - "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", - Justification = "Methods being referenced do not have DynamicallyAccessedMembers.")] private static Task GetTaskByType(Type type, object obj) { var converterDelegate = _cachedConvertToTaskByType.GetOrAdd(type, (Type t, MethodInfo taskConverterMethodInfo) => diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs index c7808a0fa145..b769b949d974 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetObjectReferenceJsonConverterFactory.cs @@ -22,7 +22,6 @@ public override bool CanConvert(Type typeToConvert) } [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "We expect that types used with DotNetObjectReference are retained.")] - [SuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "MakeGenericType is AOT safe for reference types.")] public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) { // System.Text.Json handles caching the converters per type on our behalf. No caching is required here. diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs index fc09ebf314c4..a03d87536db1 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/TaskGenericsUtil.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace Microsoft.JSInterop.Infrastructure; @@ -24,9 +23,6 @@ public static void SetTaskCompletionSourceException(object taskCompletionSource, public static Type GetTaskCompletionSourceResultType(object taskCompletionSource) => CreateResultSetter(taskCompletionSource).ResultType; - [SuppressMessage("AOT", - "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", - Justification = "MakeGenericType is AOT safe for reference types.")] public static object? GetTaskResult(Task task) { var getter = _cachedResultGetters.GetOrAdd(task.GetType(), taskInstanceType => @@ -105,9 +101,6 @@ public void SetException(object tcs, Exception exception) } } - [SuppressMessage("AOT", - "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", - Justification = "MakeGenericType is AOT safe for reference types.")] private static ITcsResultSetter CreateResultSetter(object taskCompletionSource) { return _cachedResultSetters.GetOrAdd(taskCompletionSource.GetType(), tcsType => From 757c43caa4c7838bc6f47721b5426d0d9fc6225d Mon Sep 17 00:00:00 2001 From: Nick Stanton <nickstanton@microsoft.com> Date: Wed, 28 Dec 2022 12:44:19 -0700 Subject: [PATCH 4/4] revert PropertySetter changes --- src/Components/Components/src/Reflection/PropertySetter.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Components/Components/src/Reflection/PropertySetter.cs b/src/Components/Components/src/Reflection/PropertySetter.cs index c65bea3231db..a79c71ccbaf0 100644 --- a/src/Components/Components/src/Reflection/PropertySetter.cs +++ b/src/Components/Components/src/Reflection/PropertySetter.cs @@ -17,10 +17,6 @@ internal sealed class PropertySetter "ReflectionAnalysis", "IL2060:MakeGenericMethod", Justification = "The referenced methods don't have any DynamicallyAccessedMembers annotations. See https://github.com/mono/linker/issues/1727")] - [SuppressMessage( - "AOT", - "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", - Justification = "The referenced methods are AOT safe with reference types.")] public PropertySetter(Type targetType, PropertyInfo property) { if (property.SetMethod == null)