From a4fdc61dafc7227912713f5e01674007cec64e79 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 08:41:05 -0800 Subject: [PATCH 01/45] Enable AOT warnings --- Directory.Build.targets | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 4bb0bb189ccd..4d830493b705 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -3,13 +3,15 @@ true + $([MSBuild]::ValueOrDefault($(IsTrimmable),'false')) + ))] static Dictionary? DeserializeErrors(ref Utf8JsonReader reader, JsonSerializerOptions options) => JsonSerializer.Deserialize>(ref reader, options); From f0b1c81b671279a86aaf46ad0dfe3832192d8115 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 09:10:05 -0800 Subject: [PATCH 03/45] Simplify annotations on PropertyHelper --- src/Shared/PropertyHelper/PropertyHelper.cs | 26 ++++++--------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index f0301fe7fcc0..115616af3d0f 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -13,6 +13,8 @@ namespace Microsoft.Extensions.Internal; +[RequiresUnreferencedCode("This type reflects over the properties of types (and, notably, the properties of interfaces they implement) and so is trim-incompatible")] +[RequiresDynamicCode("This type synthesizes delegate types with (possible) value-type parameters and so is AOT-incompatible")] internal sealed class PropertyHelper { private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; @@ -71,7 +73,6 @@ public PropertyHelper(PropertyInfo property) /// public Func ValueGetter { - [RequiresUnreferencedCode("This API is not trim safe.")] get { if (_valueGetter == null) @@ -88,7 +89,6 @@ public PropertyHelper(PropertyInfo property) /// public Action ValueSetter { - [RequiresUnreferencedCode("This API is not trim safe.")] get { if (_valueSetter == null) @@ -105,7 +105,6 @@ public PropertyHelper(PropertyInfo property) /// /// The object whose property value will be returned. /// The property value. - [RequiresUnreferencedCode("This API is not trim safe.")] public object? GetValue(object instance) { return ValueGetter(instance); @@ -116,7 +115,6 @@ public PropertyHelper(PropertyInfo property) /// /// The object whose property value will be set. /// The property value. - [RequiresUnreferencedCode("This API is not trim safe.")] public void SetValue(object instance, object? value) { ValueSetter(instance, value); @@ -129,9 +127,7 @@ public void SetValue(object instance, object? value) /// The type to extract property accessors for. /// A cached array of all public properties of the specified type. /// - [RequiresUnreferencedCode("This API is not trim safe.")] - public static PropertyHelper[] GetProperties( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) + public static PropertyHelper[] GetProperties(Type type) { return GetProperties(type, PropertiesCache); } @@ -150,9 +146,7 @@ public static PropertyHelper[] GetProperties( /// /// A cached array of all public properties of the specified type. /// - [RequiresUnreferencedCode("This API is not trim safe.")] - public static PropertyHelper[] GetVisibleProperties( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) + public static PropertyHelper[] GetVisibleProperties(Type type) { return GetVisibleProperties(type, PropertiesCache, VisiblePropertiesCache); } @@ -166,7 +160,6 @@ public static PropertyHelper[] GetVisibleProperties( /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. /// - [RequiresUnreferencedCode("This API is not trimmer safe.")] public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -186,7 +179,6 @@ public static PropertyHelper[] GetVisibleProperties( /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. /// - [RequiresUnreferencedCode("This API is not trimmer safe.")] public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -197,7 +189,6 @@ public static PropertyHelper[] GetVisibleProperties( CallNullSafePropertyGetterByReferenceOpenGenericMethod); } - [RequiresUnreferencedCode("This API is not trimmer safe.")] private static Func MakeFastPropertyGetter( PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, @@ -241,7 +232,6 @@ public static PropertyHelper[] GetVisibleProperties( } } - [RequiresUnreferencedCode("This API is not trimmer safe.")] private static Func MakeFastPropertyGetter( Type openGenericDelegateType, MethodInfo propertyGetMethod, @@ -270,7 +260,6 @@ public static PropertyHelper[] GetVisibleProperties( /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. This only works for reference types. /// - [RequiresUnreferencedCode("This API is not trimmer safe.")] public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -312,7 +301,6 @@ public static PropertyHelper[] GetVisibleProperties( /// The implementation of PropertyHelper will cache the property accessors per-type. This is /// faster when the same type is used multiple times with ObjectToDictionary. /// - [RequiresUnreferencedCode("Method uses reflection to generate the dictionary.")] public static IDictionary ObjectToDictionary(object? value) { if (value is IDictionary dictionary) @@ -401,7 +389,6 @@ private static void CallPropertySetter( /// /// A cached array of all public properties of the specified type. /// - [RequiresUnreferencedCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( Type type, ConcurrentDictionary? allPropertiesCache, @@ -482,7 +469,6 @@ public static PropertyHelper[] GetVisibleProperties( /// A cached array of all public properties of the specified type. /// // There isn't a way to represent trimmability requirements since for type since we unwrap nullable types. - [RequiresUnreferencedCode("This API is not trim safe.")] public static PropertyHelper[] GetProperties( Type type, ConcurrentDictionary? cache) @@ -514,7 +500,7 @@ public static PropertyHelper[] GetProperties( static void AddInterestingProperties( List propertyHelpers, - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) + Type type) { foreach (var property in type.GetProperties(Everything)) { @@ -557,6 +543,8 @@ private static bool IsRefStructProperty(PropertyInfo property) property.PropertyType.IsDefined(IsByRefLikeAttribute); } + [RequiresUnreferencedCode("A wrapper over PropertyHelper, which is not time-compatible")] + [RequiresDynamicCode("A wrapper over PropertyHelper, which is not AOT-compatible")] internal static class MetadataUpdateHandler { /// From 68229ac2f7017e798ba2e1ad5d8e916aa526276c Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 12:31:34 -0800 Subject: [PATCH 04/45] Annotate ProblemDetailJsonConverters --- .../HttpValidationProblemDetailsJsonConverter.cs | 11 ++++------- .../ProblemDetails/ProblemDetailsJsonConverter.cs | 7 +++---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs index 34ff17e844e4..9eae00e1e35a 100644 --- a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs @@ -7,18 +7,19 @@ namespace Microsoft.AspNetCore.Http; +// TODO (acasey): identify and flag consumers +[RequiresUnreferencedCode("This API is not trim safe - from ProblemDetailsJsonConverter and JsonSerializer.")] +[RequiresDynamicCode("This API is not AOT safe - from ProblemDetailsJsonConverter and JsonSerializer.")] internal sealed class HttpValidationProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Errors = JsonEncodedText.Encode("errors"); - [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "Trimmer does not allow annotating overriden methods with annotations different from the ones in base type.")] public override HttpValidationProblemDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var problemDetails = new HttpValidationProblemDetails(); return ReadProblemDetails(ref reader, options, problemDetails); } - [RequiresUnreferencedCode("JSON serialization and deserialization ProblemDetails.Extensions might require types that cannot be statically analyzed. ")] public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader reader, JsonSerializerOptions options, HttpValidationProblemDetails problemDetails) { if (reader.TokenType != JsonTokenType.StartObject) @@ -53,19 +54,17 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader return problemDetails; [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "We ensure Dictionary is preserved.")] - [UnconditionalSuppressMessage("Trimmer", "IL3050", Justification = "We ensure Dictionary is preserved.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "We ensure Dictionary is preserved and the type arguments are reference types.")] [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties, typeof(Dictionary))] static Dictionary? DeserializeErrors(ref Utf8JsonReader reader, JsonSerializerOptions options) => JsonSerializer.Deserialize>(ref reader, options); } - [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "Trimmer does not allow annotating overriden methods with annotations different from the ones in base type.")] public override void Write(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { WriteProblemDetails(writer, value, options); } - [RequiresUnreferencedCode("JSON serialization and deserialization ProblemDetails.Extensions might require types that cannot be statically analyzed. ")] public static void WriteProblemDetails(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { writer.WriteStartObject(); @@ -76,8 +75,6 @@ public static void WriteProblemDetails(Utf8JsonWriter writer, HttpValidationProb writer.WriteEndObject(); - [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "We ensure IDictionary is preserved.")] - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(IDictionary))] static void SerializeErrors(Utf8JsonWriter writer, IDictionary errors, JsonSerializerOptions options) => JsonSerializer.Serialize(writer, errors, options); } diff --git a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs index 28369eb5e0a2..799dbd8a7ac4 100644 --- a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs @@ -8,6 +8,9 @@ namespace Microsoft.AspNetCore.Http; +// TODO (acasey): identify and flag consumers +[RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] +[RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] internal sealed class ProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Type = JsonEncodedText.Encode("type"); @@ -16,7 +19,6 @@ internal sealed class ProblemDetailsJsonConverter : JsonConverter Date: Mon, 12 Dec 2022 13:27:25 -0800 Subject: [PATCH 05/45] Fix whitespace --- src/Shared/PropertyHelper/PropertyHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index 115616af3d0f..097267ef0431 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Internal; [RequiresUnreferencedCode("This type reflects over the properties of types (and, notably, the properties of interfaces they implement) and so is trim-incompatible")] -[RequiresDynamicCode("This type synthesizes delegate types with (possible) value-type parameters and so is AOT-incompatible")] +[RequiresDynamicCode("This type synthesizes delegate types with (possible) value-type parameters and so is AOT-incompatible")] internal sealed class PropertyHelper { private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; From cb77b1b3ea87a41d9a17a2a1b9a0266f94f5bb8a Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 13:29:12 -0800 Subject: [PATCH 06/45] Annotate RouteValueDictionary --- .../Http.Abstractions/src/Routing/RouteValueDictionary.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index 03982e3d303c..b351e50956a0 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -100,7 +100,8 @@ public RouteValueDictionary() /// property names are keys, and property values are the values, and copied into the dictionary. /// Only public instance non-index properties are considered. /// - [RequiresUnreferencedCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] + [RequiresUnreferencedCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] // PropertyStorage + [RequiresDynamicCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] // PropertyStorage public RouteValueDictionary(object? values) { if (values is RouteValueDictionary dictionary) @@ -818,12 +819,13 @@ public void Reset() } } + [RequiresUnreferencedCode("This API is not trim safe - from PropertyHelper")] + [RequiresDynamicCode("This API is not AOT safe - from PropertyHelper")] internal sealed class PropertyStorage { public readonly object Value; public readonly PropertyHelper[] Properties; - [RequiresUnreferencedCode("This API is not trim safe.")] public PropertyStorage(object value) { Debug.Assert(value != null); From de06e3907a119e2a2a0f719e04350d48600c16cc Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 13:29:28 -0800 Subject: [PATCH 07/45] Guess suppression for WebApplicationBuilder --- .../src/WebApplicationBuilder.cs | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 3da1ac4e83a7..3b8bc9117f27 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; @@ -29,18 +30,7 @@ public sealed class WebApplicationBuilder internal WebApplicationBuilder(WebApplicationOptions options, Action? configureDefaults = null) { - var configuration = new ConfigurationManager(); - - configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_"); - - _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings - { - Args = options.Args, - ApplicationName = options.ApplicationName, - EnvironmentName = options.EnvironmentName, - ContentRootPath = options.ContentRootPath, - Configuration = configuration, - }); + _hostApplicationBuilder = MakeHostApplicationBuilder(options); // Set WebRootPath if necessary if (options.WebRootPath is not null) @@ -86,6 +76,24 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action /// Provides information about the web hosting environment an application is running. /// From 54c72633b5183a11e8319933e29da90e75dcb912 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 13:55:46 -0800 Subject: [PATCH 08/45] Annotate ManagedAuthenticatedEncryptorFactory --- .../ManagedAuthenticatedEncryptorFactory.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs index 41ba968c9c9b..85f0152db7b1 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs @@ -63,6 +63,7 @@ private Func GetKeyedHashAlgorithmFactory(ManagedAuthenticat throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(configuration.ValidationAlgorithmType)); } + typeof(KeyedHashAlgorithm).AssertIsAssignableFrom(configuration.ValidationAlgorithmType); _logger.UsingManagedKeyedHashAlgorithm(configuration.ValidationAlgorithmType.FullName!); if (configuration.ValidationAlgorithmType == typeof(HMACSHA256)) { @@ -111,8 +112,10 @@ private static class AlgorithmActivator /// /// Creates a factory that wraps a call to . /// - public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type implementation) + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "implementation is known to be a reference type")] // TODO (acasey): confirm this is sufficient + public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type implementation) where T : class { + typeof(T).AssertIsAssignableFrom(implementation); return ((IActivator)Activator.CreateInstance(typeof(AlgorithmActivatorCore<>).MakeGenericType(implementation))!).Creator; } From 44517033e6454e7c392a3444039d2e91bf02b3e2 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 12 Dec 2022 14:19:24 -0800 Subject: [PATCH 09/45] Address some RouteValueDictionary fallout --- .../src/LinkGeneratorEndpointNameAddressExtensions.cs | 4 ++++ .../src/LinkGeneratorRouteValuesAddressExtensions.cs | 4 ++++ src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs | 4 ++++ .../Routing/src/Patterns/DefaultRoutePatternTransformer.cs | 2 ++ src/Http/Routing/src/Patterns/RoutePatternFactory.cs | 7 +++++++ src/Http/Routing/src/Patterns/RoutePatternTransformer.cs | 2 ++ 6 files changed, 23 insertions(+) diff --git a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs index 3ede96df1e54..bcb3fb530831 100644 --- a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs +++ b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs @@ -28,6 +28,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByName( this LinkGenerator generator, HttpContext httpContext, @@ -127,6 +128,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByName( this LinkGenerator generator, string endpointName, @@ -215,6 +217,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByName( this LinkGenerator generator, HttpContext httpContext, @@ -350,6 +353,7 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByName( this LinkGenerator generator, string endpointName, diff --git a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs index c83ab68d1e4e..5e11574904e1 100644 --- a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs +++ b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs @@ -28,6 +28,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static string? GetPathByRouteValues( this LinkGenerator generator, HttpContext httpContext, @@ -119,6 +120,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByRouteValues( this LinkGenerator generator, string? routeName, @@ -199,6 +201,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByRouteValues( this LinkGenerator generator, HttpContext httpContext, @@ -326,6 +329,7 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByRouteValues( this LinkGenerator generator, string? routeName, diff --git a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs index 9447793c78d3..33de9fece932 100644 --- a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs @@ -23,6 +23,7 @@ public static class MapRouteRouteBuilderExtensions /// The URL pattern of the route. /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -44,6 +45,7 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -70,6 +72,7 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -101,6 +104,7 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] // RouteValueDictionary public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, diff --git a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs index 461af9e90ff6..5155cbac6d11 100644 --- a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs +++ b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs @@ -23,6 +23,8 @@ public DefaultRoutePatternTransformer(ParameterPolicyFactory policyFactory) [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + "Consider using a different overload to avoid this issue.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + + "Consider using a different overload to avoid this issue.")] // RouteValueDictionary public override RoutePattern SubstituteRequiredValues(RoutePattern original, object requiredValues) { if (original == null) diff --git a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs index d3d6c13748cc..562927175c69 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs @@ -55,6 +55,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern) /// /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? defaults, object? parameterPolicies) { if (pattern == null) @@ -115,6 +116,7 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa /// /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? defaults, object? parameterPolicies, object? requiredValues) { if (pattern == null) @@ -206,6 +208,7 @@ public static RoutePattern Pattern(string? rawText, IEnumerableThe collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( object? defaults, object? parameterPolicies, @@ -268,6 +271,7 @@ public static RoutePattern Pattern( /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( string? rawText, object? defaults, @@ -363,6 +367,7 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( object? defaults, object? parameterPolicies, @@ -425,6 +430,7 @@ public static RoutePattern Pattern( /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( string? rawText, object? defaults, @@ -1209,6 +1215,7 @@ private static RoutePatternParameterPolicyReference ParameterPolicyCore(IParamet } [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] + [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary private static RouteValueDictionary? Wrap(object? values) { return values is null ? null : new RouteValueDictionary(values); diff --git a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs index 943b19c36a3e..104040954847 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs @@ -34,6 +34,8 @@ public abstract class RoutePatternTransformer /// [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + "Consider using a different overload to avoid this issue.")] + [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + + "Consider using a different overload to avoid this issue.")] public abstract RoutePattern? SubstituteRequiredValues(RoutePattern original, object requiredValues); /// From 1965ede4ace4b2394df5510dc314ed42ac2bb5a6 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 08:21:16 -0800 Subject: [PATCH 10/45] Annotate ILEmitTrie* --- src/Http/Routing/src/Matching/JumpTableBuilder.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Http/Routing/src/Matching/JumpTableBuilder.cs b/src/Http/Routing/src/Matching/JumpTableBuilder.cs index 0c48d3b261c2..86f08dd4dbfb 100644 --- a/src/Http/Routing/src/Matching/JumpTableBuilder.cs +++ b/src/Http/Routing/src/Matching/JumpTableBuilder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace Microsoft.AspNetCore.Routing.Matching; @@ -85,13 +86,14 @@ 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) + return MakeILEmitTrieJumpTableIfSupported(defaultDestination, exitDestination, pathEntries, fallback); + + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Guarded by IsDynamicCodeCompiled")] + static JumpTable MakeILEmitTrieJumpTableIfSupported(int defaultDestination, int exitDestination, (string text, int destination)[] pathEntries, JumpTable fallback) { -#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 RuntimeFeature.IsDynamicCodeCompiled + ? new ILEmitTrieJumpTable(defaultDestination, exitDestination, pathEntries, vectorize: null, fallback) + : fallback; } - - return fallback; } } From c02a56b567a2a1729fe3399beaecc3dcbc1dc0fa Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 08:31:22 -0800 Subject: [PATCH 11/45] Annotate RequestDelegateFactory --- .../src/RequestDelegateFactory.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 21a98241225e..3cba67ce01e4 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -28,11 +28,12 @@ namespace Microsoft.AspNetCore.Http; /// /// Creates implementations from request handlers. /// -[UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "RequestDelegateFactory.Create requires unreferenced code.")] -[UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "RequestDelegateFactory.Create requires unreferenced code.")] -[UnconditionalSuppressMessage("Trimmer", "IL2072", Justification = "RequestDelegateFactory.Create requires unreferenced code.")] -[UnconditionalSuppressMessage("Trimmer", "IL2075", Justification = "RequestDelegateFactory.Create requires unreferenced code.")] -[UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "RequestDelegateFactory.Create requires unreferenced code.")] +[UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] +[UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] +[UnconditionalSuppressMessage("Trimmer", "IL2072", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] +[UnconditionalSuppressMessage("Trimmer", "IL2075", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] +[UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] +[UnconditionalSuppressMessage("Trimmer", "IL3050", Justification = "RequestDelegateFactory.Create has RequiresDynamicCode.")] public static partial class RequestDelegateFactory { private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); @@ -125,6 +126,7 @@ public static partial class RequestDelegateFactory /// The options that will be used when calling . /// The to be passed to . [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] + [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateMetadataResult InferMetadata(MethodInfo methodInfo, RequestDelegateFactoryOptions? options = null) { var factoryContext = CreateFactoryContext(options); @@ -143,6 +145,7 @@ public static RequestDelegateMetadataResult InferMetadata(MethodInfo methodInfo, /// The used to configure the behavior of the handler. /// The . [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] + [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options) { return Create(handler, options, metadataResult: null); @@ -161,6 +164,7 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact /// /// The . [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] + [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Required to maintain compatibility")] public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null) { @@ -199,6 +203,7 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact /// The used to configure the behavior of the handler. /// The . [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] + [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory, RequestDelegateFactoryOptions? options) { return Create(methodInfo, targetFactory, options, metadataResult: null); @@ -218,6 +223,7 @@ public static RequestDelegateResult Create(MethodInfo methodInfo, Func /// The . [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] + [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory = null, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null) { From 462eb2a32d26e2bc77515c36756a750802149f84 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 08:34:44 -0800 Subject: [PATCH 12/45] Annotate Http*JsonExtensions --- src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs | 4 ++++ .../Http.Extensions/src/HttpResponseJsonExtensions.cs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs index a204e74c0477..06958f6d113d 100644 --- a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs @@ -31,6 +31,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static ValueTask ReadFromJsonAsync( this HttpRequest request, CancellationToken cancellationToken = default) @@ -48,6 +49,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static async ValueTask ReadFromJsonAsync( this HttpRequest request, JsonSerializerOptions? options, @@ -131,6 +133,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static ValueTask ReadFromJsonAsync( this HttpRequest request, Type type, @@ -149,6 +152,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static async ValueTask ReadFromJsonAsync( this HttpRequest request, Type type, diff --git a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs index 300b836a7d34..0ace67acf5c6 100644 --- a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs @@ -29,6 +29,7 @@ public static partial class HttpResponseJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -48,6 +49,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -69,6 +71,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -140,6 +143,7 @@ static async Task WriteAsJsonAsyncSlow(HttpResponse response, TValue value, Json } [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] private static async Task WriteAsJsonAsyncSlow( Stream body, TValue value, @@ -163,6 +167,7 @@ private static async Task WriteAsJsonAsyncSlow( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -183,6 +188,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -205,6 +211,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -236,6 +243,7 @@ public static Task WriteAsJsonAsync( } [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] private static async Task WriteAsJsonAsyncSlow( Stream body, object? value, From 541b6b9bfa6bdedd4adcd5ba1bdfc785d5d1fc54 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 09:09:53 -0800 Subject: [PATCH 13/45] Annotate ParameterBindingMethodCache and EndpointMetadataPopulator --- src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs | 3 +++ src/Shared/EndpointMetadataPopulator.cs | 1 + src/Shared/ParameterBindingMethodCache.cs | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs index 2abf10254b5f..515164060734 100644 --- a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs +++ b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs @@ -50,6 +50,9 @@ public bool CanWrite(ProblemDetailsContext context) [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed and we need to fallback" + "to reflection-based. The ProblemDetailsConverter is marked as RequiresUnreferencedCode already.")] + [UnconditionalSuppressMessage("Trimming", "IL3050", + Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed and we need to fallback" + + "to reflection-based. The ProblemDetailsConverter is marked as RequiresDynamicCode already.")] public ValueTask WriteAsync(ProblemDetailsContext context) { var httpContext = context.HttpContext; diff --git a/src/Shared/EndpointMetadataPopulator.cs b/src/Shared/EndpointMetadataPopulator.cs index bf36507d9d0c..425f45c46c47 100644 --- a/src/Shared/EndpointMetadataPopulator.cs +++ b/src/Shared/EndpointMetadataPopulator.cs @@ -12,6 +12,7 @@ namespace Microsoft.AspNetCore.Http; [UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "Trimmer warnings are presented in RequestDelegateFactory.")] +[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "AOT warnings are presented in RequestDelegateFactory.")] internal static class EndpointMetadataPopulator { private static readonly MethodInfo PopulateMetadataForParameterMethod = typeof(EndpointMetadataPopulator).GetMethod(nameof(PopulateMetadataForParameter), BindingFlags.NonPublic | BindingFlags.Static)!; diff --git a/src/Shared/ParameterBindingMethodCache.cs b/src/Shared/ParameterBindingMethodCache.cs index 64a994b593b8..b8f7ab5cc49a 100644 --- a/src/Shared/ParameterBindingMethodCache.cs +++ b/src/Shared/ParameterBindingMethodCache.cs @@ -54,6 +54,7 @@ public ParameterBindingMethodCache(bool preferNonGenericEnumParseOverload, bool } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] + [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public bool HasTryParseMethod(Type type) { var nonNullableParameterType = Nullable.GetUnderlyingType(type) ?? type; @@ -61,10 +62,12 @@ public bool HasTryParseMethod(Type type) } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] + [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public bool HasBindAsyncMethod(ParameterInfo parameter) => FindBindAsyncMethod(parameter).Expression is not null; [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] + [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public Func? FindTryParseMethod(Type type) { // This method is used to find TryParse methods from .NET types using reflection. It's used at app runtime. @@ -195,6 +198,7 @@ static bool ValidateReturnType(MethodInfo methodInfo) } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] + [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public (Expression? Expression, int ParamCount) FindBindAsyncMethod(ParameterInfo parameter) { (Func?, int) Finder(Type nonNullableParameterType) @@ -394,6 +398,7 @@ static bool ValidateReturnType(MethodInfo methodInfo) throw new InvalidOperationException($"No public parameterless constructor found for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'."); } + [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] private static MethodInfo? GetIBindableFromHttpContextMethod(Type type) { // Check if parameter is bindable via static abstract method on IBindableFromHttpContext From 29fcad968222751d48f526ca47c2073ce03c5b51 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 10:38:57 -0800 Subject: [PATCH 14/45] Annotate FSharpAsync codepath --- src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs | 1 + src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs | 3 +++ .../ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs | 1 + 3 files changed, 5 insertions(+) diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs index d47f97473557..768235915d00 100644 --- a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -29,6 +29,7 @@ public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType AwaitableInfo = coercedAwaitableInfo; } + [RequiresDynamicCode("Uses expression trees to coerce FSharpAsync to Task")] public static bool IsTypeAwaitable( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, out CoercedAwaitableInfo info) diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs index 52272f420834..4c57e6f50a18 100644 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -27,6 +27,7 @@ internal sealed class ObjectMethodExecutor })!; [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] + [RequiresDynamicCode("This method performs reflection on arbitrary types.")] private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]? parameterDefaultValues) { if (methodInfo == null) @@ -77,12 +78,14 @@ private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, obj public bool IsMethodAsync { get; } [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] + [RequiresDynamicCode("This method performs reflection on arbitrary types.")] public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) { return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); } [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] + [RequiresDynamicCode("This method performs reflection on arbitrary types.")] public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[] parameterDefaultValues) { if (parameterDefaultValues == null) diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs index 5b2a7389d2d6..79369e387869 100644 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -22,6 +22,7 @@ namespace Microsoft.Extensions.Internal; /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references /// to FSharp types have to be constructed dynamically at runtime. /// +[RequiresDynamicCode("Dynamically generates calls to FSharpAsync.StartAsTask")] internal static class ObjectMethodExecutorFSharpSupport { private static readonly object _fsharpValuesCacheLock = new object(); From 88a9dd57efa02239995e0434e15486f5b7f72778 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 10:50:55 -0800 Subject: [PATCH 15/45] Annotate RouteEndpointDataSource --- .../src/Builder/EndpointRouteBuilderExtensions.cs | 13 +++++++++++++ src/Http/Routing/src/RouteEndpointDataSource.cs | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs index 22e82eb81590..e31a6526c0b1 100644 --- a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs @@ -207,6 +207,7 @@ private static IEndpointConventionBuilder Map( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapGet( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -224,6 +225,7 @@ public static RouteHandlerBuilder MapGet( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapPost( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -241,6 +243,7 @@ public static RouteHandlerBuilder MapPost( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapPut( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -258,6 +261,7 @@ public static RouteHandlerBuilder MapPut( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapDelete( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -275,6 +279,7 @@ public static RouteHandlerBuilder MapDelete( /// The executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapPatch( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -293,6 +298,7 @@ public static RouteHandlerBuilder MapPatch( /// HTTP methods that the endpoint will match. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapMethods( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -312,6 +318,7 @@ public static RouteHandlerBuilder MapMethods( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -329,6 +336,7 @@ public static RouteHandlerBuilder Map( /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, @@ -357,6 +365,7 @@ public static RouteHandlerBuilder Map( /// /// [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler) { return endpoints.MapFallback("{*path:nonfile}", handler); @@ -384,6 +393,7 @@ public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoin /// /// [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] public static RouteHandlerBuilder MapFallback( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -393,6 +403,7 @@ public static RouteHandlerBuilder MapFallback( } [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] + [RequiresDynamicCode(MapEndpointTrimmerWarning)] private static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, @@ -407,6 +418,8 @@ private static RouteHandlerBuilder Map( return endpoints.GetOrAddRouteEndpointDataSource().AddRouteHandler(pattern, handler, httpMethods, isFallback); } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The relevant Map overloads have RequiredUnreferencedCode")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The relevant Map overloads have RequiredDynamicCode")] private static RouteEndpointDataSource GetOrAddRouteEndpointDataSource(this IEndpointRouteBuilder endpoints) { RouteEndpointDataSource? routeEndpointDataSource = null; diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index 2e39cd81effb..0867220e761f 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -13,6 +13,8 @@ namespace Microsoft.AspNetCore.Routing; +[RequiresUnreferencedCode("This API is not trimmer safe.")] +[RequiresDynamicCode("This API is not AOT safe.")] internal sealed class RouteEndpointDataSource : EndpointDataSource { private readonly List _routeEntries = new(); @@ -111,8 +113,6 @@ internal RouteEndpointBuilder GetSingleRouteEndpointBuilder() return CreateRouteEndpointBuilder(_routeEntries[0]); } - [UnconditionalSuppressMessage("Trimmer", "IL2026", - Justification = "We surface a RequireUnreferencedCode in the call to the Map method adding this EndpointDataSource. The trimmer is unable to infer this.")] private RouteEndpointBuilder CreateRouteEndpointBuilder( RouteEntry entry, RoutePattern? groupPrefix = null, IReadOnlyList>? groupConventions = null, IReadOnlyList>? groupFinallyConventions = null) { From 90f8c6a2c649abea1beece22feb3c958aa5cc7bb Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 13 Dec 2022 12:38:58 -0800 Subject: [PATCH 16/45] DefaultServiceProvider fallout --- src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs | 5 ++++- .../Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs | 1 + .../src/GenericHost/ISupportsUseDefaultServiceProvider.cs | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index f8e2a050bd7d..4e3d630e7357 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -203,6 +203,7 @@ public IWebHostBuilder ConfigureServices(Action configure) { _builder.UseServiceProviderFactory(context => @@ -267,7 +268,7 @@ void ConfigureStartup(HostBuilderContext context, IServiceCollection services) return this; } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2006:UnrecognizedReflectionPattern", Justification = "We need to call a generic method on IHostBuilder.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Constructing untrimmed generic types and methods with reference types")] private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object? instance = null) { var webHostBuilderContext = GetWebHostBuilderContext(context); @@ -303,6 +304,8 @@ private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessi if (configureContainerBuilder.MethodInfo != null) { var containerType = configureContainerBuilder.GetContainerType(); + Debug.Assert(containerType.IsClass); + // Store the builder in the property bag _builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder; diff --git a/src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs index 85e384a1a2a7..a00839c8d8b2 100644 --- a/src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/HostingStartupWebHostBuilder.cs @@ -63,6 +63,7 @@ public void ConfigureAppConfiguration(WebHostBuilderContext context, IConfigurat _configureConfiguration?.Invoke(context, builder); } + [RequiresDynamicCode("DefaultServiceProvider is not AOT safe")] public IWebHostBuilder UseDefaultServiceProvider(Action configure) { return _builder.UseDefaultServiceProvider(configure); diff --git a/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs b/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs index f1e91473433e..0e0f40b9145c 100644 --- a/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs +++ b/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs @@ -1,11 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Hosting; internal interface ISupportsUseDefaultServiceProvider { + [RequiresDynamicCode("The default service provider requires dynamic code and is not AOT safe")] IWebHostBuilder UseDefaultServiceProvider(Action configure); } From 790bc443c60560a45b712d4710b0b9901d797bec Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 15 Dec 2022 15:02:43 +0800 Subject: [PATCH 17/45] Update AOT suppressions --- Directory.Build.targets | 2 +- .../Microsoft.AspNetCore.Components.csproj | 1 + ...Microsoft.AspNetCore.Components.Web.csproj | 1 + .../Microsoft.JSInterop.WebAssembly.csproj | 1 + ...mponents.WebAssembly.Authentication.csproj | 1 + ...t.AspNetCore.Components.WebAssembly.csproj | 1 + .../ManagedAuthenticatedEncryptorFactory.cs | 3 +- .../Extensions/src/DataProtectionProvider.cs | 3 ++ .../src/WebApplicationBuilder.cs | 6 ++-- .../src/GenericHost/GenericWebHostBuilder.cs | 4 ++- .../HostingStartupWebHostBuilder.cs | 1 - .../ISupportsUseDefaultServiceProvider.cs | 2 -- .../Hosting/src/Internal/StartupLoader.cs | 19 +++++++++- src/Hosting/Hosting/src/Internal/WebHost.cs | 3 ++ .../Hosting/src/Startup/StartupBase.cs | 3 ++ src/Hosting/Hosting/src/WebHostBuilder.cs | 3 ++ .../Hosting/src/WebHostBuilderExtensions.cs | 3 ++ .../Hosting/test/Internal/MyContainer.cs | 2 +- .../src/Routing/RouteValueDictionary.cs | 9 +++++ .../Core/src/IdentityBuilderExtensions.cs | 24 ++++++++++--- .../Extensions.Core/src/IdentityBuilder.cs | 13 +++++++ .../src/Microsoft.JSInterop.csproj | 1 + .../HealthChecks/src/HealthCheckOptions.cs | 2 +- .../src/Microsoft.AspNetCore.SpaProxy.csproj | 1 + src/Shared/PropertyHelper/PropertyHelper.cs | 36 +++++++++++++++---- 25 files changed, 121 insertions(+), 24 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index 4d830493b705..1980cdcc77b2 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -10,7 +10,7 @@ '$(IsAnalyzersProject)' != 'true' and '$(IsProjectTemplateProject)' != 'true'">true - $([MSBuild]::ValueOrDefault($(IsTrimmable),'false')) + $([MSBuild]::ValueOrDefault($(IsTrimmable),'false')) diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 27bab422c46c..314e75696c2d 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -8,6 +8,7 @@ enable true true + false diff --git a/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj b/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj index 3df719088e59..5c9d8ade55ef 100644 --- a/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj +++ b/src/Components/Web/src/Microsoft.AspNetCore.Components.Web.csproj @@ -8,6 +8,7 @@ Microsoft.AspNetCore.Components enable true + false diff --git a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj index a25062af22f3..ee665e5a09c9 100644 --- a/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj +++ b/src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj @@ -8,6 +8,7 @@ true enable true + false diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj index d5eeee7d1eba..3e241ee418bc 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.csproj @@ -9,6 +9,7 @@ true true true + false diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 316817a3fd5c..a09c735a11cc 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -10,6 +10,7 @@ enable true true + false diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs index 85f0152db7b1..20def91df8e8 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/ManagedAuthenticatedEncryptorFactory.cs @@ -112,10 +112,9 @@ private static class AlgorithmActivator /// /// Creates a factory that wraps a call to . /// - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "implementation is known to be a reference type")] // TODO (acasey): confirm this is sufficient + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe to use because implementation is either a KeyedHashAlgorithm or SymmetricAlgorithm type.")] public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type implementation) where T : class { - typeof(T).AssertIsAssignableFrom(implementation); return ((IActivator)Activator.CreateInstance(typeof(AlgorithmActivatorCore<>).MakeGenericType(implementation))!).Creator; } diff --git a/src/DataProtection/Extensions/src/DataProtectionProvider.cs b/src/DataProtection/Extensions/src/DataProtectionProvider.cs index 234661722b34..1cf6632495d7 100644 --- a/src/DataProtection/Extensions/src/DataProtectionProvider.cs +++ b/src/DataProtection/Extensions/src/DataProtectionProvider.cs @@ -172,6 +172,9 @@ internal static IDataProtectionProvider CreateProvider( setupAction(builder); // extract the provider instance from the service collection + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return serviceCollection.BuildServiceProvider().GetRequiredService(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. } } diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 3b8bc9117f27..0919792a968a 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; @@ -76,14 +75,14 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 4e3d630e7357..95bf862e8892 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -203,7 +203,6 @@ public IWebHostBuilder ConfigureServices(Action configure) { _builder.UseServiceProviderFactory(context => @@ -211,7 +210,10 @@ public IWebHostBuilder UseDefaultServiceProvider(Action configure) { return _builder.UseDefaultServiceProvider(configure); diff --git a/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs b/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs index 0e0f40b9145c..f1e91473433e 100644 --- a/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs +++ b/src/Hosting/Hosting/src/GenericHost/ISupportsUseDefaultServiceProvider.cs @@ -1,13 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Hosting; internal interface ISupportsUseDefaultServiceProvider { - [RequiresDynamicCode("The default service provider requires dynamic code and is not AOT safe")] IWebHostBuilder UseDefaultServiceProvider(Action configure); } diff --git a/src/Hosting/Hosting/src/Internal/StartupLoader.cs b/src/Hosting/Hosting/src/Internal/StartupLoader.cs index ee23eda69c12..966fef483b1b 100644 --- a/src/Hosting/Hosting/src/Internal/StartupLoader.cs +++ b/src/Hosting/Hosting/src/Internal/StartupLoader.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Hosting.Internal; using Microsoft.Extensions.DependencyInjection; @@ -54,13 +55,26 @@ public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object); var builder = (ConfigureServicesDelegateBuilder)Activator.CreateInstance( - typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type), + CreateConfigureServicesDelegateBuilder(type), hostingServiceProvider, servicesMethod, configureContainerMethod, instance)!; return new StartupMethods(instance, configureMethod.Build(instance), builder.Build()); + + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "There is a runtime check for ValueType startup container. It's unlikely anyone will use a ValueType here.")] + static Type CreateConfigureServicesDelegateBuilder(Type type) + { + if (type.IsValueType && !RuntimeFeature.IsDynamicCodeSupported) + { + throw new InvalidOperationException("ValueType startup container isn't supported with AOT."); + } + + return typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type); + } } private abstract class ConfigureServicesDelegateBuilder @@ -146,7 +160,10 @@ IServiceProvider ConfigureServicesWithContainerConfiguration(IServiceCollection applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder); } + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return applicationServiceProvider ?? services.BuildServiceProvider(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. } } diff --git a/src/Hosting/Hosting/src/Internal/WebHost.cs b/src/Hosting/Hosting/src/Internal/WebHost.cs index b2350b6ad006..b1804d2bb862 100644 --- a/src/Hosting/Hosting/src/Internal/WebHost.cs +++ b/src/Hosting/Hosting/src/Internal/WebHost.cs @@ -115,7 +115,10 @@ public void Initialize() // EnsureApplicationServices may have failed due to a missing or throwing Startup class. if (_applicationServices == null) { + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. _applicationServices = _applicationServiceCollection.BuildServiceProvider(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. } if (!_options.CaptureStartupErrors) diff --git a/src/Hosting/Hosting/src/Startup/StartupBase.cs b/src/Hosting/Hosting/src/Startup/StartupBase.cs index c458273eab7e..7354dcf72c36 100644 --- a/src/Hosting/Hosting/src/Startup/StartupBase.cs +++ b/src/Hosting/Hosting/src/Startup/StartupBase.cs @@ -38,7 +38,10 @@ public virtual void ConfigureServices(IServiceCollection services) /// The . public virtual IServiceProvider CreateServiceProvider(IServiceCollection services) { + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. return services.BuildServiceProvider(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. } } diff --git a/src/Hosting/Hosting/src/WebHostBuilder.cs b/src/Hosting/Hosting/src/WebHostBuilder.cs index c0db8b8734e3..dc46795d989d 100644 --- a/src/Hosting/Hosting/src/WebHostBuilder.cs +++ b/src/Hosting/Hosting/src/WebHostBuilder.cs @@ -200,7 +200,10 @@ public IWebHost Build() static IServiceProvider GetProviderFromFactory(IServiceCollection collection) { + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. var provider = collection.BuildServiceProvider(); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. var factory = provider.GetService>(); if (factory != null && factory is not DefaultServiceProviderFactory) diff --git a/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs b/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs index b6c63d8a77db..e7364bd09726 100644 --- a/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs +++ b/src/Hosting/Hosting/src/WebHostBuilderExtensions.cs @@ -216,7 +216,10 @@ public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hos { var options = new ServiceProviderOptions(); configure(context, options); + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. services.Replace(ServiceDescriptor.Singleton>(new DefaultServiceProviderFactory(options))); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. }); } diff --git a/src/Hosting/Hosting/test/Internal/MyContainer.cs b/src/Hosting/Hosting/test/Internal/MyContainer.cs index 434c616d3b6f..d2becf394968 100644 --- a/src/Hosting/Hosting/test/Internal/MyContainer.cs +++ b/src/Hosting/Hosting/test/Internal/MyContainer.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Extensions.DependencyInjection; diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index b351e50956a0..97336e41d00d 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -586,6 +586,9 @@ public bool TryGetValue(string key, out object? value) [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] private bool TryGetValueSlow(string key, out object? value) { if (_propertyStorage != null) @@ -623,6 +626,9 @@ private void EnsureCapacity(int capacity) [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] private void EnsureCapacitySlow(int capacity) { if (_propertyStorage != null) @@ -794,6 +800,9 @@ public bool MoveNext() [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] private bool MoveNextRare() { var dictionary = _dictionary; diff --git a/src/Identity/Core/src/IdentityBuilderExtensions.cs b/src/Identity/Core/src/IdentityBuilderExtensions.cs index a05ca35c002c..a3a655ffb129 100644 --- a/src/Identity/Core/src/IdentityBuilderExtensions.cs +++ b/src/Identity/Core/src/IdentityBuilderExtensions.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; @@ -17,9 +18,12 @@ public static class IdentityBuilderExtensions /// /// The current instance. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder) { var userType = builder.UserType; + Debug.Assert(userType.IsClass); + var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType); var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType); var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType); @@ -30,11 +34,15 @@ public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder buil .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType); } + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] private static void AddSignInManagerDeps(this IdentityBuilder builder) { + var userType = builder.UserType; + Debug.Assert(userType.IsClass); + builder.Services.AddHttpContextAccessor(); - builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(builder.UserType)); - builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(builder.UserType)); + builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(userType)); + builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(userType)); } /// @@ -42,10 +50,14 @@ private static void AddSignInManagerDeps(this IdentityBuilder builder) /// /// The current instance. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) { + var userType = builder.UserType; + Debug.Assert(userType.IsClass); + builder.AddSignInManagerDeps(); - var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); + var managerType = typeof(SignInManager<>).MakeGenericType(userType); builder.Services.AddScoped(managerType); return builder; } @@ -56,10 +68,14 @@ public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) /// The type of the sign in manager to add. /// The current instance. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddSignInManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSignInManager>(this IdentityBuilder builder) where TSignInManager : class { + var userType = builder.UserType; + Debug.Assert(userType.IsClass); + builder.AddSignInManagerDeps(); - var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); + var managerType = typeof(SignInManager<>).MakeGenericType(userType); var customType = typeof(TSignInManager); if (!managerType.IsAssignableFrom(customType)) { diff --git a/src/Identity/Extensions.Core/src/IdentityBuilder.cs b/src/Identity/Extensions.Core/src/IdentityBuilder.cs index 28e3804e9c93..ff9e35f2768c 100644 --- a/src/Identity/Extensions.Core/src/IdentityBuilder.cs +++ b/src/Identity/Extensions.Core/src/IdentityBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -21,6 +22,7 @@ public class IdentityBuilder /// The to attach to. public IdentityBuilder(Type user, IServiceCollection services) { + Debug.Assert(user.IsClass); UserType = user; Services = services; } @@ -70,6 +72,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The user validator type. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddUserValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TValidator>() where TValidator : class => AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator)); @@ -78,6 +81,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The type of the claims principal factory. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddClaimsPrincipalFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFactory>() where TFactory : class => AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory)); @@ -97,6 +101,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The validator type used to validate passwords. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddPasswordValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TValidator>() where TValidator : class => AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator)); @@ -105,6 +110,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The user store type. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddUserStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TStore>() where TStore : class => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore)); @@ -123,6 +129,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// The name of the provider to add. /// The type of the to add. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddTokenProvider(string providerName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type provider) { if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).IsAssignableFrom(provider)) @@ -142,6 +149,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the user manager to add. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddUserManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserManager>() where TUserManager : class { var userManagerType = typeof(UserManager<>).MakeGenericType(UserType); @@ -162,6 +170,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role type. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddRoles<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRole>() where TRole : class { RoleType = typeof(TRole); @@ -176,6 +185,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role validator type. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] public virtual IdentityBuilder AddRoleValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRole>() where TRole : class { if (RoleType == null) @@ -206,6 +216,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role store. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] public virtual IdentityBuilder AddRoleStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TStore>() where TStore : class { if (RoleType == null) @@ -220,6 +231,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the role manager to add. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] public virtual IdentityBuilder AddRoleManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRoleManager>() where TRoleManager : class { if (RoleType == null) @@ -244,6 +256,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the user confirmation to add. /// The current instance. + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public virtual IdentityBuilder AddUserConfirmation<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserConfirmation>() where TUserConfirmation : class => AddScoped(typeof(IUserConfirmation<>).MakeGenericType(UserType), typeof(TUserConfirmation)); } diff --git a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj index ed6e7c663554..e565cce835e9 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj +++ b/src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj @@ -9,6 +9,7 @@ enable true $(DefineConstants);JS_INTEROP + false diff --git a/src/Middleware/HealthChecks/src/HealthCheckOptions.cs b/src/Middleware/HealthChecks/src/HealthCheckOptions.cs index f813e47993ea..fdcb040d0822 100644 --- a/src/Middleware/HealthChecks/src/HealthCheckOptions.cs +++ b/src/Middleware/HealthChecks/src/HealthCheckOptions.cs @@ -49,7 +49,7 @@ public IDictionary ResultStatusCodes private static IDictionary ValidateStatusCodesMapping(IDictionary mapping) { - var missingHealthStatus = ((HealthStatus[])Enum.GetValues(typeof(HealthStatus))).Except(mapping.Keys).ToList(); + var missingHealthStatus = Enum.GetValues().Except(mapping.Keys).ToList(); if (missingHealthStatus.Count > 0) { var missing = string.Join(", ", missingHealthStatus.Select(status => $"{nameof(HealthStatus)}.{status}")); diff --git a/src/Middleware/Spa/SpaProxy/src/Microsoft.AspNetCore.SpaProxy.csproj b/src/Middleware/Spa/SpaProxy/src/Microsoft.AspNetCore.SpaProxy.csproj index 1807fe067e83..b2ebc472c1fa 100644 --- a/src/Middleware/Spa/SpaProxy/src/Microsoft.AspNetCore.SpaProxy.csproj +++ b/src/Middleware/Spa/SpaProxy/src/Microsoft.AspNetCore.SpaProxy.csproj @@ -4,6 +4,7 @@ Helpers for launching the SPA CLI proxy automatically when the application starts in ASP.NET MVC Core. $(DefaultNetCoreTargetFramework) true + false diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index 097267ef0431..67472463c283 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -13,8 +13,6 @@ namespace Microsoft.Extensions.Internal; -[RequiresUnreferencedCode("This type reflects over the properties of types (and, notably, the properties of interfaces they implement) and so is trim-incompatible")] -[RequiresDynamicCode("This type synthesizes delegate types with (possible) value-type parameters and so is AOT-incompatible")] internal sealed class PropertyHelper { private const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; @@ -73,6 +71,8 @@ public PropertyHelper(PropertyInfo property) /// public Func ValueGetter { + [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] get { if (_valueGetter == null) @@ -89,6 +89,8 @@ public PropertyHelper(PropertyInfo property) /// public Action ValueSetter { + [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] get { if (_valueSetter == null) @@ -105,6 +107,8 @@ public PropertyHelper(PropertyInfo property) /// /// The object whose property value will be returned. /// The property value. + [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public object? GetValue(object instance) { return ValueGetter(instance); @@ -115,6 +119,8 @@ public PropertyHelper(PropertyInfo property) /// /// The object whose property value will be set. /// The property value. + [RequiresUnreferencedCode("This API is not trim safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public void SetValue(object instance, object? value) { ValueSetter(instance, value); @@ -127,7 +133,9 @@ public void SetValue(object instance, object? value) /// The type to extract property accessors for. /// A cached array of all public properties of the specified type. /// - public static PropertyHelper[] GetProperties(Type type) + [RequiresUnreferencedCode("This API is not trim safe.")] + public static PropertyHelper[] GetProperties( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { return GetProperties(type, PropertiesCache); } @@ -146,7 +154,9 @@ public static PropertyHelper[] GetProperties(Type type) /// /// A cached array of all public properties of the specified type. /// - public static PropertyHelper[] GetVisibleProperties(Type type) + [RequiresUnreferencedCode("This API is not trim safe.")] + public static PropertyHelper[] GetVisibleProperties( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { return GetVisibleProperties(type, PropertiesCache, VisiblePropertiesCache); } @@ -160,6 +170,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. /// + [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -179,6 +191,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. /// + [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -189,6 +203,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) CallNullSafePropertyGetterByReferenceOpenGenericMethod); } + [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] private static Func MakeFastPropertyGetter( PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, @@ -232,6 +248,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) } } + [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] private static Func MakeFastPropertyGetter( Type openGenericDelegateType, MethodInfo propertyGetMethod, @@ -260,6 +278,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) /// This method is more memory efficient than a dynamically compiled lambda, and about the /// same speed. This only works for reference types. /// + [RequiresUnreferencedCode("This API is not trimmer safe.")] + [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -301,6 +321,8 @@ public static PropertyHelper[] GetVisibleProperties(Type type) /// The implementation of PropertyHelper will cache the property accessors per-type. This is /// faster when the same type is used multiple times with ObjectToDictionary. /// + [RequiresUnreferencedCode("Method uses reflection to generate the dictionary.")] + [RequiresDynamicCode("Method uses reflection to generate the dictionary.")] public static IDictionary ObjectToDictionary(object? value) { if (value is IDictionary dictionary) @@ -389,6 +411,7 @@ private static void CallPropertySetter( /// /// A cached array of all public properties of the specified type. /// + [RequiresUnreferencedCode("This API is not trim safe.")] public static PropertyHelper[] GetVisibleProperties( Type type, ConcurrentDictionary? allPropertiesCache, @@ -469,6 +492,7 @@ public static PropertyHelper[] GetVisibleProperties( /// A cached array of all public properties of the specified type. /// // There isn't a way to represent trimmability requirements since for type since we unwrap nullable types. + [RequiresUnreferencedCode("This API is not trim safe.")] public static PropertyHelper[] GetProperties( Type type, ConcurrentDictionary? cache) @@ -500,7 +524,7 @@ public static PropertyHelper[] GetProperties( static void AddInterestingProperties( List propertyHelpers, - Type type) + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type) { foreach (var property in type.GetProperties(Everything)) { @@ -543,8 +567,6 @@ private static bool IsRefStructProperty(PropertyInfo property) property.PropertyType.IsDefined(IsByRefLikeAttribute); } - [RequiresUnreferencedCode("A wrapper over PropertyHelper, which is not time-compatible")] - [RequiresDynamicCode("A wrapper over PropertyHelper, which is not AOT-compatible")] internal static class MetadataUpdateHandler { /// From f8b05e8c96a29bc7cbfc42b10c1ac2f538db1665 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 15 Dec 2022 15:06:19 +0800 Subject: [PATCH 18/45] Clean up --- .../src/WebApplicationBuilder.cs | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplicationBuilder.cs b/src/DefaultBuilder/src/WebApplicationBuilder.cs index 0919792a968a..a67653f56b3d 100644 --- a/src/DefaultBuilder/src/WebApplicationBuilder.cs +++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs @@ -29,7 +29,21 @@ public sealed class WebApplicationBuilder internal WebApplicationBuilder(WebApplicationOptions options, Action? configureDefaults = null) { - _hostApplicationBuilder = MakeHostApplicationBuilder(options); + var configuration = new ConfigurationManager(); + + configuration.AddEnvironmentVariables(prefix: "ASPNETCORE_"); + + // TODO: Remove when DI no longer has RequiresDynamicCodeAttribute https://github.com/dotnet/runtime/pull/79425 +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. + _hostApplicationBuilder = new HostApplicationBuilder(new HostApplicationBuilderSettings + { + Args = options.Args, + ApplicationName = options.ApplicationName, + EnvironmentName = options.EnvironmentName, + ContentRootPath = options.ContentRootPath, + Configuration = configuration, + }); +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. // Set WebRootPath if necessary if (options.WebRootPath is not null) @@ -75,25 +89,6 @@ internal WebApplicationBuilder(WebApplicationOptions options, Action /// Provides information about the web hosting environment an application is running. /// From 124b1502f50c8b4ab5f79fd1bdb00b749743a890 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 15 Dec 2022 15:39:07 +0800 Subject: [PATCH 19/45] Update --- .../src/GenericHost/GenericWebHostBuilder.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 95bf862e8892..905f85b060b4 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Builder; @@ -305,23 +306,10 @@ private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessi var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName); if (configureContainerBuilder.MethodInfo != null) { - var containerType = configureContainerBuilder.GetContainerType(); - Debug.Assert(containerType.IsClass); - // Store the builder in the property bag _builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder; - var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType); - - // Get the private ConfigureContainer method on this type then close over the container type - var configureCallback = typeof(GenericWebHostBuilder).GetMethod(nameof(ConfigureContainerImpl), BindingFlags.NonPublic | BindingFlags.Instance)! - .MakeGenericMethod(containerType) - .CreateDelegate(actionType, this); - - // _builder.ConfigureContainer(ConfigureContainer); - typeof(IHostBuilder).GetMethod(nameof(IHostBuilder.ConfigureContainer))! - .MakeGenericMethod(containerType) - .InvokeWithoutWrappingExceptions(_builder, new object[] { configureCallback }); + InvokeContainer(this, configureContainerBuilder); } // Resolve Configure after calling ConfigureServices and ConfigureContainer @@ -347,6 +335,30 @@ private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessi } }; }); + + [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "There is a runtime check for ValueType startup container. It's unlikely anyone will use a ValueType here.")] + static void InvokeContainer(GenericWebHostBuilder genericWebHostBuilder, ConfigureContainerBuilder configureContainerBuilder) + { + var containerType = configureContainerBuilder.GetContainerType(); + + if (containerType.IsValueType && !RuntimeFeature.IsDynamicCodeSupported) + { + throw new InvalidOperationException("ValueType startup container isn't supported with AOT."); + } + + var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType); + + // Get the private ConfigureContainer method on this type then close over the container type + var configureCallback = typeof(GenericWebHostBuilder).GetMethod(nameof(ConfigureContainerImpl), BindingFlags.NonPublic | BindingFlags.Instance)! + .MakeGenericMethod(containerType) + .CreateDelegate(actionType, genericWebHostBuilder); + + // _builder.ConfigureContainer(ConfigureContainer); + typeof(IHostBuilder).GetMethod(nameof(IHostBuilder.ConfigureContainer))! + .MakeGenericMethod(containerType) + .InvokeWithoutWrappingExceptions(genericWebHostBuilder._builder, new object[] { configureCallback }); + } } private void ConfigureContainerImpl(HostBuilderContext context, TContainer container) where TContainer : notnull From 2eb8f4b026ee5e71b25e55165234fa01177d7c88 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 15 Dec 2022 16:58:14 +0800 Subject: [PATCH 20/45] Update --- .../Builder/EndpointRouteBuilderExtensions.cs | 2 - .../Routing/src/RouteEndpointDataSource.cs | 6 ++- .../CoercedAwaitableInfo.cs | 2 +- .../ObjectMethodExecutorFSharpSupport.cs | 4 +- src/Shared/ParameterBindingMethodCache.cs | 2 +- ...tpValidationProblemDetailsJsonConverter.cs | 38 +++++++++++-------- .../ProblemDetailsJsonConverter.cs | 17 +++++++-- 7 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs index e31a6526c0b1..cedeff33ad9d 100644 --- a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs @@ -418,8 +418,6 @@ private static RouteHandlerBuilder Map( return endpoints.GetOrAddRouteEndpointDataSource().AddRouteHandler(pattern, handler, httpMethods, isFallback); } - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The relevant Map overloads have RequiredUnreferencedCode")] - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The relevant Map overloads have RequiredDynamicCode")] private static RouteEndpointDataSource GetOrAddRouteEndpointDataSource(this IEndpointRouteBuilder endpoints) { RouteEndpointDataSource? routeEndpointDataSource = null; diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index 0867220e761f..035a64a4560c 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -13,8 +13,6 @@ namespace Microsoft.AspNetCore.Routing; -[RequiresUnreferencedCode("This API is not trimmer safe.")] -[RequiresDynamicCode("This API is not AOT safe.")] internal sealed class RouteEndpointDataSource : EndpointDataSource { private readonly List _routeEntries = new(); @@ -113,6 +111,10 @@ internal RouteEndpointBuilder GetSingleRouteEndpointBuilder() return CreateRouteEndpointBuilder(_routeEntries[0]); } + [UnconditionalSuppressMessage("Trimmer", "IL2026", + Justification = "We surface a RequireUnreferencedCode in the call to the Map method adding this EndpointDataSource. The trimmer is unable to infer this.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "We surface a RequiresDynamicCode in the call to the Map method adding this EndpointDataSource. The trimmer is unable to infer this.")] private RouteEndpointBuilder CreateRouteEndpointBuilder( RouteEntry entry, RoutePattern? groupPrefix = null, IReadOnlyList>? groupConventions = null, IReadOnlyList>? groupFinallyConventions = null) { diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs index 768235915d00..4951c218b9a8 100644 --- a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs +++ b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs @@ -29,7 +29,7 @@ public CoercedAwaitableInfo(Expression coercerExpression, Type coercerResultType AwaitableInfo = coercedAwaitableInfo; } - [RequiresDynamicCode("Uses expression trees to coerce FSharpAsync to Task")] + [RequiresDynamicCode("Dynamically generates calls to FSharpAsync.")] public static bool IsTypeAwaitable( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, out CoercedAwaitableInfo info) diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs index 79369e387869..95970685f6cb 100644 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutorFSharpSupport.cs @@ -22,7 +22,7 @@ namespace Microsoft.Extensions.Internal; /// FSharp.Core.dll, because non-F# applications wouldn't use it. So all the references /// to FSharp types have to be constructed dynamically at runtime. /// -[RequiresDynamicCode("Dynamically generates calls to FSharpAsync.StartAsTask")] +[RequiresDynamicCode("Dynamically generates calls to FSharpAsync.")] internal static class ObjectMethodExecutorFSharpSupport { private static readonly object _fsharpValuesCacheLock = new object(); @@ -31,7 +31,7 @@ internal static class ObjectMethodExecutorFSharpSupport private static PropertyInfo _fsharpOptionOfTaskCreationOptionsNoneProperty; private static PropertyInfo _fsharpOptionOfCancellationTokenNoneProperty; - [UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "Reflecting over the async FSharpAsync<> contract")] + [UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "Reflecting over the async FSharpAsync<> contract.")] public static bool TryBuildCoercerFromFSharpAsyncToAwaitable( Type possibleFSharpAsyncType, out Expression coerceToAwaitableExpression, diff --git a/src/Shared/ParameterBindingMethodCache.cs b/src/Shared/ParameterBindingMethodCache.cs index b8f7ab5cc49a..c0461a115dc8 100644 --- a/src/Shared/ParameterBindingMethodCache.cs +++ b/src/Shared/ParameterBindingMethodCache.cs @@ -398,7 +398,7 @@ static bool ValidateReturnType(MethodInfo methodInfo) throw new InvalidOperationException($"No public parameterless constructor found for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'."); } - [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] + [RequiresDynamicCode("MakeGenericMethod is possible used with ValueTypes and isn't compatible with AOT.")] private static MethodInfo? GetIBindableFromHttpContextMethod(Type type) { // Check if parameter is bindable via static abstract method on IBindableFromHttpContext diff --git a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs index 9eae00e1e35a..f1f3f9d2b736 100644 --- a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs @@ -7,19 +7,22 @@ namespace Microsoft.AspNetCore.Http; -// TODO (acasey): identify and flag consumers -[RequiresUnreferencedCode("This API is not trim safe - from ProblemDetailsJsonConverter and JsonSerializer.")] -[RequiresDynamicCode("This API is not AOT safe - from ProblemDetailsJsonConverter and JsonSerializer.")] -internal sealed class HttpValidationProblemDetailsJsonConverter : JsonConverter +internal sealed partial class HttpValidationProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Errors = JsonEncodedText.Encode("errors"); + [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [UnconditionalSuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] + [UnconditionalSuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] public override HttpValidationProblemDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var problemDetails = new HttpValidationProblemDetails(); return ReadProblemDetails(ref reader, options, problemDetails); } + [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader reader, JsonSerializerOptions options, HttpValidationProblemDetails problemDetails) { if (reader.TokenType != JsonTokenType.StartObject) @@ -31,7 +34,8 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader { if (reader.ValueTextEquals(Errors.EncodedUtf8Bytes)) { - var errors = DeserializeErrors(ref reader, options); + var context = new ErrorsJsonContext(options); + var errors = JsonSerializer.Deserialize(ref reader, context.DictionaryStringStringArray); if (errors is not null) { foreach (var item in errors) @@ -52,30 +56,34 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader } return problemDetails; - - [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "We ensure Dictionary is preserved.")] - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "We ensure Dictionary is preserved and the type arguments are reference types.")] - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties, typeof(Dictionary))] - static Dictionary? DeserializeErrors(ref Utf8JsonReader reader, JsonSerializerOptions options) - => JsonSerializer.Deserialize>(ref reader, options); } + [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [UnconditionalSuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] + [UnconditionalSuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] public override void Write(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { WriteProblemDetails(writer, value, options); } + [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] public static void WriteProblemDetails(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { writer.WriteStartObject(); ProblemDetailsJsonConverter.WriteProblemDetails(writer, value, options); writer.WritePropertyName(Errors); - SerializeErrors(writer, value.Errors, options); - writer.WriteEndObject(); + var context = new ErrorsJsonContext(options); + JsonSerializer.Serialize(writer, value.Errors, context.IDictionaryStringStringArray); - static void SerializeErrors(Utf8JsonWriter writer, IDictionary errors, JsonSerializerOptions options) - => JsonSerializer.Serialize(writer, errors, options); + writer.WriteEndObject(); } + + [JsonSerializable(typeof(IDictionary))] + [JsonSerializable(typeof(Dictionary))] + private sealed partial class ErrorsJsonContext : JsonSerializerContext + { } } diff --git a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs index 799dbd8a7ac4..a523de3e4aad 100644 --- a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs @@ -8,10 +8,7 @@ namespace Microsoft.AspNetCore.Http; -// TODO (acasey): identify and flag consumers -[RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] -[RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] -internal sealed class ProblemDetailsJsonConverter : JsonConverter +internal sealed partial class ProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Type = JsonEncodedText.Encode("type"); private static readonly JsonEncodedText Title = JsonEncodedText.Encode("title"); @@ -19,6 +16,10 @@ internal sealed class ProblemDetailsJsonConverter : JsonConverter Date: Fri, 16 Dec 2022 14:58:11 +0800 Subject: [PATCH 21/45] PR feedback --- .../Core/src/IdentityBuilderExtensions.cs | 29 +++++-------------- .../Extensions.Core/src/IdentityBuilder.cs | 15 ++++++++-- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Identity/Core/src/IdentityBuilderExtensions.cs b/src/Identity/Core/src/IdentityBuilderExtensions.cs index a3a655ffb129..35f9c34786a3 100644 --- a/src/Identity/Core/src/IdentityBuilderExtensions.cs +++ b/src/Identity/Core/src/IdentityBuilderExtensions.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; @@ -21,13 +20,10 @@ public static class IdentityBuilderExtensions [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder) { - var userType = builder.UserType; - Debug.Assert(userType.IsClass); - - var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(userType); - var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(userType); - var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(userType); - var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(userType); + var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(builder.UserType); + var phoneNumberProviderType = typeof(PhoneNumberTokenProvider<>).MakeGenericType(builder.UserType); + var emailTokenProviderType = typeof(EmailTokenProvider<>).MakeGenericType(builder.UserType); + var authenticatorProviderType = typeof(AuthenticatorTokenProvider<>).MakeGenericType(builder.UserType); return builder.AddTokenProvider(TokenOptions.DefaultProvider, dataProtectionProviderType) .AddTokenProvider(TokenOptions.DefaultEmailProvider, emailTokenProviderType) .AddTokenProvider(TokenOptions.DefaultPhoneProvider, phoneNumberProviderType) @@ -37,12 +33,9 @@ public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder buil [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] private static void AddSignInManagerDeps(this IdentityBuilder builder) { - var userType = builder.UserType; - Debug.Assert(userType.IsClass); - builder.Services.AddHttpContextAccessor(); - builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(userType)); - builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(userType)); + builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(builder.UserType)); + builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(builder.UserType)); } /// @@ -53,11 +46,8 @@ private static void AddSignInManagerDeps(this IdentityBuilder builder) [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) { - var userType = builder.UserType; - Debug.Assert(userType.IsClass); - builder.AddSignInManagerDeps(); - var managerType = typeof(SignInManager<>).MakeGenericType(userType); + var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); builder.Services.AddScoped(managerType); return builder; } @@ -71,11 +61,8 @@ public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] public static IdentityBuilder AddSignInManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSignInManager>(this IdentityBuilder builder) where TSignInManager : class { - var userType = builder.UserType; - Debug.Assert(userType.IsClass); - builder.AddSignInManagerDeps(); - var managerType = typeof(SignInManager<>).MakeGenericType(userType); + var managerType = typeof(SignInManager<>).MakeGenericType(builder.UserType); var customType = typeof(TSignInManager); if (!managerType.IsAssignableFrom(customType)) { diff --git a/src/Identity/Extensions.Core/src/IdentityBuilder.cs b/src/Identity/Extensions.Core/src/IdentityBuilder.cs index ff9e35f2768c..da68d20dc5b6 100644 --- a/src/Identity/Extensions.Core/src/IdentityBuilder.cs +++ b/src/Identity/Extensions.Core/src/IdentityBuilder.cs @@ -22,7 +22,11 @@ public class IdentityBuilder /// The to attach to. public IdentityBuilder(Type user, IServiceCollection services) { - Debug.Assert(user.IsClass); + if (!user.IsClass) + { + throw new ArgumentException("User type must be a class.", nameof(user)); + } + UserType = user; Services = services; } @@ -34,7 +38,14 @@ public IdentityBuilder(Type user, IServiceCollection services) /// The to use for the roles. /// The to attach to. public IdentityBuilder(Type user, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type role, IServiceCollection services) : this(user, services) - => RoleType = role; + { + if (!role.IsClass) + { + throw new ArgumentException("Role type must be a class.", nameof(role)); + } + + RoleType = role; + } /// /// Gets the used for users. From b6bf700193d98deeca2a1c979dd9c370009611cd Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 15:01:51 +0800 Subject: [PATCH 22/45] Clean up --- src/Identity/Extensions.Core/src/IdentityBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Identity/Extensions.Core/src/IdentityBuilder.cs b/src/Identity/Extensions.Core/src/IdentityBuilder.cs index da68d20dc5b6..9b109546f813 100644 --- a/src/Identity/Extensions.Core/src/IdentityBuilder.cs +++ b/src/Identity/Extensions.Core/src/IdentityBuilder.cs @@ -22,9 +22,9 @@ public class IdentityBuilder /// The to attach to. public IdentityBuilder(Type user, IServiceCollection services) { - if (!user.IsClass) + if (user.IsValueType) { - throw new ArgumentException("User type must be a class.", nameof(user)); + throw new ArgumentException("User type can't be a value type.", nameof(user)); } UserType = user; @@ -39,9 +39,9 @@ public IdentityBuilder(Type user, IServiceCollection services) /// The to attach to. public IdentityBuilder(Type user, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type role, IServiceCollection services) : this(user, services) { - if (!role.IsClass) + if (role.IsValueType) { - throw new ArgumentException("Role type must be a class.", nameof(role)); + throw new ArgumentException("Role type can't be a value type.", nameof(role)); } RoleType = role; From 6ac219951212815a3bc0d9186340f971cdb43cde Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 15:08:03 +0800 Subject: [PATCH 23/45] Apply suggestions from code review Co-authored-by: Stephen Halter Co-authored-by: Eric Erhardt --- .../Hosting/src/GenericHost/GenericWebHostBuilder.cs | 2 +- .../Http.Abstractions/src/Routing/RouteValueDictionary.cs | 6 +++--- src/Identity/Extensions.Core/src/IdentityBuilder.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 905f85b060b4..3898d766a3da 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -344,7 +344,7 @@ static void InvokeContainer(GenericWebHostBuilder genericWebHostBuilder, Configu if (containerType.IsValueType && !RuntimeFeature.IsDynamicCodeSupported) { - throw new InvalidOperationException("ValueType startup container isn't supported with AOT."); + throw new InvalidOperationException("A ValueType TContainerBuilder isn't supported with AOT."); } var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType); diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index 97336e41d00d..b39d91aea9b7 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -588,7 +588,7 @@ public bool TryGetValue(string key, out object? value) "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private bool TryGetValueSlow(string key, out object? value) { if (_propertyStorage != null) @@ -628,7 +628,7 @@ private void EnsureCapacity(int capacity) "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private void EnsureCapacitySlow(int capacity) { if (_propertyStorage != null) @@ -802,7 +802,7 @@ public bool MoveNext() "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] + "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private bool MoveNextRare() { var dictionary = _dictionary; diff --git a/src/Identity/Extensions.Core/src/IdentityBuilder.cs b/src/Identity/Extensions.Core/src/IdentityBuilder.cs index 9b109546f813..22e6731b64b5 100644 --- a/src/Identity/Extensions.Core/src/IdentityBuilder.cs +++ b/src/Identity/Extensions.Core/src/IdentityBuilder.cs @@ -83,7 +83,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The user validator type. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddUserValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TValidator>() where TValidator : class => AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(TValidator)); From d2b083811959094bac89f68d70328ed488fdcdd3 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 15:21:04 +0800 Subject: [PATCH 24/45] PR feedback --- src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 3898d766a3da..0efc32635772 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -271,7 +271,6 @@ void ConfigureStartup(HostBuilderContext context, IServiceCollection services) return this; } - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Constructing untrimmed generic types and methods with reference types")] private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType, HostBuilderContext context, IServiceCollection services, object? instance = null) { var webHostBuilderContext = GetWebHostBuilderContext(context); From 9dbcc98a9b2b0e53975607341d0e0077f6d63cca Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 16:03:12 +0800 Subject: [PATCH 25/45] PR feedback --- .../Http.Abstractions/src/Routing/RouteValueDictionary.cs | 2 -- src/Http/Routing/src/Matching/JumpTableBuilder.cs | 1 + .../Routing/src/Patterns/DefaultRoutePatternTransformer.cs | 2 -- src/Http/Routing/src/Patterns/RoutePatternFactory.cs | 6 ------ src/Http/Routing/src/Patterns/RoutePatternTransformer.cs | 2 -- 5 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index b39d91aea9b7..ddf6af67132c 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -101,7 +101,6 @@ public RouteValueDictionary() /// Only public instance non-index properties are considered. /// [RequiresUnreferencedCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] // PropertyStorage - [RequiresDynamicCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] // PropertyStorage public RouteValueDictionary(object? values) { if (values is RouteValueDictionary dictionary) @@ -829,7 +828,6 @@ public void Reset() } [RequiresUnreferencedCode("This API is not trim safe - from PropertyHelper")] - [RequiresDynamicCode("This API is not AOT safe - from PropertyHelper")] internal sealed class PropertyStorage { public readonly object Value; diff --git a/src/Http/Routing/src/Matching/JumpTableBuilder.cs b/src/Http/Routing/src/Matching/JumpTableBuilder.cs index 86f08dd4dbfb..11b013979a0a 100644 --- a/src/Http/Routing/src/Matching/JumpTableBuilder.cs +++ b/src/Http/Routing/src/Matching/JumpTableBuilder.cs @@ -88,6 +88,7 @@ public static JumpTable Build(int defaultDestination, int exitDestination, (stri // Use the ILEmitTrieJumpTable if the IL is going to be compiled (not interpreted) return MakeILEmitTrieJumpTableIfSupported(defaultDestination, exitDestination, pathEntries, fallback); + // TODO: This suppression can be removed when https://github.com/dotnet/linker/issues/2715 is complete. [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Guarded by IsDynamicCodeCompiled")] static JumpTable MakeILEmitTrieJumpTableIfSupported(int defaultDestination, int exitDestination, (string text, int destination)[] pathEntries, JumpTable fallback) { diff --git a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs index 5155cbac6d11..461af9e90ff6 100644 --- a/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs +++ b/src/Http/Routing/src/Patterns/DefaultRoutePatternTransformer.cs @@ -23,8 +23,6 @@ public DefaultRoutePatternTransformer(ParameterPolicyFactory policyFactory) [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + "Consider using a different overload to avoid this issue.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + - "Consider using a different overload to avoid this issue.")] // RouteValueDictionary public override RoutePattern SubstituteRequiredValues(RoutePattern original, object requiredValues) { if (original == null) diff --git a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs index 562927175c69..ee0ab0d2a075 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs @@ -55,7 +55,6 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern) /// /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? defaults, object? parameterPolicies) { if (pattern == null) @@ -208,7 +207,6 @@ public static RoutePattern Pattern(string? rawText, IEnumerableThe collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( object? defaults, object? parameterPolicies, @@ -271,7 +269,6 @@ public static RoutePattern Pattern( /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( string? rawText, object? defaults, @@ -367,7 +364,6 @@ public static RoutePattern Pattern(string rawText, params RoutePatternPathSegmen /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( object? defaults, object? parameterPolicies, @@ -430,7 +426,6 @@ public static RoutePattern Pattern( /// The collection of segments. /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static RoutePattern Pattern( string? rawText, object? defaults, @@ -1215,7 +1210,6 @@ private static RoutePatternParameterPolicyReference ParameterPolicyCore(IParamet } [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary private static RouteValueDictionary? Wrap(object? values) { return values is null ? null : new RouteValueDictionary(values); diff --git a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs index 104040954847..943b19c36a3e 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternTransformer.cs @@ -34,8 +34,6 @@ public abstract class RoutePatternTransformer /// [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + "Consider using a different overload to avoid this issue.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly." + - "Consider using a different overload to avoid this issue.")] public abstract RoutePattern? SubstituteRequiredValues(RoutePattern original, object requiredValues); /// From 4f9f679dc8003d20117b1dad051fb747bd5abab6 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 21:08:44 +0800 Subject: [PATCH 26/45] PR feedback --- .../src/RequestDelegateFactory.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 3cba67ce01e4..bcd9b57373e9 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -28,12 +28,8 @@ namespace Microsoft.AspNetCore.Http; /// /// Creates implementations from request handlers. /// -[UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] -[UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] -[UnconditionalSuppressMessage("Trimmer", "IL2072", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] -[UnconditionalSuppressMessage("Trimmer", "IL2075", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] -[UnconditionalSuppressMessage("Trimmer", "IL2077", Justification = "RequestDelegateFactory.Create has RequiresUnreferencedCode.")] -[UnconditionalSuppressMessage("Trimmer", "IL3050", Justification = "RequestDelegateFactory.Create has RequiresDynamicCode.")] +[RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] +[RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static partial class RequestDelegateFactory { private static readonly ParameterBindingMethodCache ParameterBindingMethodCache = new(); @@ -125,8 +121,6 @@ public static partial class RequestDelegateFactory /// The for the route handler to be passed to . /// The options that will be used when calling . /// The to be passed to . - [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] - [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateMetadataResult InferMetadata(MethodInfo methodInfo, RequestDelegateFactoryOptions? options = null) { var factoryContext = CreateFactoryContext(options); @@ -144,8 +138,6 @@ public static RequestDelegateMetadataResult InferMetadata(MethodInfo methodInfo, /// A request handler with any number of custom parameters that often produces a response with its return value. /// The used to configure the behavior of the handler. /// The . - [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] - [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options) { return Create(handler, options, metadataResult: null); @@ -163,8 +155,6 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact /// with that metadata. Otherwise, this metadata inference will be skipped as this step has already been done. /// /// The . - [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] - [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads.", Justification = "Required to maintain compatibility")] public static RequestDelegateResult Create(Delegate handler, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null) { @@ -202,8 +192,6 @@ public static RequestDelegateResult Create(Delegate handler, RequestDelegateFact /// Creates the for the non-static method. /// The used to configure the behavior of the handler. /// The . - [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] - [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory, RequestDelegateFactoryOptions? options) { return Create(methodInfo, targetFactory, options, metadataResult: null); @@ -222,8 +210,6 @@ public static RequestDelegateResult Create(MethodInfo methodInfo, Func /// The . - [RequiresUnreferencedCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] - [RequiresDynamicCode("RequestDelegateFactory performs object creation, serialization and deserialization on the delegates and its parameters. This cannot be statically analyzed.")] [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required to maintain compatibility")] public static RequestDelegateResult Create(MethodInfo methodInfo, Func? targetFactory = null, RequestDelegateFactoryOptions? options = null, RequestDelegateMetadataResult? metadataResult = null) { From 361b03e7dab6f7edd91a86d268d3e833de51baad Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 21:42:54 +0800 Subject: [PATCH 27/45] PR feedback --- src/Http/Routing/src/RouteEndpointDataSource.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index 035a64a4560c..b84ce5830dbf 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -112,9 +112,11 @@ internal RouteEndpointBuilder GetSingleRouteEndpointBuilder() } [UnconditionalSuppressMessage("Trimmer", "IL2026", - Justification = "We surface a RequireUnreferencedCode in the call to the Map method adding this EndpointDataSource. The trimmer is unable to infer this.")] + Justification = "We surface a RequireUnreferencedCode in the call to the Map methods adding route handlers to this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use trimmer unsafe features.")] [UnconditionalSuppressMessage("AOT", "IL3050", - Justification = "We surface a RequiresDynamicCode in the call to the Map method adding this EndpointDataSource. The trimmer is unable to infer this.")] + Justification = "We surface a RequiresDynamicCode in the call to the Map methods adding route handlers this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use AOT unsafe features.")] private RouteEndpointBuilder CreateRouteEndpointBuilder( RouteEntry entry, RoutePattern? groupPrefix = null, IReadOnlyList>? groupConventions = null, IReadOnlyList>? groupFinallyConventions = null) { From a7821981d4557a1c13059c9833f4de495ddbc304 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 21:50:02 +0800 Subject: [PATCH 28/45] PR feedback --- .../src/HttpRequestJsonExtensions.cs | 10 ++++++---- .../src/HttpResponseJsonExtensions.cs | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs index 06958f6d113d..6f4a60a9fd3f 100644 --- a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs @@ -21,6 +21,8 @@ public static class HttpRequestJsonExtensions { private const string RequiresUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved."; + private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. " + + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications."; /// /// Read JSON from the request and deserialize to the specified type. @@ -31,7 +33,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static ValueTask ReadFromJsonAsync( this HttpRequest request, CancellationToken cancellationToken = default) @@ -49,7 +51,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static async ValueTask ReadFromJsonAsync( this HttpRequest request, JsonSerializerOptions? options, @@ -133,7 +135,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static ValueTask ReadFromJsonAsync( this HttpRequest request, Type type, @@ -152,7 +154,7 @@ public static class HttpRequestJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static async ValueTask ReadFromJsonAsync( this HttpRequest request, Type type, diff --git a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs index 0ace67acf5c6..e38090586ce7 100644 --- a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs @@ -18,6 +18,8 @@ public static partial class HttpResponseJsonExtensions { private const string RequiresUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved."; + private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. " + + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications."; /// /// Write the specified value as JSON to the response body. The response content-type will be set to @@ -29,7 +31,7 @@ public static partial class HttpResponseJsonExtensions /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -49,7 +51,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -71,7 +73,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, TValue value, @@ -143,7 +145,7 @@ static async Task WriteAsJsonAsyncSlow(HttpResponse response, TValue value, Json } [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] private static async Task WriteAsJsonAsyncSlow( Stream body, TValue value, @@ -167,7 +169,7 @@ private static async Task WriteAsJsonAsyncSlow( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -188,7 +190,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -211,7 +213,7 @@ public static Task WriteAsJsonAsync( /// A used to cancel the operation. /// The task object representing the asynchronous operation. [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] public static Task WriteAsJsonAsync( this HttpResponse response, object? value, @@ -243,7 +245,7 @@ public static Task WriteAsJsonAsync( } [RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)] - [RequiresDynamicCode(RequiresUnreferencedCodeMessage)] + [RequiresDynamicCode(RequiresDynamicCodeMessage)] private static async Task WriteAsJsonAsyncSlow( Stream body, object? value, From f2846a894ce53f27b6d2a96c5ee539db2bdb4f95 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 21:54:41 +0800 Subject: [PATCH 29/45] PR feedback --- .../Builder/EndpointRouteBuilderExtensions.cs | 47 ++++++++++--------- .../FallbackEndpointRouteBuilderExtensions.cs | 2 - 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs index cedeff33ad9d..97f01d862be2 100644 --- a/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointRouteBuilderExtensions.cs @@ -15,7 +15,8 @@ namespace Microsoft.AspNetCore.Builder; /// public static class EndpointRouteBuilderExtensions { - internal const string MapEndpointTrimmerWarning = "This API may perform reflection on the supplied delegate and its parameters. These types may be trimmed if not directly referenced."; + private const string MapEndpointUnreferencedCodeWarning = "This API may perform reflection on the supplied delegate and its parameters. These types may be trimmed if not directly referenced."; + private const string MapEndpointDynamicCodeWarning = "This API may perform reflection on the supplied delegate and its parameters. These types may require generated code and aren't compatible with native AOT applications."; // Avoid creating a new array every call private static readonly string[] GetVerb = new[] { HttpMethods.Get }; @@ -206,8 +207,8 @@ private static IEndpointConventionBuilder Map( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapGet( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -224,8 +225,8 @@ public static RouteHandlerBuilder MapGet( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapPost( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -242,8 +243,8 @@ public static RouteHandlerBuilder MapPost( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapPut( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -260,8 +261,8 @@ public static RouteHandlerBuilder MapPut( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapDelete( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -278,8 +279,8 @@ public static RouteHandlerBuilder MapDelete( /// The route pattern. /// The executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapPatch( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -297,8 +298,8 @@ public static RouteHandlerBuilder MapPatch( /// The delegate executed when the endpoint is matched. /// HTTP methods that the endpoint will match. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapMethods( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -317,8 +318,8 @@ public static RouteHandlerBuilder MapMethods( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -335,8 +336,8 @@ public static RouteHandlerBuilder Map( /// The route pattern. /// The delegate executed when the endpoint is matched. /// A that can be used to further customize the endpoint. - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, @@ -364,8 +365,8 @@ public static RouteHandlerBuilder Map( /// {*path:nonfile}. The order of the registered endpoint will be int.MaxValue. /// /// - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoints, Delegate handler) { return endpoints.MapFallback("{*path:nonfile}", handler); @@ -392,8 +393,8 @@ public static RouteHandlerBuilder MapFallback(this IEndpointRouteBuilder endpoin /// to exclude requests for static files. /// /// - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] public static RouteHandlerBuilder MapFallback( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, @@ -402,8 +403,8 @@ public static RouteHandlerBuilder MapFallback( return endpoints.Map(RoutePatternFactory.Parse(pattern), handler, httpMethods: null, isFallback: true); } - [RequiresUnreferencedCode(MapEndpointTrimmerWarning)] - [RequiresDynamicCode(MapEndpointTrimmerWarning)] + [RequiresUnreferencedCode(MapEndpointUnreferencedCodeWarning)] + [RequiresDynamicCode(MapEndpointDynamicCodeWarning)] private static RouteHandlerBuilder Map( this IEndpointRouteBuilder endpoints, RoutePattern pattern, diff --git a/src/Http/Routing/src/Builder/FallbackEndpointRouteBuilderExtensions.cs b/src/Http/Routing/src/Builder/FallbackEndpointRouteBuilderExtensions.cs index 173cfb59f468..d8c851bbd7dd 100644 --- a/src/Http/Routing/src/Builder/FallbackEndpointRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/FallbackEndpointRouteBuilderExtensions.cs @@ -36,7 +36,6 @@ public static class FallbackEndpointRouteBuilderExtensions /// {*path:nonfile}. The order of the registered endpoint will be int.MaxValue. /// /// - [RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)] public static IEndpointConventionBuilder MapFallback(this IEndpointRouteBuilder endpoints, RequestDelegate requestDelegate) { ArgumentNullException.ThrowIfNull(endpoints); @@ -66,7 +65,6 @@ public static IEndpointConventionBuilder MapFallback(this IEndpointRouteBuilder /// to exclude requests for static files. /// /// - [RequiresUnreferencedCode(EndpointRouteBuilderExtensions.MapEndpointTrimmerWarning)] public static IEndpointConventionBuilder MapFallback( this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern, From 946ac3cd46c0c38fb328973e6a6ed0a8bea9ff66 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 22:03:28 +0800 Subject: [PATCH 30/45] PR feedback --- ...kGeneratorEndpointNameAddressExtensions.cs | 4 ---- .../src/MapRouteRouteBuilderExtensions.cs | 4 ---- .../src/Patterns/RoutePatternFactory.cs | 1 - .../Core/src/IdentityBuilderExtensions.cs | 8 ++++---- .../Extensions.Core/src/IdentityBuilder.cs | 20 +++++++++---------- .../ObjectMethodExecutor.cs | 8 ++------ 6 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs index bcb3fb530831..3ede96df1e54 100644 --- a/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs +++ b/src/Http/Routing/src/LinkGeneratorEndpointNameAddressExtensions.cs @@ -28,7 +28,6 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByName( this LinkGenerator generator, HttpContext httpContext, @@ -128,7 +127,6 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByName( this LinkGenerator generator, string endpointName, @@ -217,7 +215,6 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByName( this LinkGenerator generator, HttpContext httpContext, @@ -353,7 +350,6 @@ public static class LinkGeneratorEndpointNameAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByName( this LinkGenerator generator, string endpointName, diff --git a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs index 33de9fece932..9447793c78d3 100644 --- a/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs +++ b/src/Http/Routing/src/MapRouteRouteBuilderExtensions.cs @@ -23,7 +23,6 @@ public static class MapRouteRouteBuilderExtensions /// The URL pattern of the route. /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -45,7 +44,6 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -72,7 +70,6 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, @@ -104,7 +101,6 @@ public static IRouteBuilder MapRoute( /// /// A reference to this instance after the operation has completed. [RequiresUnreferencedCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] - [RequiresDynamicCode("This API may perform reflection on supplied parameter which may be trimmed if not referenced directly.")] // RouteValueDictionary public static IRouteBuilder MapRoute( this IRouteBuilder routeBuilder, string? name, diff --git a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs index ee0ab0d2a075..d3d6c13748cc 100644 --- a/src/Http/Routing/src/Patterns/RoutePatternFactory.cs +++ b/src/Http/Routing/src/Patterns/RoutePatternFactory.cs @@ -115,7 +115,6 @@ public static RoutePattern Parse([StringSyntax("Route")] string pattern, RouteVa /// /// The . [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static RoutePattern Parse([StringSyntax("Route")] string pattern, object? defaults, object? parameterPolicies, object? requiredValues) { if (pattern == null) diff --git a/src/Identity/Core/src/IdentityBuilderExtensions.cs b/src/Identity/Core/src/IdentityBuilderExtensions.cs index 35f9c34786a3..5a9d26d228d4 100644 --- a/src/Identity/Core/src/IdentityBuilderExtensions.cs +++ b/src/Identity/Core/src/IdentityBuilderExtensions.cs @@ -17,7 +17,7 @@ public static class IdentityBuilderExtensions /// /// The current instance. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")] public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder) { var dataProtectionProviderType = typeof(DataProtectorTokenProvider<>).MakeGenericType(builder.UserType); @@ -30,7 +30,7 @@ public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder buil .AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType); } - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")] private static void AddSignInManagerDeps(this IdentityBuilder builder) { builder.Services.AddHttpContextAccessor(); @@ -43,7 +43,7 @@ private static void AddSignInManagerDeps(this IdentityBuilder builder) /// /// The current instance. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")] public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) { builder.AddSignInManagerDeps(); @@ -58,7 +58,7 @@ public static IdentityBuilder AddSignInManager(this IdentityBuilder builder) /// The type of the sign in manager to add. /// The current instance. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")] public static IdentityBuilder AddSignInManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TSignInManager>(this IdentityBuilder builder) where TSignInManager : class { builder.AddSignInManagerDeps(); diff --git a/src/Identity/Extensions.Core/src/IdentityBuilder.cs b/src/Identity/Extensions.Core/src/IdentityBuilder.cs index 22e6731b64b5..b64111fc77af 100644 --- a/src/Identity/Extensions.Core/src/IdentityBuilder.cs +++ b/src/Identity/Extensions.Core/src/IdentityBuilder.cs @@ -92,7 +92,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The type of the claims principal factory. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddClaimsPrincipalFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFactory>() where TFactory : class => AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory)); @@ -112,7 +112,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The validator type used to validate passwords. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddPasswordValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TValidator>() where TValidator : class => AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator)); @@ -121,7 +121,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// /// The user store type. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddUserStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TStore>() where TStore : class => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore)); @@ -140,7 +140,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers( /// The name of the provider to add. /// The type of the to add. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddTokenProvider(string providerName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type provider) { if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).IsAssignableFrom(provider)) @@ -160,7 +160,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the user manager to add. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddUserManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserManager>() where TUserManager : class { var userManagerType = typeof(UserManager<>).MakeGenericType(UserType); @@ -181,7 +181,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role type. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddRoles<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRole>() where TRole : class { RoleType = typeof(TRole); @@ -196,7 +196,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role validator type. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because RoleType is a reference type.")] public virtual IdentityBuilder AddRoleValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRole>() where TRole : class { if (RoleType == null) @@ -227,7 +227,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The role store. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because RoleType is a reference type.")] public virtual IdentityBuilder AddRoleStore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TStore>() where TStore : class { if (RoleType == null) @@ -242,7 +242,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the role manager to add. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and role type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because RoleType is a reference type.")] public virtual IdentityBuilder AddRoleManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRoleManager>() where TRoleManager : class { if (RoleType == null) @@ -267,7 +267,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall /// /// The type of the user confirmation to add. /// The current instance. - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because generic type and user type are reference types.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because UserType is a reference type.")] public virtual IdentityBuilder AddUserConfirmation<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserConfirmation>() where TUserConfirmation : class => AddScoped(typeof(IUserConfirmation<>).MakeGenericType(UserType), typeof(TUserConfirmation)); } diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs index 4c57e6f50a18..57839cabbab8 100644 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -10,6 +10,8 @@ namespace Microsoft.Extensions.Internal; +[RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] +[RequiresDynamicCode("This method performs reflection on arbitrary types.")] internal sealed class ObjectMethodExecutor { private readonly object?[]? _parameterDefaultValues; @@ -26,8 +28,6 @@ internal sealed class ObjectMethodExecutor typeof(Action) // unsafeOnCompletedMethod })!; - [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] - [RequiresDynamicCode("This method performs reflection on arbitrary types.")] private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]? parameterDefaultValues) { if (methodInfo == null) @@ -77,15 +77,11 @@ private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, obj public bool IsMethodAsync { get; } - [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] - [RequiresDynamicCode("This method performs reflection on arbitrary types.")] public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) { return new ObjectMethodExecutor(methodInfo, targetTypeInfo, null); } - [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] - [RequiresDynamicCode("This method performs reflection on arbitrary types.")] public static ObjectMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[] parameterDefaultValues) { if (parameterDefaultValues == null) From 7a8a57e7b2b85192d7d9fb1a9c1b8ada2dbaaf23 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 22:06:36 +0800 Subject: [PATCH 31/45] PR feedback --- src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs | 2 +- src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs | 2 +- .../Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs index 6f4a60a9fd3f..396400657154 100644 --- a/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs @@ -21,7 +21,7 @@ public static class HttpRequestJsonExtensions { private const string RequiresUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved."; - private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. " + + private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and need runtime code generation. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications."; /// diff --git a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs index e38090586ce7..a4a9b2e80e49 100644 --- a/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs +++ b/src/Http/Http.Extensions/src/HttpResponseJsonExtensions.cs @@ -18,7 +18,7 @@ public static partial class HttpResponseJsonExtensions { private const string RequiresUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved."; - private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. " + + private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and need runtime code generation. " + "Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications."; /// diff --git a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs index 5e11574904e1..c83ab68d1e4e 100644 --- a/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs +++ b/src/Http/Routing/src/LinkGeneratorRouteValuesAddressExtensions.cs @@ -28,7 +28,6 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] public static string? GetPathByRouteValues( this LinkGenerator generator, HttpContext httpContext, @@ -120,7 +119,6 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// A URI with an absolute path, or null. [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetPathByRouteValues( this LinkGenerator generator, string? routeName, @@ -201,7 +199,6 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByRouteValues( this LinkGenerator generator, HttpContext httpContext, @@ -329,7 +326,6 @@ public static class LinkGeneratorRouteValuesAddressExtensions /// /// [RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)] - [RequiresDynamicCode(RouteValueDictionaryTrimmerWarning.Warning)] // RouteValueDictionary public static string? GetUriByRouteValues( this LinkGenerator generator, string? routeName, From f99a84763ff6f7b1c880fc34cc7ac8aeb15624c3 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 16 Dec 2022 22:18:12 +0800 Subject: [PATCH 32/45] Clean up --- src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs index 57839cabbab8..8767cc2b4b7c 100644 --- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs +++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs @@ -10,8 +10,8 @@ namespace Microsoft.Extensions.Internal; -[RequiresUnreferencedCode("This method performs reflection on arbitrary types.")] -[RequiresDynamicCode("This method performs reflection on arbitrary types.")] +[RequiresUnreferencedCode("ObjectMethodExecutor performs reflection on arbitrary types.")] +[RequiresDynamicCode("ObjectMethodExecutor performs reflection on arbitrary types.")] internal sealed class ObjectMethodExecutor { private readonly object?[]? _parameterDefaultValues; From bca72d2b8036d5a490b7681f220f244ed1346218 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 17 Dec 2022 11:29:07 +0800 Subject: [PATCH 33/45] PR feedback --- .../src/Routing/RouteValueDictionary.cs | 6 - src/Shared/PropertyHelper/PropertyHelper.cs | 104 +++++++++--------- 2 files changed, 53 insertions(+), 57 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index ddf6af67132c..f5b71b0b5a1c 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -585,9 +585,6 @@ public bool TryGetValue(string key, out object? value) [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] - [UnconditionalSuppressMessage("AOT", "IL3050", - Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private bool TryGetValueSlow(string key, out object? value) { if (_propertyStorage != null) @@ -625,9 +622,6 @@ private void EnsureCapacity(int capacity) [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] - [UnconditionalSuppressMessage("AOT", "IL3050", - Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private void EnsureCapacitySlow(int capacity) { if (_propertyStorage != null) diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs index 67472463c283..0b752424d55e 100644 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ b/src/Shared/PropertyHelper/PropertyHelper.cs @@ -8,6 +8,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Reflection.Metadata; +using System.Runtime.CompilerServices; [assembly: MetadataUpdateHandler(typeof(Microsoft.Extensions.Internal.PropertyHelper.MetadataUpdateHandler))] @@ -72,15 +73,9 @@ public PropertyHelper(PropertyInfo property) public Func ValueGetter { [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] get { - if (_valueGetter == null) - { - _valueGetter = MakeFastPropertyGetter(Property); - } - - return _valueGetter; + return _valueGetter ??= MakeFastPropertyGetter(Property); } } @@ -90,15 +85,9 @@ public PropertyHelper(PropertyInfo property) public Action ValueSetter { [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] get { - if (_valueSetter == null) - { - _valueSetter = MakeFastPropertySetter(Property); - } - - return _valueSetter; + return _valueSetter ??= MakeFastPropertySetter(Property); } } @@ -108,7 +97,6 @@ public PropertyHelper(PropertyInfo property) /// The object whose property value will be returned. /// The property value. [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public object? GetValue(object instance) { return ValueGetter(instance); @@ -120,7 +108,6 @@ public PropertyHelper(PropertyInfo property) /// The object whose property value will be set. /// The property value. [RequiresUnreferencedCode("This API is not trim safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public void SetValue(object instance, object? value) { ValueSetter(instance, value); @@ -171,7 +158,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -192,7 +178,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. /// [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -204,7 +189,6 @@ public static PropertyHelper[] GetVisibleProperties( } [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] private static Func MakeFastPropertyGetter( PropertyInfo propertyInfo, MethodInfo propertyGetterWrapperMethod, @@ -227,24 +211,34 @@ public static PropertyHelper[] GetVisibleProperties( Debug.Assert(!getMethod.IsStatic); Debug.Assert(getMethod.GetParameters().Length == 0); - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - if (getMethod.DeclaringType!.IsValueType) + if (RuntimeFeature.IsDynamicCodeSupported) { - // Create a delegate (ref TDeclaringType) -> TValue - return MakeFastPropertyGetter( - typeof(ByRefFunc<,>), - getMethod, - propertyGetterByRefWrapperMethod); + // TODO: Remove disable when https://github.com/dotnet/linker/issues/2715 is complete. +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + if (getMethod.DeclaringType!.IsValueType) + { + // Create a delegate (ref TDeclaringType) -> TValue + return MakeFastPropertyGetter( + typeof(ByRefFunc<,>), + getMethod, + propertyGetterByRefWrapperMethod); + } + else + { + // Create a delegate TDeclaringType -> TValue + return MakeFastPropertyGetter( + typeof(Func<,>), + getMethod, + propertyGetterWrapperMethod); + } +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. } else { - // Create a delegate TDeclaringType -> TValue - return MakeFastPropertyGetter( - typeof(Func<,>), - getMethod, - propertyGetterWrapperMethod); + return propertyInfo.GetValue; } } @@ -279,7 +273,6 @@ public static PropertyHelper[] GetVisibleProperties( /// same speed. This only works for reference types. /// [RequiresUnreferencedCode("This API is not trimmer safe.")] - [RequiresDynamicCode("This API requires dynamic code because it makes generic types which may be filled with ValueTypes.")] public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) { Debug.Assert(propertyInfo != null); @@ -292,22 +285,32 @@ public static PropertyHelper[] GetVisibleProperties( var parameters = setMethod.GetParameters(); Debug.Assert(parameters.Length == 1); - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - var typeInput = setMethod.DeclaringType!; - var parameterType = parameters[0].ParameterType; - - // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } - var propertySetterAsAction = - setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); - var callPropertySetterClosedGenericMethod = - CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); - var callPropertySetterDelegate = - callPropertySetterClosedGenericMethod.CreateDelegate( - typeof(Action), propertySetterAsAction); - - return (Action)callPropertySetterDelegate; + if (RuntimeFeature.IsDynamicCodeSupported) + { + // TODO: Remove disable when https://github.com/dotnet/linker/issues/2715 is complete. +#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. + // Instance methods in the CLR can be turned into static methods where the first parameter + // is open over "target". This parameter is always passed by reference, so we have a code + // path for value types and a code path for reference types. + var typeInput = setMethod.DeclaringType!; + var parameterType = parameters[0].ParameterType; + + // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } + var propertySetterAsAction = + setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); + var callPropertySetterClosedGenericMethod = + CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); + var callPropertySetterDelegate = + callPropertySetterClosedGenericMethod.CreateDelegate( + typeof(Action), propertySetterAsAction); + + return (Action)callPropertySetterDelegate; +#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. + } + else + { + return propertyInfo.SetValue; + } } /// @@ -322,7 +325,6 @@ public static PropertyHelper[] GetVisibleProperties( /// faster when the same type is used multiple times with ObjectToDictionary. /// [RequiresUnreferencedCode("Method uses reflection to generate the dictionary.")] - [RequiresDynamicCode("Method uses reflection to generate the dictionary.")] public static IDictionary ObjectToDictionary(object? value) { if (value is IDictionary dictionary) From baf2fb5664e1196fe7a1b3749e0523ca912b9681 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 17 Dec 2022 11:35:53 +0800 Subject: [PATCH 34/45] PR feedback --- src/Shared/EndpointMetadataPopulator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/EndpointMetadataPopulator.cs b/src/Shared/EndpointMetadataPopulator.cs index 425f45c46c47..c17dbf818d59 100644 --- a/src/Shared/EndpointMetadataPopulator.cs +++ b/src/Shared/EndpointMetadataPopulator.cs @@ -11,8 +11,8 @@ namespace Microsoft.AspNetCore.Http; -[UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "Trimmer warnings are presented in RequestDelegateFactory.")] -[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "AOT warnings are presented in RequestDelegateFactory.")] +[RequiresUnreferencedCode("This API performs reflection on types that can't be statically analyzed.")] +[RequiresDynamicCode("This API performs reflection on types that can't be statically analyzed.")] internal static class EndpointMetadataPopulator { private static readonly MethodInfo PopulateMetadataForParameterMethod = typeof(EndpointMetadataPopulator).GetMethod(nameof(PopulateMetadataForParameter), BindingFlags.NonPublic | BindingFlags.Static)!; From 6722fbb5e34a40eb1c933a192aeb6439a6ec9108 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 17 Dec 2022 12:51:39 +0800 Subject: [PATCH 35/45] PR feedback --- .../src/Extensions/EndpointBuilder.cs | 10 +++++++++- src/Http/Http.Extensions/src/RequestDelegateFactory.cs | 6 +++--- .../Routing/src/Builder/EndpointFilterExtensions.cs | 8 ++++++++ src/Http/Routing/src/RouteEndpointDataSource.cs | 6 +++--- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs index c767c4efe954..72646106f342 100644 --- a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs +++ b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Builder; @@ -15,7 +16,14 @@ public abstract class EndpointBuilder /// /// Gets the list of filters that apply to this endpoint. /// - public IList> FilterFactories => _filterFactories ??= new(); + public IList> FilterFactories + { + [RequiresDynamicCode("Filter factories generate dynamic code and aren't compatible with native AOT applications.")] + get + { + return _filterFactories ??= new(); + } + } /// /// Gets or sets the delegate used to process requests for the endpoint. diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index bcd9b57373e9..079d432979b0 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -262,7 +262,7 @@ private static RequestDelegateFactoryContext CreateFactoryContext(RequestDelegat } var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices ?? EmptyServiceProvider.Instance; - var endpointBuilder = options?.EndpointBuilder ?? new RDFEndpointBuilder(serviceProvider); + var endpointBuilder = options?.EndpointBuilder ?? new RdfEndpointBuilder(serviceProvider); var jsonSerializerOptions = serviceProvider.GetService>()?.Value.SerializerOptions; var factoryContext = new RequestDelegateFactoryContext @@ -2525,9 +2525,9 @@ public Task ExecuteAsync(HttpContext httpContext) } } - private sealed class RDFEndpointBuilder : EndpointBuilder + private sealed class RdfEndpointBuilder : EndpointBuilder { - public RDFEndpointBuilder(IServiceProvider applicationServices) + public RdfEndpointBuilder(IServiceProvider applicationServices) { ApplicationServices = applicationServices; } diff --git a/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs b/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs index 7ca0a7d98cb8..f6e39298bda2 100644 --- a/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs @@ -13,12 +13,15 @@ namespace Microsoft.AspNetCore.Http; /// public static class EndpointFilterExtensions { + internal const string FilterRequiresDynamicCodeWarning = "Filter factories generate dynamic code and aren't compatible with native AOT applications."; + /// /// Registers a filter onto the route handler. /// /// The . /// The to register. /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpointFilter filter) where TBuilder : IEndpointConventionBuilder => builder.AddEndpointFilterFactory((routeHandlerContext, next) => (context) => filter.InvokeAsync(context, next)); @@ -29,6 +32,7 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder) where TBuilder : IEndpointConventionBuilder where TFilterType : IEndpointFilter @@ -63,6 +67,7 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static RouteHandlerBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteHandlerBuilder builder) where TFilterType : IEndpointFilter { @@ -76,6 +81,7 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static RouteGroupBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteGroupBuilder builder) where TFilterType : IEndpointFilter { @@ -89,6 +95,7 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The . /// A method representing the core logic of the filter. /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder, Func> routeHandlerFilter) where TBuilder : IEndpointConventionBuilder { @@ -101,6 +108,7 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, FuncThe . /// A method representing the logic for constructing the filter. /// A that can be used to further customize the route handler. + [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilterFactory(this TBuilder builder, Func filterFactory) where TBuilder : IEndpointConventionBuilder { diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index b84ce5830dbf..fada011abb80 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -202,7 +202,7 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( // they can do so via IEndpointConventionBuilder.Finally like the do to override any other entry-specific metadata. if (isRouteHandler) { - rdfOptions = CreateRDFOptions(entry, pattern, builder); + rdfOptions = CreateRdfOptions(entry, pattern, builder); rdfMetadataResult = RequestDelegateFactory.InferMetadata(entry.RouteHandler.Method, rdfOptions); } @@ -227,7 +227,7 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( if (isRouteHandler || builder.FilterFactories.Count > 0) { - rdfOptions ??= CreateRDFOptions(entry, pattern, builder); + rdfOptions ??= CreateRdfOptions(entry, pattern, builder); // We ignore the returned EndpointMetadata has been already populated since we passed in non-null EndpointMetadata. // We always set factoryRequestDelegate in case something is still referencing the redirected version of the RequestDelegate. @@ -259,7 +259,7 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( return builder; } - private RequestDelegateFactoryOptions CreateRDFOptions(RouteEntry entry, RoutePattern pattern, RouteEndpointBuilder builder) + private RequestDelegateFactoryOptions CreateRdfOptions(RouteEntry entry, RoutePattern pattern, RouteEndpointBuilder builder) { var routeParamNames = new List(pattern.Parameters.Count); foreach (var parameter in pattern.Parameters) From 20dd7af3f75e80bcd04c799adde3f85213b69afa Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 20 Dec 2022 20:36:16 +0800 Subject: [PATCH 36/45] Update src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs Co-authored-by: Eric Erhardt --- src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 0efc32635772..c8df1cab87af 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -335,7 +335,7 @@ private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessi }; }); - [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "There is a runtime check for ValueType startup container. It's unlikely anyone will use a ValueType here.")] static void InvokeContainer(GenericWebHostBuilder genericWebHostBuilder, ConfigureContainerBuilder configureContainerBuilder) { From 7b9537fe0b58f845f218f7c92b036d074f1ba208 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 20 Dec 2022 20:36:26 +0800 Subject: [PATCH 37/45] Update src/Hosting/Hosting/src/Internal/StartupLoader.cs Co-authored-by: Eric Erhardt --- src/Hosting/Hosting/src/Internal/StartupLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hosting/Hosting/src/Internal/StartupLoader.cs b/src/Hosting/Hosting/src/Internal/StartupLoader.cs index 966fef483b1b..8a5332be0470 100644 --- a/src/Hosting/Hosting/src/Internal/StartupLoader.cs +++ b/src/Hosting/Hosting/src/Internal/StartupLoader.cs @@ -64,7 +64,7 @@ public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider return new StartupMethods(instance, configureMethod.Build(instance), builder.Build()); [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] - [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", Justification = "There is a runtime check for ValueType startup container. It's unlikely anyone will use a ValueType here.")] static Type CreateConfigureServicesDelegateBuilder(Type type) { From 4e48ef7119cd9f2c5f04ff82ef2ce244cdfee329 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 20 Dec 2022 22:42:13 +0800 Subject: [PATCH 38/45] [AOT] Fixing the problems with ProblemDetails (#45646) --- .../src/ProblemDetails/ProblemDetails.cs | 10 +- ...lidationProblemDetailsJsonConverterTest.cs | 34 ++++++ .../src/DefaultProblemDetailsWriter.cs | 31 ++++-- .../DeveloperExceptionPageMiddlewareImpl.cs | 63 ++++++++--- .../DeveloperExceptionPageSampleTest.cs | 10 ++ ...lidationProblemDetailsJsonConverterTest.cs | 2 +- ...tpValidationProblemDetailsJsonConverter.cs | 101 ++++++++++++------ .../ProblemDetailsJsonConverter.cs | 37 ++++--- 8 files changed, 215 insertions(+), 73 deletions(-) diff --git a/src/Http/Http.Abstractions/src/ProblemDetails/ProblemDetails.cs b/src/Http/Http.Abstractions/src/ProblemDetails/ProblemDetails.cs index 2d01289cdf19..582154c971a8 100644 --- a/src/Http/Http.Abstractions/src/ProblemDetails/ProblemDetails.cs +++ b/src/Http/Http.Abstractions/src/ProblemDetails/ProblemDetails.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using Microsoft.AspNetCore.Http; @@ -12,6 +13,8 @@ namespace Microsoft.AspNetCore.Mvc; [JsonConverter(typeof(ProblemDetailsJsonConverter))] public class ProblemDetails { + private readonly IDictionary _extensions = new Dictionary(StringComparer.Ordinal); + /// /// A URI reference [RFC3986] that identifies the problem type. This specification encourages that, when /// dereferenced, it provide human-readable documentation for the problem type @@ -59,5 +62,10 @@ public class ProblemDetails /// In particular, complex types or collection types may not round-trip to the original type when using the built-in JSON or XML formatters. /// [JsonExtensionData] - public IDictionary Extensions { get; } = new Dictionary(StringComparer.Ordinal); + public IDictionary Extensions + { + [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] + get => _extensions; + } } diff --git a/src/Http/Http.Abstractions/test/HttpValidationProblemDetailsJsonConverterTest.cs b/src/Http/Http.Abstractions/test/HttpValidationProblemDetailsJsonConverterTest.cs index 06ca5f330c07..838bcd836243 100644 --- a/src/Http/Http.Abstractions/test/HttpValidationProblemDetailsJsonConverterTest.cs +++ b/src/Http/Http.Abstractions/test/HttpValidationProblemDetailsJsonConverterTest.cs @@ -11,6 +11,40 @@ public class HttpValidationProblemDetailsJsonConverterTest { private static JsonSerializerOptions JsonSerializerOptions => new JsonOptions().SerializerOptions; + [Fact] + public void Write_Works() + { + var converter = new HttpValidationProblemDetailsJsonConverter(); + var problemDetails = new HttpValidationProblemDetails(); + + problemDetails.Type = "https://tools.ietf.org/html/rfc9110#section-15.5.5"; + problemDetails.Title = "Not found"; + problemDetails.Status = 404; + problemDetails.Detail = "Product not found"; + problemDetails.Instance = "http://example.com/products/14"; + problemDetails.Extensions["traceId"] = "|37dd3dd5-4a9619f953c40a16."; + problemDetails.Errors.Add("key0", new[] { "error0" }); + problemDetails.Errors.Add("key1", new[] { "error1", "error2" }); + + var ms = new MemoryStream(); + var writer = new Utf8JsonWriter(ms); + converter.Write(writer, problemDetails, JsonSerializerOptions); + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + var document = JsonDocument.Parse(ms); + Assert.Equal(problemDetails.Type, document.RootElement.GetProperty("type").GetString()); + Assert.Equal(problemDetails.Title, document.RootElement.GetProperty("title").GetString()); + Assert.Equal(problemDetails.Status, document.RootElement.GetProperty("status").GetInt32()); + Assert.Equal(problemDetails.Detail, document.RootElement.GetProperty("detail").GetString()); + Assert.Equal(problemDetails.Instance, document.RootElement.GetProperty("instance").GetString()); + Assert.Equal((string)problemDetails.Extensions["traceId"]!, document.RootElement.GetProperty("traceId").GetString()); + var errorsElement = document.RootElement.GetProperty("errors"); + Assert.Equal("error0", errorsElement.GetProperty("key0")[0].GetString()); + Assert.Equal("error1", errorsElement.GetProperty("key1")[0].GetString()); + Assert.Equal("error2", errorsElement.GetProperty("key1")[1].GetString()); + } + [Fact] public void Read_Works() { diff --git a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs index 515164060734..91dadb9ae317 100644 --- a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs +++ b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -48,20 +50,20 @@ public bool CanWrite(ProblemDetailsContext context) } [UnconditionalSuppressMessage("Trimming", "IL2026", - Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed and we need to fallback" + - "to reflection-based. The ProblemDetailsConverter is marked as RequiresUnreferencedCode already.")] + Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with a warning")] [UnconditionalSuppressMessage("Trimming", "IL3050", - Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed and we need to fallback" + - "to reflection-based. The ProblemDetailsConverter is marked as RequiresDynamicCode already.")] + Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with a warning")] public ValueTask WriteAsync(ProblemDetailsContext context) { var httpContext = context.HttpContext; ProblemDetailsDefaults.Apply(context.ProblemDetails, httpContext.Response.StatusCode); _options.CustomizeProblemDetails?.Invoke(context); - if (context.ProblemDetails.Extensions is { Count: 0 }) + // Use source generation serialization in two scenarios: + // 1. There are no extensions. Source generation is faster and works well with trimming. + // 2. Native AOT. In this case only the data types specified on ProblemDetailsJsonContext will work. + if (context.ProblemDetails.Extensions is { Count: 0 } || !RuntimeFeature.IsDynamicCodeSupported) { - // We can use the source generation in this case return new ValueTask(httpContext.Response.WriteAsJsonAsync( context.ProblemDetails, ProblemDetailsJsonContext.Default.ProblemDetails, @@ -74,7 +76,22 @@ public ValueTask WriteAsync(ProblemDetailsContext context) contentType: "application/problem+json")); } + // Additional values are specified on JsonSerializerContext to support some values for extensions. + // For example, the DeveloperExceptionMiddleware serializes its complex type to JsonElement, which problem details then needs to serialize. [JsonSerializable(typeof(ProblemDetails))] + [JsonSerializable(typeof(JsonElement))] + [JsonSerializable(typeof(string))] + [JsonSerializable(typeof(decimal))] + [JsonSerializable(typeof(float))] + [JsonSerializable(typeof(double))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(long))] + [JsonSerializable(typeof(Guid))] + [JsonSerializable(typeof(Uri))] + [JsonSerializable(typeof(TimeSpan))] + [JsonSerializable(typeof(DateTime))] + [JsonSerializable(typeof(DateTimeOffset))] internal sealed partial class ProblemDetailsJsonContext : JsonSerializerContext - { } + { + } } diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs index a3bb30343b05..1d0b4b33d979 100644 --- a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs +++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs @@ -5,11 +5,14 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics.RazorViews; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http.Json; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.FileProviders; @@ -34,6 +37,7 @@ internal class DeveloperExceptionPageMiddlewareImpl private readonly ExceptionDetailsProvider _exceptionDetailsProvider; private readonly Func _exceptionHandler; private static readonly MediaTypeHeaderValue _textHtmlMediaType = new MediaTypeHeaderValue("text/html"); + private readonly ExtensionsExceptionJsonContext _serializationContext; private readonly IProblemDetailsService? _problemDetailsService; /// @@ -45,6 +49,7 @@ internal class DeveloperExceptionPageMiddlewareImpl /// /// The used for writing diagnostic messages. /// The list of registered . + /// The used for serialization. /// The used for writing messages. public DeveloperExceptionPageMiddlewareImpl( RequestDelegate next, @@ -53,6 +58,7 @@ public DeveloperExceptionPageMiddlewareImpl( IWebHostEnvironment hostingEnvironment, DiagnosticSource diagnosticSource, IEnumerable filters, + IOptions? jsonOptions = null, IProblemDetailsService? problemDetailsService = null) { if (next == null) @@ -77,8 +83,8 @@ public DeveloperExceptionPageMiddlewareImpl( _diagnosticSource = diagnosticSource; _exceptionDetailsProvider = new ExceptionDetailsProvider(_fileProvider, _logger, _options.SourceCodeLineCount); _exceptionHandler = DisplayException; + _serializationContext = CreateSerializationContext(jsonOptions?.Value); _problemDetailsService = problemDetailsService; - foreach (var filter in filters.Reverse()) { var nextFilter = _exceptionHandler; @@ -86,6 +92,13 @@ public DeveloperExceptionPageMiddlewareImpl( } } + private static ExtensionsExceptionJsonContext CreateSerializationContext(JsonOptions? jsonOptions) + { + // Create context from configured options to get settings such as PropertyNamePolicy and DictionaryKeyPolicy. + jsonOptions ??= new JsonOptions(); + return new ExtensionsExceptionJsonContext(new JsonSerializerOptions(jsonOptions.SerializerOptions)); + } + /// /// Process an individual request. /// @@ -172,21 +185,7 @@ private async Task DisplayExceptionContent(ErrorContext errorContext) if (_problemDetailsService != null) { - var problemDetails = new ProblemDetails - { - Title = TypeNameHelper.GetTypeDisplayName(errorContext.Exception.GetType()), - Detail = errorContext.Exception.Message, - Status = httpContext.Response.StatusCode - }; - - problemDetails.Extensions["exception"] = new - { - Details = errorContext.Exception.ToString(), - Headers = httpContext.Request.Headers, - Path = httpContext.Request.Path.ToString(), - Endpoint = httpContext.GetEndpoint()?.ToString(), - RouteValues = httpContext.Features.Get()?.RouteValues, - }; + var problemDetails = CreateProblemDetails(errorContext, httpContext); await _problemDetailsService.WriteAsync(new() { @@ -214,6 +213,31 @@ await _problemDetailsService.WriteAsync(new() } } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Values set on ProblemDetails.Extensions are supported by the default writer.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Values set on ProblemDetails.Extensions are supported by the default writer.")] + private ProblemDetails CreateProblemDetails(ErrorContext errorContext, HttpContext httpContext) + { + var problemDetails = new ProblemDetails + { + Title = TypeNameHelper.GetTypeDisplayName(errorContext.Exception.GetType()), + Detail = errorContext.Exception.Message, + Status = httpContext.Response.StatusCode + }; + + // Problem details source gen serialization doesn't know about IHeaderDictionary or RouteValueDictionary. + // Serialize payload to a JsonElement here. Problem details serialization can write JsonElement in extensions dictionary. + problemDetails.Extensions["exception"] = JsonSerializer.SerializeToElement(new ExceptionExtensionData + ( + Details: errorContext.Exception.ToString(), + Headers: httpContext.Request.Headers, + Path: httpContext.Request.Path.ToString(), + Endpoint: httpContext.GetEndpoint()?.ToString(), + RouteValues: httpContext.Features.Get()?.RouteValues + ), _serializationContext.ExceptionExtensionData); + + return problemDetails; + } + private Task DisplayCompilationException( HttpContext context, ICompilationException compilationException) @@ -328,3 +352,10 @@ private Task DisplayRuntimeException(HttpContext context, Exception ex) return errorPage.ExecuteAsync(context); } } + +internal record ExceptionExtensionData(string Details, IHeaderDictionary Headers, string Path, string? Endpoint, RouteValueDictionary? RouteValues); + +[JsonSerializable(typeof(ExceptionExtensionData))] +internal sealed partial class ExtensionsExceptionJsonContext : JsonSerializerContext +{ +} diff --git a/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs b/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs index 25770a6d51a1..ba9a28039657 100644 --- a/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs +++ b/src/Middleware/Diagnostics/test/FunctionalTests/DeveloperExceptionPageSampleTest.cs @@ -6,6 +6,7 @@ using System.Net.Http.Headers; using Microsoft.AspNetCore.Mvc; using System.Net.Http.Json; +using System.Text.Json; namespace Microsoft.AspNetCore.Diagnostics.FunctionalTests; @@ -49,5 +50,14 @@ public async Task DeveloperExceptionPage_ShowsProblemDetails_WhenHtmlNotAccepted Assert.NotNull(body); Assert.Equal(500, body.Status); Assert.Contains("Demonstration exception", body.Detail); + + var exceptionNode = (JsonElement)body.Extensions["exception"]; + Assert.Contains("System.Exception: Demonstration exception.", exceptionNode.GetProperty("details").GetString()); + Assert.Equal("application/json", exceptionNode.GetProperty("headers").GetProperty("Accept")[0].GetString()); + Assert.Equal("localhost", exceptionNode.GetProperty("headers").GetProperty("Host")[0].GetString()); + Assert.Equal("/", exceptionNode.GetProperty("path").GetString()); + Assert.Equal("Endpoint display name", exceptionNode.GetProperty("endpoint").GetString()); + Assert.Equal("Value1", exceptionNode.GetProperty("routeValues").GetProperty("routeValue1").GetString()); + Assert.Equal("Value2", exceptionNode.GetProperty("routeValues").GetProperty("routeValue2").GetString()); } } diff --git a/src/Mvc/Mvc.Core/test/Infrastructure/ValidationProblemDetailsJsonConverterTest.cs b/src/Mvc/Mvc.Core/test/Infrastructure/ValidationProblemDetailsJsonConverterTest.cs index b7b877bf38f0..df3ef0d94cd3 100644 --- a/src/Mvc/Mvc.Core/test/Infrastructure/ValidationProblemDetailsJsonConverterTest.cs +++ b/src/Mvc/Mvc.Core/test/Infrastructure/ValidationProblemDetailsJsonConverterTest.cs @@ -148,7 +148,7 @@ public void WriteWorks() using Utf8JsonWriter writer = new(stream); // Act - converter.Write(writer, problemDetails, null); + converter.Write(writer, problemDetails, new JsonSerializerOptions()); writer.Flush(); var json = Encoding.UTF8.GetString(stream.ToArray()); diff --git a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs index f1f3f9d2b736..d903bf99c34d 100644 --- a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs @@ -1,28 +1,21 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; namespace Microsoft.AspNetCore.Http; -internal sealed partial class HttpValidationProblemDetailsJsonConverter : JsonConverter +internal sealed class HttpValidationProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Errors = JsonEncodedText.Encode("errors"); - [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [UnconditionalSuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] - [UnconditionalSuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] public override HttpValidationProblemDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var problemDetails = new HttpValidationProblemDetails(); return ReadProblemDetails(ref reader, options, problemDetails); } - [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader reader, JsonSerializerOptions options, HttpValidationProblemDetails problemDetails) { if (reader.TokenType != JsonTokenType.StartObject) @@ -34,15 +27,7 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader { if (reader.ValueTextEquals(Errors.EncodedUtf8Bytes)) { - var context = new ErrorsJsonContext(options); - var errors = JsonSerializer.Deserialize(ref reader, context.DictionaryStringStringArray); - if (errors is not null) - { - foreach (var item in errors) - { - problemDetails.Errors[item.Key] = item.Value; - } - } + ReadErrors(ref reader, problemDetails.Errors); } else { @@ -56,34 +41,88 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader } return problemDetails; + + static void ReadErrors(ref Utf8JsonReader reader, IDictionary errors) + { + if (!reader.Read()) + { + throw new JsonException("Unexpected end when reading JSON."); + } + + switch (reader.TokenType) + { + case JsonTokenType.StartObject: + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + var name = reader.GetString()!; + + if (!reader.Read()) + { + throw new JsonException("Unexpected end when reading JSON."); + } + + if (reader.TokenType == JsonTokenType.Null) + { + errors[name] = null!; + } + else + { + var values = new List(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + values.Add(reader.GetString()!); + } + errors[name] = values.ToArray(); + } + } + break; + case JsonTokenType.Null: + return; + default: + throw new JsonException($"Unexpected token when reading errors: {reader.TokenType}"); + } + } } - [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [UnconditionalSuppressMessage("Trimming", "IL2046:'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] - [UnconditionalSuppressMessage("AOT", "IL3051:'RequiresDynamicCodeAttribute' annotations must match across all interface implementations or overrides.", Justification = "")] public override void Write(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { WriteProblemDetails(writer, value, options); } - [RequiresUnreferencedCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] - [RequiresDynamicCode("JSON serialization and deserialization of ProblemDetails.Extensions might require types that cannot be statically analyzed.")] public static void WriteProblemDetails(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) { writer.WriteStartObject(); ProblemDetailsJsonConverter.WriteProblemDetails(writer, value, options); writer.WritePropertyName(Errors); - - var context = new ErrorsJsonContext(options); - JsonSerializer.Serialize(writer, value.Errors, context.IDictionaryStringStringArray); + WriteErrors(writer, value, options); writer.WriteEndObject(); - } - [JsonSerializable(typeof(IDictionary))] - [JsonSerializable(typeof(Dictionary))] - private sealed partial class ErrorsJsonContext : JsonSerializerContext - { } + static void WriteErrors(Utf8JsonWriter writer, HttpValidationProblemDetails value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + foreach (var kvp in value.Errors) + { + var name = kvp.Key; + var errors = kvp.Value; + + writer.WritePropertyName(options.DictionaryKeyPolicy?.ConvertName(name) ?? name); + if (errors is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteStartArray(); + foreach (var error in errors) + { + writer.WriteStringValue(error); + } + writer.WriteEndArray(); + } + } + writer.WriteEndObject(); + } + } } diff --git a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs index a523de3e4aad..13fa59608f2b 100644 --- a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs +++ b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Http; -internal sealed partial class ProblemDetailsJsonConverter : JsonConverter +internal sealed class ProblemDetailsJsonConverter : JsonConverter { private static readonly JsonEncodedText Type = JsonEncodedText.Encode("type"); private static readonly JsonEncodedText Title = JsonEncodedText.Encode("title"); @@ -16,10 +16,6 @@ internal sealed partial class ProblemDetailsJsonConverter : JsonConverter Date: Tue, 20 Dec 2022 22:51:44 +0800 Subject: [PATCH 39/45] PR feedback --- src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index f5b71b0b5a1c..49ef0f0a51fd 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -793,9 +793,6 @@ public bool MoveNext() [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresUnreferencedCodeAttribute. " + "We do not need to additionally produce an error in this method since it is shared by trimmer friendly code paths.")] - [UnconditionalSuppressMessage("AOT", "IL3050", - Justification = "The constructor that would result in _propertyStorage being non-null is annotated with RequiresDynamicCodeAttribute. " + - "We do not need to additionally produce an error in this method since it is shared by AOT friendly code paths.")] private bool MoveNextRare() { var dictionary = _dictionary; From 3b27d80b747b75d3f8cbef2259f49fbde952269e Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 21 Dec 2022 10:47:41 +0800 Subject: [PATCH 40/45] PR feedback --- .../src/Extensions/EndpointBuilder.cs | 10 +--- .../src/Builder/EndpointFilterExtensions.cs | 8 --- .../Routing/src/RouteEndpointDataSource.cs | 50 ++++++++++++------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs index 72646106f342..c767c4efe954 100644 --- a/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs +++ b/src/Http/Http.Abstractions/src/Extensions/EndpointBuilder.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics.CodeAnalysis; using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Builder; @@ -16,14 +15,7 @@ public abstract class EndpointBuilder /// /// Gets the list of filters that apply to this endpoint. /// - public IList> FilterFactories - { - [RequiresDynamicCode("Filter factories generate dynamic code and aren't compatible with native AOT applications.")] - get - { - return _filterFactories ??= new(); - } - } + public IList> FilterFactories => _filterFactories ??= new(); /// /// Gets or sets the delegate used to process requests for the endpoint. diff --git a/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs b/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs index f6e39298bda2..7ca0a7d98cb8 100644 --- a/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs +++ b/src/Http/Routing/src/Builder/EndpointFilterExtensions.cs @@ -13,15 +13,12 @@ namespace Microsoft.AspNetCore.Http; /// public static class EndpointFilterExtensions { - internal const string FilterRequiresDynamicCodeWarning = "Filter factories generate dynamic code and aren't compatible with native AOT applications."; - /// /// Registers a filter onto the route handler. /// /// The . /// The to register. /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpointFilter filter) where TBuilder : IEndpointConventionBuilder => builder.AddEndpointFilterFactory((routeHandlerContext, next) => (context) => filter.InvokeAsync(context, next)); @@ -32,7 +29,6 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder) where TBuilder : IEndpointConventionBuilder where TFilterType : IEndpointFilter @@ -67,7 +63,6 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static RouteHandlerBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteHandlerBuilder builder) where TFilterType : IEndpointFilter { @@ -81,7 +76,6 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The type of the to register. /// The . /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static RouteGroupBuilder AddEndpointFilter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFilterType>(this RouteGroupBuilder builder) where TFilterType : IEndpointFilter { @@ -95,7 +89,6 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, IEndpo /// The . /// A method representing the core logic of the filter. /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilter(this TBuilder builder, Func> routeHandlerFilter) where TBuilder : IEndpointConventionBuilder { @@ -108,7 +101,6 @@ public static TBuilder AddEndpointFilter(this TBuilder builder, FuncThe . /// A method representing the logic for constructing the filter. /// A that can be used to further customize the route handler. - [RequiresDynamicCode(FilterRequiresDynamicCodeWarning)] public static TBuilder AddEndpointFilterFactory(this TBuilder builder, Func filterFactory) where TBuilder : IEndpointConventionBuilder { diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index fada011abb80..06cd976fafdb 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -111,12 +111,6 @@ internal RouteEndpointBuilder GetSingleRouteEndpointBuilder() return CreateRouteEndpointBuilder(_routeEntries[0]); } - [UnconditionalSuppressMessage("Trimmer", "IL2026", - Justification = "We surface a RequireUnreferencedCode in the call to the Map methods adding route handlers to this EndpointDataSource. Analysis is unable to infer this. " + - "Map methods that configure a RequestDelegate don't use trimmer unsafe features.")] - [UnconditionalSuppressMessage("AOT", "IL3050", - Justification = "We surface a RequiresDynamicCode in the call to the Map methods adding route handlers this EndpointDataSource. Analysis is unable to infer this. " + - "Map methods that configure a RequestDelegate don't use AOT unsafe features.")] private RouteEndpointBuilder CreateRouteEndpointBuilder( RouteEntry entry, RoutePattern? groupPrefix = null, IReadOnlyList>? groupConventions = null, IReadOnlyList>? groupFinallyConventions = null) { @@ -196,16 +190,6 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( RequestDelegateFactoryOptions? rdfOptions = null; RequestDelegateMetadataResult? rdfMetadataResult = null; - // Any metadata inferred directly inferred by RDF or indirectly inferred via IEndpoint(Parameter)MetadataProviders are - // considered less specific than method-level attributes and conventions but more specific than group conventions - // so inferred metadata gets added in between these. If group conventions need to override inferred metadata, - // they can do so via IEndpointConventionBuilder.Finally like the do to override any other entry-specific metadata. - if (isRouteHandler) - { - rdfOptions = CreateRdfOptions(entry, pattern, builder); - rdfMetadataResult = RequestDelegateFactory.InferMetadata(entry.RouteHandler.Method, rdfOptions); - } - // Add delegate attributes as metadata before entry-specific conventions but after group conventions. var attributes = handler.Method.GetCustomAttributes(); if (attributes is not null) @@ -225,13 +209,23 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( // If no convention has modified builder.RequestDelegate, we can use the RequestDelegate returned by the RequestDelegateFactory directly. var conventionOverriddenRequestDelegate = ReferenceEquals(builder.RequestDelegate, redirectRequestDelegate) ? null : builder.RequestDelegate; + // Any metadata inferred directly inferred by RDF or indirectly inferred via IEndpoint(Parameter)MetadataProviders are + // considered less specific than method-level attributes and conventions but more specific than group conventions + // so inferred metadata gets added in between these. If group conventions need to override inferred metadata, + // they can do so via IEndpointConventionBuilder.Finally like the do to override any other entry-specific metadata. + if (isRouteHandler) + { + rdfOptions = CreateRdfOptions(entry, pattern, builder); + rdfMetadataResult = InferHandlerMetadata(entry.RouteHandler.Method, rdfOptions); + } + if (isRouteHandler || builder.FilterFactories.Count > 0) { rdfOptions ??= CreateRdfOptions(entry, pattern, builder); // We ignore the returned EndpointMetadata has been already populated since we passed in non-null EndpointMetadata. // We always set factoryRequestDelegate in case something is still referencing the redirected version of the RequestDelegate. - factoryCreatedRequestDelegate = RequestDelegateFactory.Create(entry.RouteHandler, rdfOptions, rdfMetadataResult).RequestDelegate; + factoryCreatedRequestDelegate = CreateHandlerRequestDelegate(entry.RouteHandler, rdfOptions, rdfMetadataResult); } Debug.Assert(factoryCreatedRequestDelegate is not null); @@ -257,6 +251,28 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( } return builder; + + [UnconditionalSuppressMessage("Trimmer", "IL2026", + Justification = "We surface a RequireUnreferencedCode in the call to the Map methods adding route handlers to this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use trimmer unsafe features.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "We surface a RequiresDynamicCode in the call to the Map methods adding route handlers this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use AOT unsafe features.")] + static RequestDelegateMetadataResult InferHandlerMetadata(MethodInfo methodInfo, RequestDelegateFactoryOptions? options = null) + { + return RequestDelegateFactory.InferMetadata(methodInfo, options); + } + + [UnconditionalSuppressMessage("Trimmer", "IL2026", + Justification = "We surface a RequireUnreferencedCode in the call to the Map methods adding route handlers to this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use trimmer unsafe features.")] + [UnconditionalSuppressMessage("AOT", "IL3050", + Justification = "We surface a RequiresDynamicCode in the call to the Map methods adding route handlers this EndpointDataSource. Analysis is unable to infer this. " + + "Map methods that configure a RequestDelegate don't use AOT unsafe features.")] + static RequestDelegate CreateHandlerRequestDelegate(Delegate handler, RequestDelegateFactoryOptions options, RequestDelegateMetadataResult? metadataResult) + { + return RequestDelegateFactory.Create(handler, options, metadataResult).RequestDelegate; + } } private RequestDelegateFactoryOptions CreateRdfOptions(RouteEntry entry, RoutePattern pattern, RouteEndpointBuilder builder) From dcd762e1dd2ffe6296fbe9195d6ea6e82f0a4bba Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 21 Dec 2022 10:50:37 +0800 Subject: [PATCH 41/45] Fix --- .../Routing/src/RouteEndpointDataSource.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Http/Routing/src/RouteEndpointDataSource.cs b/src/Http/Routing/src/RouteEndpointDataSource.cs index 06cd976fafdb..1bacad5cd6f0 100644 --- a/src/Http/Routing/src/RouteEndpointDataSource.cs +++ b/src/Http/Routing/src/RouteEndpointDataSource.cs @@ -190,6 +190,16 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( RequestDelegateFactoryOptions? rdfOptions = null; RequestDelegateMetadataResult? rdfMetadataResult = null; + // Any metadata inferred directly inferred by RDF or indirectly inferred via IEndpoint(Parameter)MetadataProviders are + // considered less specific than method-level attributes and conventions but more specific than group conventions + // so inferred metadata gets added in between these. If group conventions need to override inferred metadata, + // they can do so via IEndpointConventionBuilder.Finally like the do to override any other entry-specific metadata. + if (isRouteHandler) + { + rdfOptions = CreateRdfOptions(entry, pattern, builder); + rdfMetadataResult = InferHandlerMetadata(entry.RouteHandler.Method, rdfOptions); + } + // Add delegate attributes as metadata before entry-specific conventions but after group conventions. var attributes = handler.Method.GetCustomAttributes(); if (attributes is not null) @@ -209,16 +219,6 @@ private RouteEndpointBuilder CreateRouteEndpointBuilder( // If no convention has modified builder.RequestDelegate, we can use the RequestDelegate returned by the RequestDelegateFactory directly. var conventionOverriddenRequestDelegate = ReferenceEquals(builder.RequestDelegate, redirectRequestDelegate) ? null : builder.RequestDelegate; - // Any metadata inferred directly inferred by RDF or indirectly inferred via IEndpoint(Parameter)MetadataProviders are - // considered less specific than method-level attributes and conventions but more specific than group conventions - // so inferred metadata gets added in between these. If group conventions need to override inferred metadata, - // they can do so via IEndpointConventionBuilder.Finally like the do to override any other entry-specific metadata. - if (isRouteHandler) - { - rdfOptions = CreateRdfOptions(entry, pattern, builder); - rdfMetadataResult = InferHandlerMetadata(entry.RouteHandler.Method, rdfOptions); - } - if (isRouteHandler || builder.FilterFactories.Count > 0) { rdfOptions ??= CreateRdfOptions(entry, pattern, builder); From c755e789375523cbb3129cff39ea506139a38f42 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Jan 2023 09:43:13 +0800 Subject: [PATCH 42/45] Update src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs Co-authored-by: Eric Erhardt --- src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs index 91dadb9ae317..ea3e318a9a2d 100644 --- a/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs +++ b/src/Http/Http.Extensions/src/DefaultProblemDetailsWriter.cs @@ -50,9 +50,9 @@ public bool CanWrite(ProblemDetailsContext context) } [UnconditionalSuppressMessage("Trimming", "IL2026", - Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with a warning")] + Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with RequiresUnreferencedCode.")] [UnconditionalSuppressMessage("Trimming", "IL3050", - Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with a warning")] + Justification = "JSON serialization of ProblemDetails.Extensions might require types that cannot be statically analyzed. The property is annotated with RequiresDynamicCode.")] public ValueTask WriteAsync(ProblemDetailsContext context) { var httpContext = context.HttpContext; From f6ff8feceda5065de4d5f743547a74f5aa74a216 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Jan 2023 13:55:11 +0800 Subject: [PATCH 43/45] PR feedback --- .../test/Identity.Test/IdentityBuilderTest.cs | 12 ++++++++ .../DeveloperExceptionPageMiddlewareImpl.cs | 28 +++++++++++++++---- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/Identity/test/Identity.Test/IdentityBuilderTest.cs b/src/Identity/test/Identity.Test/IdentityBuilderTest.cs index 9b811d355ec6..faddd25e5c8b 100644 --- a/src/Identity/test/Identity.Test/IdentityBuilderTest.cs +++ b/src/Identity/test/Identity.Test/IdentityBuilderTest.cs @@ -26,6 +26,18 @@ public void AddRolesServicesAdded() Assert.IsType>(sp.GetRequiredService>()); } + [Fact] + public void IdentityBuilder_ValueTypeUser_Error() + { + Assert.Throws(() => new IdentityBuilder(typeof(int), new ServiceCollection())); + } + + [Fact] + public void IdentityBuilder_ValueTypeRole_Error() + { + Assert.Throws(() => new IdentityBuilder(typeof(PocoUser), typeof(int), new ServiceCollection())); + } + [Fact] public void AddRolesWithoutStoreWillError() { diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs index 1d0b4b33d979..0ae63cdf126e 100644 --- a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs +++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs @@ -228,11 +228,11 @@ private ProblemDetails CreateProblemDetails(ErrorContext errorContext, HttpConte // Serialize payload to a JsonElement here. Problem details serialization can write JsonElement in extensions dictionary. problemDetails.Extensions["exception"] = JsonSerializer.SerializeToElement(new ExceptionExtensionData ( - Details: errorContext.Exception.ToString(), - Headers: httpContext.Request.Headers, - Path: httpContext.Request.Path.ToString(), - Endpoint: httpContext.GetEndpoint()?.ToString(), - RouteValues: httpContext.Features.Get()?.RouteValues + details: errorContext.Exception.ToString(), + headers: httpContext.Request.Headers, + path: httpContext.Request.Path.ToString(), + endpoint: httpContext.GetEndpoint()?.ToString(), + routeValues: httpContext.Features.Get()?.RouteValues ), _serializationContext.ExceptionExtensionData); return problemDetails; @@ -353,7 +353,23 @@ private Task DisplayRuntimeException(HttpContext context, Exception ex) } } -internal record ExceptionExtensionData(string Details, IHeaderDictionary Headers, string Path, string? Endpoint, RouteValueDictionary? RouteValues); +internal class ExceptionExtensionData +{ + public ExceptionExtensionData(string details, IHeaderDictionary headers, string path, string? endpoint, RouteValueDictionary? routeValues) + { + Details = details; + Headers = headers; + Path = path; + Endpoint = endpoint; + RouteValues = routeValues; + } + + public string Details { get; } + public IHeaderDictionary Headers { get; } + public string Path { get; } + public string? Endpoint { get; } + public RouteValueDictionary? RouteValues { get; } +} [JsonSerializable(typeof(ExceptionExtensionData))] internal sealed partial class ExtensionsExceptionJsonContext : JsonSerializerContext From 51ca4ece5a05a3191ef428437e2cde993a354728 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Jan 2023 14:01:03 +0800 Subject: [PATCH 44/45] PR feedback --- src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs index 49ef0f0a51fd..abf2896c88ee 100644 --- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs +++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs @@ -100,7 +100,7 @@ public RouteValueDictionary() /// property names are keys, and property values are the values, and copied into the dictionary. /// Only public instance non-index properties are considered. /// - [RequiresUnreferencedCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] // PropertyStorage + [RequiresUnreferencedCode("This constructor may perform reflection on the specificed value which may be trimmed if not referenced directly. Consider using a different overload to avoid this issue.")] public RouteValueDictionary(object? values) { if (values is RouteValueDictionary dictionary) From 09e0fdf600f4f6ac4d82f18864dc1e2c217cd569 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 4 Jan 2023 14:06:11 +0800 Subject: [PATCH 45/45] PR feedback --- .../DeveloperExceptionPageMiddlewareImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs index 0ae63cdf126e..1af140bb4787 100644 --- a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs +++ b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs @@ -353,7 +353,7 @@ private Task DisplayRuntimeException(HttpContext context, Exception ex) } } -internal class ExceptionExtensionData +internal sealed class ExceptionExtensionData { public ExceptionExtensionData(string details, IHeaderDictionary headers, string path, string? endpoint, RouteValueDictionary? routeValues) {