From 1d9f86b729af65c37168ac691aa843fb4647260a Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 3 Apr 2023 15:44:24 +0100 Subject: [PATCH] Expose the feature switch as a property on JsonSerializer -- rename feature switch to match namespace. --- .../System.Text.Json/ref/System.Text.Json.cs | 1 + .../src/ILLink/ILLink.Substitutions.xml | 6 ++-- .../Text/Json/AppContextSwitchHelper.cs | 6 ---- .../Serialization/JsonSerializer.Helpers.cs | 15 ++++++++++ .../JsonSerializerOptions.Converters.cs | 2 +- .../Serialization/JsonSerializerOptions.cs | 4 +-- .../Serialization/OptionsTests.cs | 30 ++++++++++++++----- .../System.Text.Json.Tests.csproj | 3 ++ ...s => IsReflectionEnabledByDefaultFalse.cs} | 2 +- .../System.Text.Json.TrimmingTests.proj | 4 +-- 10 files changed, 50 insertions(+), 23 deletions(-) rename src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/{UseReflectionDefaultFalse.cs => IsReflectionEnabledByDefaultFalse.cs} (98%) diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 12426171d3f1e..3002b59dcff3b 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -280,6 +280,7 @@ public static partial class JsonSerializer [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("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.")] public static TValue? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.JsonSerializerOptions? options = null) { throw null; } public static TValue? Deserialize(ref System.Text.Json.Utf8JsonReader reader, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { throw null; } + public static bool IsReflectionEnabledByDefault { get { throw null; } } public static void Serialize(System.IO.Stream utf8Json, object? value, System.Text.Json.Serialization.Metadata.JsonTypeInfo jsonTypeInfo) { } [System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications.")] [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("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.")] diff --git a/src/libraries/System.Text.Json/src/ILLink/ILLink.Substitutions.xml b/src/libraries/System.Text.Json/src/ILLink/ILLink.Substitutions.xml index 71a7c627e8ab8..026bbf9f13602 100644 --- a/src/libraries/System.Text.Json/src/ILLink/ILLink.Substitutions.xml +++ b/src/libraries/System.Text.Json/src/ILLink/ILLink.Substitutions.xml @@ -1,8 +1,8 @@ - - + + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs index 6099468cb6df2..3e1291c676d92 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/AppContextSwitchHelper.cs @@ -5,12 +5,6 @@ namespace System.Text.Json { internal static class AppContextSwitchHelper { - public static bool UseReflectionDefault { get; } = - AppContext.TryGetSwitch( - switchName: "System.Text.Json.Serialization.UseReflectionDefault", - isEnabled: out bool value) - ? value : true; - public static bool IsSourceGenReflectionFallbackEnabled { get; } = AppContext.TryGetSwitch( switchName: "System.Text.Json.Serialization.EnableSourceGenReflectionFallback", diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs index a3ccc872a9091..3b4bda03d8e8a 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs @@ -13,6 +13,21 @@ public static partial class JsonSerializer internal const string SerializationUnreferencedCodeMessage = "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."; internal const string SerializationRequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use System.Text.Json source generation for native AOT applications."; + /// + /// Indicates whether unconfigured instances + /// should be set to use the reflection-based . + /// + /// + /// The value of the property is backed by the "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault" + /// feature switch and defaults to if unset. For trimmed applications, disabling the feature switch + /// at link time will result in the property being substituted with a constant value. + /// + public static bool IsReflectionEnabledByDefault { get; } = + AppContext.TryGetSwitch( + switchName: "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", + isEnabled: out bool value) + ? value : true; + [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] private static JsonTypeInfo GetTypeInfo(JsonSerializerOptions? options, Type inputType, bool fallBackToNearestAncestorType = false) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs index f7ddbb39a5432..b6c501903a602 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Converters.cs @@ -45,7 +45,7 @@ public JsonConverter GetConverter(Type typeToConvert) ThrowHelper.ThrowArgumentNullException(nameof(typeToConvert)); } - if (AppContextSwitchHelper.UseReflectionDefault && _typeInfoResolver is null) + if (JsonSerializer.IsReflectionEnabledByDefault && _typeInfoResolver is null) { // Backward compatibility -- root & query the default reflection converters // but do not populate the TypeInfoResolver setting. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 50f65883b5226..5359499e0cbea 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -680,7 +680,7 @@ public void MakeReadOnly() [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] internal void ConfigureForJsonSerializer() { - if (AppContextSwitchHelper.UseReflectionDefault) + if (JsonSerializer.IsReflectionEnabledByDefault) { // Even if a resolver has already been specified, we need to root // the default resolver to gain access to the default converters. @@ -834,7 +834,7 @@ private static JsonSerializerOptions GetOrCreateDefaultOptionsInstance() // we need to specify a resolver instance for the case where // reflection is disabled by default: use one that returns null for all types. - TypeInfoResolver = AppContextSwitchHelper.UseReflectionDefault + TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault ? DefaultJsonTypeInfoResolver.RootDefaultInstance() : new JsonTypeInfoResolverChain(), diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs index 3712a230221ce..d290fd8d259bb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs @@ -481,20 +481,28 @@ public static void Options_JsonSerializerContext_DoesNotFallbackToReflection() Assert.Throws(() => JsonSerializer.Serialize(unsupportedValue, options)); } + [Fact] + public static void JsonSerializer_IsReflectionEnabledByDefault_DefaultsToTrue() + { + Assert.True(JsonSerializer.IsReflectionEnabledByDefault); + } + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_DisablingUseReflectionDefaultSwitch_DefaultOptionsDoesNotSupportReflection() + public static void Options_DisablingIsReflectionEnabledByDefaultSwitch_DefaultOptionsDoesNotSupportReflection() { var options = new RemoteInvokeOptions { RuntimeConfigurationOptions = { - ["System.Text.Json.Serialization.UseReflectionDefault"] = false + ["System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault"] = false } }; RemoteExecutor.Invoke(static () => { + Assert.False(JsonSerializer.IsReflectionEnabledByDefault); + var options = JsonSerializerOptions.Default; Assert.True(options.IsReadOnly); @@ -519,18 +527,20 @@ public static void Options_DisablingUseReflectionDefaultSwitch_DefaultOptionsDoe [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_DisablingUseReflectionDefaultSwitch_NewOptionsDoesNotSupportReflection() + public static void Options_DisablingIsReflectionEnabledByDefaultSwitch_NewOptionsDoesNotSupportReflection() { var options = new RemoteInvokeOptions { RuntimeConfigurationOptions = { - ["System.Text.Json.Serialization.UseReflectionDefault"] = false + ["System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault"] = false } }; RemoteExecutor.Invoke(static () => { + Assert.False(JsonSerializer.IsReflectionEnabledByDefault); + var options = new JsonSerializerOptions(); Assert.False(options.IsReadOnly); @@ -561,18 +571,20 @@ public static void Options_DisablingUseReflectionDefaultSwitch_NewOptionsDoesNot [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_DisablingUseReflectionDefaultSwitch_CanUseSourceGen() + public static void Options_DisablingIsReflectionEnabledByDefaultSwitch_CanUseSourceGen() { var options = new RemoteInvokeOptions { RuntimeConfigurationOptions = { - ["System.Text.Json.Serialization.UseReflectionDefault"] = false + ["System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault"] = false } }; RemoteExecutor.Invoke(static () => { + Assert.False(JsonSerializer.IsReflectionEnabledByDefault); + var options = new JsonSerializerOptions(); options.TypeInfoResolverChain.Add(JsonContext.Default); @@ -654,19 +666,21 @@ public static void Options_JsonSerializerContext_Net6CompatibilitySwitch_FallsBa [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_JsonSerializerContext_Net6CompatibilitySwitch_IsOverriddenByDisablingUseReflectionDefault() + public static void Options_JsonSerializerContext_Net6CompatibilitySwitch_IsOverriddenByDisablingIsReflectionEnabledByDefault() { var options = new RemoteInvokeOptions { RuntimeConfigurationOptions = { - ["System.Text.Json.Serialization.UseReflectionDefault"] = false, + ["System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault"] = false, ["System.Text.Json.Serialization.EnableSourceGenReflectionFallback"] = true } }; RemoteExecutor.Invoke(static () => { + Assert.False(JsonSerializer.IsReflectionEnabledByDefault); + JsonContext context = JsonContext.Default; var unsupportedValue = new MyClass(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 565f6a8ab3daf..25d2d13344463 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -264,6 +264,9 @@ + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/UseReflectionDefaultFalse.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/IsReflectionEnabledByDefaultFalse.cs similarity index 98% rename from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/UseReflectionDefaultFalse.cs rename to src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/IsReflectionEnabledByDefaultFalse.cs index be2827a642ba2..8d4c97720cc6c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/UseReflectionDefaultFalse.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/IsReflectionEnabledByDefaultFalse.cs @@ -12,7 +12,7 @@ public static class Program { // Validates that expected the components are trimmed when - // the UseReflectionDefault feature switch is turned on. + // the IsReflectionEnabledByDefault feature switch is turned on. public static int Main() { MyPoco valueToSerialize = new MyPoco { Value = 42 }; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/System.Text.Json.TrimmingTests.proj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/System.Text.Json.TrimmingTests.proj index d6d8ac87b6f27..db59eac29e719 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/System.Text.Json.TrimmingTests.proj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/System.Text.Json.TrimmingTests.proj @@ -60,8 +60,8 @@ - - System.Text.Json.Serialization.UseReflectionDefault + + System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault