diff --git a/docs/workflow/trimming/feature-switches.md b/docs/workflow/trimming/feature-switches.md index 46e7452fad2c5..40a168d7c25bc 100644 --- a/docs/workflow/trimming/feature-switches.md +++ b/docs/workflow/trimming/feature-switches.md @@ -29,7 +29,6 @@ configurations but their defaults might vary as any SDK can set the defaults dif | _EnableConsumingManagedCodeFromNativeHosting | System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting | Getting a managed function from native hosting is disabled when set to false and related functionality can be trimmed. | | VerifyDependencyInjectionOpenGenericServiceTrimmability | Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability | When set to true, DependencyInjection will verify trimming annotations applied to open generic services are correct | | DisableDependencyInjectionDynamicEngine | Microsoft.Extensions.DependencyInjection.DisableDynamicEngine | When set to true, DependencyInjection will avoid using System.Reflection.Emit when realizing services | -| NullabilityInfoContextSupport | System.Reflection.NullabilityInfoContext.IsSupported | Nullable attributes can be trimmed when set to false | | DynamicCodeSupport | System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported | Changes RuntimeFeature.IsDynamicCodeSupported to false to allow testing AOT-safe fallback code without publishing for Native AOT. | | _AggressiveAttributeTrimming | System.AggressiveAttributeTrimming | When set to true, aggressively trims attributes to allow for the most size savings possible, even if it could result in runtime behavior changes | | JsonSerializerIsReflectionEnabledByDefault | System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault | When set to false, disables using reflection as the default contract resolver in System.Text.Json | diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml index 7706dd2ce18fe..db98894014bc5 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.LinkAttributes.Shared.xml @@ -57,37 +57,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml index 4ddeabcb227f1..465c0d5f87b37 100644 --- a/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml +++ b/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.Shared.xml @@ -36,9 +36,6 @@ - - - diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index 1f1669a04359c..bb7058f936c7c 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4058,9 +4058,6 @@ Unable to properly load any time zone data files. - - NullabilityInfoContext is not supported in the current application because 'System.Reflection.NullabilityInfoContext.IsSupported' is set to false. Set the MSBuild Property 'NullabilityInfoContextSupport' to true in order to enable it. - Thread is running or terminated; it cannot restart. diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs index 5c56ef2519759..fd279a70fbbb6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/NullabilityInfoContext.cs @@ -22,9 +22,6 @@ sealed class NullabilityInfoContext private readonly Dictionary _publicOnlyModules = new(); private readonly Dictionary _context = new(); - internal static bool IsSupported { get; } = - AppContext.TryGetSwitch("System.Reflection.NullabilityInfoContext.IsSupported", out bool isSupported) ? isSupported : true; - [Flags] private enum NotAnnotatedStatus { @@ -75,9 +72,6 @@ public NullabilityInfo Create(ParameterInfo parameterInfo) #else NetstandardHelpers.ThrowIfNull(parameterInfo, nameof(parameterInfo)); #endif - - EnsureIsSupported(); - IList attributes = parameterInfo.GetCustomAttributesData(); NullableAttributeStateParser parser = parameterInfo.Member is MethodBase method && IsPrivateOrInternalMethodAndAnnotationDisabled(method) ? NullableAttributeStateParser.Unknown @@ -204,9 +198,6 @@ public NullabilityInfo Create(PropertyInfo propertyInfo) #else NetstandardHelpers.ThrowIfNull(propertyInfo, nameof(propertyInfo)); #endif - - EnsureIsSupported(); - MethodInfo? getter = propertyInfo.GetGetMethod(true); MethodInfo? setter = propertyInfo.GetSetMethod(true); bool annotationsDisabled = (getter == null || IsPrivateOrInternalMethodAndAnnotationDisabled(getter)) @@ -263,9 +254,6 @@ public NullabilityInfo Create(EventInfo eventInfo) #else NetstandardHelpers.ThrowIfNull(eventInfo, nameof(eventInfo)); #endif - - EnsureIsSupported(); - return GetNullabilityInfo(eventInfo, eventInfo.EventHandlerType!, CreateParser(eventInfo.GetCustomAttributesData())); } @@ -284,9 +272,6 @@ public NullabilityInfo Create(FieldInfo fieldInfo) #else NetstandardHelpers.ThrowIfNull(fieldInfo, nameof(fieldInfo)); #endif - - EnsureIsSupported(); - IList attributes = fieldInfo.GetCustomAttributesData(); NullableAttributeStateParser parser = IsPrivateOrInternalFieldAndAnnotationDisabled(fieldInfo) ? NullableAttributeStateParser.Unknown : CreateParser(attributes); NullabilityInfo nullability = GetNullabilityInfo(fieldInfo, fieldInfo.FieldType, parser); @@ -294,14 +279,6 @@ public NullabilityInfo Create(FieldInfo fieldInfo) return nullability; } - private static void EnsureIsSupported() - { - if (!IsSupported) - { - throw new InvalidOperationException(SR.NullabilityInfoContext_NotSupported); - } - } - private bool IsPrivateOrInternalFieldAndAnnotationDisabled(FieldInfo fieldInfo) { if ((fieldInfo.IsPrivate || fieldInfo.IsFamilyAndAssembly || fieldInfo.IsAssembly) && diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/NullabilityInfoContextTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/NullabilityInfoContextTests.cs index 77c6984d68e62..343e951d176c2 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/NullabilityInfoContextTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Reflection/NullabilityInfoContextTests.cs @@ -284,43 +284,6 @@ public void GenericDictionaryPropertyTest(string propertyName, NullabilityState Assert.Null(nullability.ElementType); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void VerifyIsSupportedThrows() - { - RemoteInvokeOptions options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions.Add("System.Reflection.NullabilityInfoContext.IsSupported", "false"); - - using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(() => - { - FieldInfo field = testType.GetField("FieldNullable", flags); - Assert.Throws(() => nullabilityContext.Create(field)); - - EventInfo @event = testType.GetEvent("EventNullable"); - Assert.Throws(() => nullabilityContext.Create(@event)); - - PropertyInfo property = testType.GetProperty("PropertyNullable", flags); - Assert.Throws(() => nullabilityContext.Create(property)); - - MethodInfo method = testType.GetMethod("MethodNullNonNullNonNon", flags); - Assert.Throws(() => nullabilityContext.Create(method.ReturnParameter)); - }, options); - } - - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void VerifyIsSupportedWorks() - { - RemoteInvokeOptions options = new RemoteInvokeOptions(); - options.RuntimeConfigurationOptions.Add("System.Reflection.NullabilityInfoContext.IsSupported", "true"); - - using RemoteInvokeHandle remoteHandle = RemoteExecutor.Invoke(() => - { - FieldInfo field = testType.GetField("FieldNullable", flags); - NullabilityInfo nullability = nullabilityContext.Create(field); - Assert.Equal(NullabilityState.Nullable, nullability.ReadState); - Assert.Equal(NullabilityState.Nullable, nullability.WriteState); - }, options); - } - public static IEnumerable GenericPropertyReferenceTypeTestData() { yield return new object[] { "PropertyNullable", NullabilityState.Nullable, NullabilityState.Nullable, typeof(TypeWithNotNullContext) }; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportFalse.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportFalse.cs deleted file mode 100644 index 60ed73d8da278..0000000000000 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportFalse.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System; -using System.Reflection; - -/// -/// Ensures setting NullabilityInfoContextSupport = false causes NullabilityInfoContext.Create to throw -/// -class Program -{ - static int Main(string[] args) - { - MethodInfo testMethod = typeof(TestType).GetMethod("TestMethod")!; - NullabilityInfoContext nullabilityContext = new NullabilityInfoContext(); - try - { - nullabilityContext.Create(testMethod.ReturnParameter); - return -1; - } - catch (InvalidOperationException) - { - return 100; - } - } -} - -class TestType -{ - public static string? TestMethod() - { - return null; - } -} diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportTrue.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportTrue.cs deleted file mode 100644 index ecd32e4e37a19..0000000000000 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/NullabilityInfoContextSupportTrue.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable enable - -using System.Reflection; - -/// -/// Ensures setting NullabilityInfoContextSupport = true works in a trimmed app -/// -class Program -{ - static int Main(string[] args) - { - MethodInfo testMethod = typeof(TestType).GetMethod("TestMethod")!; - NullabilityInfoContext nullabilityContext = new NullabilityInfoContext(); - NullabilityInfo nullability = nullabilityContext.Create(testMethod.ReturnParameter); - - if (nullability.ReadState != NullabilityState.Nullable) - { - return -1; - } - - return 100; - } -} - -class TestType -{ - public static string? TestMethod() - { - return null; - } -} diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/System.Runtime.TrimmingTests.proj b/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/System.Runtime.TrimmingTests.proj index 6cfdea28f5e0b..737dfc7148712 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/System.Runtime.TrimmingTests.proj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/TrimmingTests/System.Runtime.TrimmingTests.proj @@ -56,12 +56,6 @@ true - - NullabilityInfoContextSupport - - - NullabilityInfoContextSupport - diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 4ff132432c214..b8791ea8fb171 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -749,9 +749,6 @@ The constructor parameter '{0}' on type '{1}' doesn't allow null values. Consider updating its nullability annotation. - - NullabilityInfoContext is not supported in the current application because 'System.Reflection.NullabilityInfoContext.IsSupported' is set to false. Set the MSBuild Property 'NullabilityInfoContextSupport' to true in order to enable it. - JSON schema generation is not supported for contracts using ReferenceHandler.Preserve. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs index 05ae6cec8721a..3cc57e8ad2827 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs @@ -13,9 +13,6 @@ namespace System.Text.Json.Serialization.Metadata { public partial class DefaultJsonTypeInfoResolver { - private static readonly bool s_isNullabilityInfoContextSupported = - AppContext.TryGetSwitch("System.Reflection.NullabilityInfoContext.IsSupported", out bool isSupported) ? isSupported : true; - internal static MemberAccessor MemberAccessor { [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] @@ -74,10 +71,7 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte if (typeInfo is { Kind: JsonTypeInfoKind.Object, IsNullable: false }) { - // If the System.Reflection.NullabilityInfoContext.IsSupported feature switch has been disabled, - // we want to avoid resolving nullability information for properties and parameters unless the - // user has explicitly opted into nullability enforcement in which case an exception will be surfaced. - NullabilityInfoContext? nullabilityCtx = s_isNullabilityInfoContextSupported || options.RespectNullableAnnotations ? new() : null; + NullabilityInfoContext nullabilityCtx = new(); if (converter.ConstructorIsParameterized) { @@ -99,7 +93,7 @@ private static JsonTypeInfo CreateTypeInfoCore(Type type, JsonConverter converte [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoContext? nullabilityCtx) + private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx) { Debug.Assert(!typeInfo.IsReadOnly); Debug.Assert(typeInfo.Kind is JsonTypeInfoKind.Object); @@ -146,7 +140,7 @@ private static void PopulateProperties(JsonTypeInfo typeInfo, NullabilityInfoCon private static void AddMembersDeclaredBySuperType( JsonTypeInfo typeInfo, Type currentType, - NullabilityInfoContext? nullabilityCtx, + NullabilityInfoContext nullabilityCtx, bool constructorHasSetsRequiredMembersAttribute, ref JsonTypeInfo.PropertyHierarchyResolutionState state) { @@ -207,7 +201,7 @@ private static void AddMember( JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, - NullabilityInfoContext? nullabilityCtx, + NullabilityInfoContext nullabilityCtx, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute, ref JsonTypeInfo.PropertyHierarchyResolutionState state) @@ -229,7 +223,7 @@ private static void AddMember( JsonTypeInfo typeInfo, Type typeToConvert, MemberInfo memberInfo, - NullabilityInfoContext? nullabilityCtx, + NullabilityInfoContext nullabilityCtx, JsonSerializerOptions options, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute) @@ -289,7 +283,7 @@ private static bool PropertyIsOverriddenAndIgnored(PropertyInfo propertyInfo, Di [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext? nullabilityCtx) + private static void PopulateParameterInfoValues(JsonTypeInfo typeInfo, NullabilityInfoContext nullabilityCtx) { Debug.Assert(typeInfo.Converter.ConstructorInfo != null); ParameterInfo[] parameters = typeInfo.Converter.ConstructorInfo.GetParameters(); @@ -330,7 +324,7 @@ private static void PopulatePropertyInfo( MemberInfo memberInfo, JsonConverter? customConverter, JsonIgnoreCondition? ignoreCondition, - NullabilityInfoContext? nullabilityCtx, + NullabilityInfoContext nullabilityCtx, bool shouldCheckForRequiredKeyword, bool hasJsonIncludeAttribute) { @@ -473,9 +467,9 @@ internal static void DeterminePropertyAccessors(JsonPropertyInfo jsonPrope [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, NullabilityInfoContext? nullabilityCtx) + private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, MemberInfo memberInfo, NullabilityInfoContext nullabilityCtx) { - if (!propertyInfo.PropertyTypeCanBeNull || nullabilityCtx is null) + if (!propertyInfo.PropertyTypeCanBeNull) { return; } @@ -497,17 +491,12 @@ private static void DeterminePropertyNullability(JsonPropertyInfo propertyInfo, [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContext? nullabilityCtx) + private static NullabilityState DetermineParameterNullability(ParameterInfo parameterInfo, NullabilityInfoContext nullabilityCtx) { if (!parameterInfo.ParameterType.IsNullableType()) { return NullabilityState.NotNull; } - - if (nullabilityCtx is null) - { - return NullabilityState.Unknown; - } #if NET8_0 // Workaround for https://github.com/dotnet/runtime/issues/92487 // The fix has been incorporated into .NET 9 (and the polyfilled implementations in netfx). 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 f0b9b5ad0ca63..b6f91aa6e6e20 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 @@ -962,32 +962,6 @@ public static void Options_RespectNullableAnnotationsDefault_FeatureSwitch(bool? }, arg, options).Dispose(); } - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_NullabilityInfoFeatureSwitchDisabled_ReportsPropertiesAsNullable() - { - var options = new RemoteInvokeOptions() - { - RuntimeConfigurationOptions = - { - ["System.Reflection.NullabilityInfoContext.IsSupported"] = false - } - }; - - RemoteExecutor.Invoke(static () => - { - var value = new NullableAnnotationsTests.NotNullablePropertyClass(); - string expectedJson = """{"Property":null}"""; - - Assert.Null(value.Property); - string json = JsonSerializer.Serialize(value); - Assert.Equal(expectedJson, json); - value = JsonSerializer.Deserialize(json); - Assert.Null(value.Property); - - }, options).Dispose(); - } - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] [InlineData(null)] @@ -1028,34 +1002,6 @@ public static void Options_RespectRequiredConstructorParameters_FeatureSwitch(bo }, arg, options).Dispose(); } - [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public static void Options_NullabilityInfoFeatureSwitchDisabled_RespectNullabilityAnnotationsEnabled_ThrowsInvalidOperationException() - { - var options = new RemoteInvokeOptions() - { - RuntimeConfigurationOptions = - { - ["System.Reflection.NullabilityInfoContext.IsSupported"] = false - } - }; - - RemoteExecutor.Invoke(static () => - { - var jsonOptions = new JsonSerializerOptions { RespectNullableAnnotations = true }; - var value = new NullableAnnotationsTests.NotNullablePropertyClass(); - string expectedJson = """{"Property":null}"""; - InvalidOperationException ex; - - ex = Assert.Throws(() => JsonSerializer.Serialize(value, jsonOptions)); - Assert.Contains("System.Reflection.NullabilityInfoContext.IsSupported", ex.Message); - - ex = Assert.Throws(() => JsonSerializer.Deserialize(expectedJson, jsonOptions)); - Assert.Contains("System.Reflection.NullabilityInfoContext.IsSupported", ex.Message); - - }, options).Dispose(); - } - private static void GenericObjectOrJsonElementConverterTestHelper(string converterName, object objectValue, string stringValue) { var options = new JsonSerializerOptions();