diff --git a/Directory.Build.targets b/Directory.Build.targets
index 4bb0bb189ccd..1980cdcc77b2 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -3,13 +3,15 @@
true
+ $([MSBuild]::ValueOrDefault($(IsTrimmable),'false'))
+
.
///
- public static Func CreateFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type implementation)
+ [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
{
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 3da1ac4e83a7..a67653f56b3d 100644
--- a/src/DefaultBuilder/src/WebApplicationBuilder.cs
+++ b/src/DefaultBuilder/src/WebApplicationBuilder.cs
@@ -33,6 +33,8 @@ internal WebApplicationBuilder(WebApplicationOptions options, 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
@@ -342,6 +334,30 @@ private void UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessi
}
};
});
+
+ [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)
+ {
+ var containerType = configureContainerBuilder.GetContainerType();
+
+ if (containerType.IsValueType && !RuntimeFeature.IsDynamicCodeSupported)
+ {
+ throw new InvalidOperationException("A ValueType TContainerBuilder 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
diff --git a/src/Hosting/Hosting/src/Internal/StartupLoader.cs b/src/Hosting/Hosting/src/Internal/StartupLoader.cs
index ee23eda69c12..8a5332be0470 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: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)
+ {
+ 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/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/src/Routing/RouteValueDictionary.cs b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs
index 03982e3d303c..abf2896c88ee 100644
--- a/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs
+++ b/src/Http/Http.Abstractions/src/Routing/RouteValueDictionary.cs
@@ -818,12 +818,12 @@ public void Reset()
}
}
+ [RequiresUnreferencedCode("This API is not trim 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);
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 2abf10254b5f..ea3e318a9a2d 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,17 +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 RequiresUnreferencedCode.")]
+ [UnconditionalSuppressMessage("Trimming", "IL3050",
+ 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;
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,
@@ -71,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/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs b/src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs
index a204e74c0477..396400657154 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 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,6 +33,7 @@ public static class HttpRequestJsonExtensions
/// A used to cancel the operation.
/// The task object representing the asynchronous operation.
[RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
+ [RequiresDynamicCode(RequiresDynamicCodeMessage)]
public static ValueTask ReadFromJsonAsync(
this HttpRequest request,
CancellationToken cancellationToken = default)
@@ -48,6 +51,7 @@ public static class HttpRequestJsonExtensions
/// A used to cancel the operation.
/// The task object representing the asynchronous operation.
[RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
+ [RequiresDynamicCode(RequiresDynamicCodeMessage)]
public static async ValueTask ReadFromJsonAsync(
this HttpRequest request,
JsonSerializerOptions? options,
@@ -131,6 +135,7 @@ public static class HttpRequestJsonExtensions
/// A used to cancel the operation.
/// The task object representing the asynchronous operation.
[RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
+ [RequiresDynamicCode(RequiresDynamicCodeMessage)]
public static ValueTask
/// The current instance.
/// The current instance.
+ [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")]
public static IdentityBuilder AddDefaultTokenProviders(this IdentityBuilder builder)
{
- var userType = builder.UserType;
- 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)
.AddTokenProvider(TokenOptions.DefaultAuthenticatorProvider, authenticatorProviderType);
}
+ [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")]
private static void AddSignInManagerDeps(this IdentityBuilder builder)
{
builder.Services.AddHttpContextAccessor();
@@ -42,6 +43,7 @@ private static void AddSignInManagerDeps(this IdentityBuilder builder)
///
/// The current instance.
/// The current instance.
+ [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "MakeGenericType is safe because user type is a reference type.")]
public static IdentityBuilder AddSignInManager(this IdentityBuilder builder)
{
builder.AddSignInManagerDeps();
@@ -56,6 +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 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 28e3804e9c93..b64111fc77af 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,11 @@ public class IdentityBuilder
/// The to attach to.
public IdentityBuilder(Type user, IServiceCollection services)
{
+ if (user.IsValueType)
+ {
+ throw new ArgumentException("User type can't be a value type.", nameof(user));
+ }
+
UserType = user;
Services = services;
}
@@ -32,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.IsValueType)
+ {
+ throw new ArgumentException("Role type can't be a value type.", nameof(role));
+ }
+
+ RoleType = role;
+ }
///
/// Gets the used for users.
@@ -70,6 +83,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers(
///
/// The user validator type.
/// The current instance.
+ [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));
@@ -78,6 +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 UserType is a reference type.")]
public virtual IdentityBuilder AddClaimsPrincipalFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TFactory>() where TFactory : class
=> AddScoped(typeof(IUserClaimsPrincipalFactory<>).MakeGenericType(UserType), typeof(TFactory));
@@ -97,6 +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 UserType is a reference type.")]
public virtual IdentityBuilder AddPasswordValidator<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TValidator>() where TValidator : class
=> AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(TValidator));
@@ -105,6 +121,7 @@ private IdentityBuilder AddScoped(Type serviceType, [DynamicallyAccessedMembers(
///
/// The user store type.
/// The current instance.
+ [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));
@@ -123,6 +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 UserType is a reference type.")]
public virtual IdentityBuilder AddTokenProvider(string providerName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type provider)
{
if (!typeof(IUserTwoFactorTokenProvider<>).MakeGenericType(UserType).IsAssignableFrom(provider))
@@ -142,6 +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 UserType is a reference type.")]
public virtual IdentityBuilder AddUserManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserManager>() where TUserManager : class
{
var userManagerType = typeof(UserManager<>).MakeGenericType(UserType);
@@ -162,6 +181,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall
///
/// The role type.
/// The current instance.
+ [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);
@@ -176,6 +196,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall
///
/// The role validator type.
/// The current instance.
+ [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)
@@ -206,6 +227,7 @@ public virtual IdentityBuilder AddTokenProvider(string providerName, [Dynamicall
///
/// The role store.
/// The current instance.
+ [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)
@@ -220,6 +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 RoleType is a reference type.")]
public virtual IdentityBuilder AddRoleManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TRoleManager>() where TRoleManager : class
{
if (RoleType == null)
@@ -244,6 +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 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/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/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 @@
enabletrue$(DefineConstants);JS_INTEROP
+ false
diff --git a/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs b/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddlewareImpl.cs
index a3bb30343b05..1af140bb4787 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,26 @@ private Task DisplayRuntimeException(HttpContext context, Exception ex)
return errorPage.ExecuteAsync(context);
}
}
+
+internal sealed 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
+{
+}
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/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/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/EndpointMetadataPopulator.cs b/src/Shared/EndpointMetadataPopulator.cs
index bf36507d9d0c..c17dbf818d59 100644
--- a/src/Shared/EndpointMetadataPopulator.cs
+++ b/src/Shared/EndpointMetadataPopulator.cs
@@ -11,7 +11,8 @@
namespace Microsoft.AspNetCore.Http;
-[UnconditionalSuppressMessage("Trimmer", "IL2060", Justification = "Trimmer 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)!;
diff --git a/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs b/src/Shared/ObjectMethodExecutor/CoercedAwaitableInfo.cs
index d47f97473557..4951c218b9a8 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("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/ObjectMethodExecutor.cs b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
index 52272f420834..8767cc2b4b7c 100644
--- a/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
+++ b/src/Shared/ObjectMethodExecutor/ObjectMethodExecutor.cs
@@ -10,6 +10,8 @@
namespace Microsoft.Extensions.Internal;
+[RequiresUnreferencedCode("ObjectMethodExecutor performs reflection on arbitrary types.")]
+[RequiresDynamicCode("ObjectMethodExecutor performs reflection on arbitrary types.")]
internal sealed class ObjectMethodExecutor
{
private readonly object?[]? _parameterDefaultValues;
@@ -26,7 +28,6 @@ internal sealed class ObjectMethodExecutor
typeof(Action) // unsafeOnCompletedMethod
})!;
- [RequiresUnreferencedCode("This method performs reflection on arbitrary types.")]
private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, object?[]? parameterDefaultValues)
{
if (methodInfo == null)
@@ -76,13 +77,11 @@ private ObjectMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo, obj
public bool IsMethodAsync { get; }
- [RequiresUnreferencedCode("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.")]
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..95970685f6cb 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.")]
internal static class ObjectMethodExecutorFSharpSupport
{
private static readonly object _fsharpValuesCacheLock = new object();
@@ -30,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 64a994b593b8..c0461a115dc8 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("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 95b0a9cdcda4..d903bf99c34d 100644
--- a/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.cs
+++ b/src/Shared/ProblemDetails/HttpValidationProblemDetailsJsonConverter.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 System.Text.Json;
using System.Text.Json.Serialization;
@@ -11,14 +10,12 @@ 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)
@@ -30,14 +27,7 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader
{
if (reader.ValueTextEquals(Errors.EncodedUtf8Bytes))
{
- var errors = DeserializeErrors(ref reader, options);
- if (errors is not null)
- {
- foreach (var item in errors)
- {
- problemDetails.Errors[item.Key] = item.Value;
- }
- }
+ ReadErrors(ref reader, problemDetails.Errors);
}
else
{
@@ -52,32 +42,87 @@ public static HttpValidationProblemDetails ReadProblemDetails(ref Utf8JsonReader
return problemDetails;
- [UnconditionalSuppressMessage("Trimmer", "IL2026", Justification = "We ensure Dictionary is preserved.")]
- [DynamicDependency(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicProperties, typeof(Dictionary))]
- static Dictionary? DeserializeErrors(ref Utf8JsonReader reader, JsonSerializerOptions options)
- => JsonSerializer.Deserialize>(ref reader, options);
+ 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}");
+ }
+ }
}
- [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();
ProblemDetailsJsonConverter.WriteProblemDetails(writer, value, options);
writer.WritePropertyName(Errors);
- SerializeErrors(writer, value.Errors, options);
+ WriteErrors(writer, value, options);
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);
+ 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 28369eb5e0a2..13fa59608f2b 100644
--- a/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs
+++ b/src/Shared/ProblemDetails/ProblemDetailsJsonConverter.cs
@@ -16,7 +16,6 @@ internal sealed class ProblemDetailsJsonConverter : JsonConverter 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;
}
}
[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,
@@ -283,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;
+ }
}
///