diff --git a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs index e15e8993889e43..a89745d8e1301d 100644 --- a/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs +++ b/src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs @@ -91,15 +91,19 @@ private static bool HasCustomAttributeWithName(this ICustomAttributeProvider mem /// /// Polyfill for BindingFlags.DoNotWrapExceptions /// - public static object? InvokeNoWrapExceptions(this MethodInfo methodInfo, object? obj, object?[] parameters) + public static object? CreateInstanceNoWrapExceptions( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] this Type type, + Type[] parameterTypes, + object?[] parameters) { + ConstructorInfo ctorInfo = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, parameterTypes, null)!; #if NETCOREAPP - return methodInfo.Invoke(obj, BindingFlags.DoNotWrapExceptions, null, parameters, null); + return ctorInfo.Invoke(BindingFlags.DoNotWrapExceptions, null, parameters, null); #else object? result = null; try { - result = methodInfo.Invoke(obj, parameters); + result = ctorInfo.Invoke(parameters); } catch (TargetInvocationException ex) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index 652637873a2d57..bfdb8479a4e3f8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -69,6 +69,22 @@ internal virtual void ReadElementAndSetProperty( throw new InvalidOperationException(); } + [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] + internal virtual JsonTypeInfo CreateReflectionJsonTypeInfo(JsonSerializerOptions options) + { + Debug.Fail("Should not be reachable."); + + throw new InvalidOperationException(); + } + + internal virtual JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions options) + { + Debug.Fail("Should not be reachable."); + + throw new InvalidOperationException(); + } + internal abstract JsonParameterInfo CreateJsonParameterInfo(); internal abstract JsonConverter CreateCastingConverter(); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 522f134a952969..0a9f00a67db7dc 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Converters; using System.Text.Json.Serialization.Metadata; @@ -67,6 +68,18 @@ public override bool CanConvert(Type typeToConvert) internal override ConverterStrategy ConverterStrategy => ConverterStrategy.Value; + [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] + internal sealed override JsonTypeInfo CreateReflectionJsonTypeInfo(JsonSerializerOptions options) + { + return new ReflectionJsonTypeInfo(this, options); + } + + internal sealed override JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions options) + { + return new CustomJsonTypeInfo(this, options); + } + internal sealed override JsonParameterInfo CreateJsonParameterInfo() { return new JsonParameterInfo(); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/CustomJsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/CustomJsonTypeInfoOfT.cs index a997ee01d021bc..984125dba3443e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/CustomJsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/CustomJsonTypeInfoOfT.cs @@ -1,11 +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.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Text.Json.Serialization.Converters; namespace System.Text.Json.Serialization.Metadata { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.cs index 4389bd9e115297..c90a5450a0458e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Text.Json.Reflection; using System.Threading; @@ -85,20 +85,26 @@ public virtual JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] private static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) { - s_createReflectionJsonTypeInfoMethodInfo ??= typeof(DefaultJsonTypeInfoResolver).GetMethod(nameof(CreateReflectionJsonTypeInfo), BindingFlags.NonPublic | BindingFlags.Static)!; - return (JsonTypeInfo)s_createReflectionJsonTypeInfoMethodInfo.MakeGenericMethod(type) - .InvokeNoWrapExceptions(null, new object[] { options })!; - } + JsonTypeInfo jsonTypeInfo; + JsonConverter converter = GetConverterForType(type, options); - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)] - private static JsonTypeInfo CreateReflectionJsonTypeInfo(JsonSerializerOptions options) - { - JsonConverter converter = GetConverterForType(typeof(T), options); - return new ReflectionJsonTypeInfo(converter, options); - } + if (converter.TypeToConvert == type) + { + // For performance, avoid doing a reflection-based instantiation + // if the converter type matches that of the declared type. + jsonTypeInfo = converter.CreateReflectionJsonTypeInfo(options); + } + else + { + Type jsonTypeInfoType = typeof(ReflectionJsonTypeInfo<>).MakeGenericType(type); + jsonTypeInfo = (JsonTypeInfo)jsonTypeInfoType.CreateInstanceNoWrapExceptions( + parameterTypes: new Type[] { typeof(JsonConverter), typeof(JsonSerializerOptions) }, + parameters: new object[] { converter, options })!; + } - private static MethodInfo? s_createReflectionJsonTypeInfoMethodInfo; + Debug.Assert(jsonTypeInfo.Type == type); + return jsonTypeInfo; + } /// /// Gets a list of user-defined callbacks that can be used to modify the initial contract. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs index af6b4cf10104d5..58fe2bb1fecb0b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.Cache.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Json.Reflection; @@ -56,7 +55,7 @@ public abstract partial class JsonTypeInfo [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] internal JsonPropertyInfo CreatePropertyUsingReflection(Type propertyType) { - JsonPropertyInfo? jsonPropertyInfo; + JsonPropertyInfo jsonPropertyInfo; if (Options.TryGetTypeInfoCached(propertyType, out JsonTypeInfo? jsonTypeInfo)) { @@ -69,9 +68,10 @@ internal JsonPropertyInfo CreatePropertyUsingReflection(Type propertyType) { // Metadata for `propertyType` has not been registered yet. // Use reflection to instantiate the correct JsonPropertyInfo - s_createJsonPropertyInfo ??= typeof(JsonTypeInfo).GetMethod(nameof(CreateJsonPropertyInfo), BindingFlags.NonPublic | BindingFlags.Static)!; - jsonPropertyInfo = (JsonPropertyInfo)s_createJsonPropertyInfo.MakeGenericMethod(propertyType) - .InvokeNoWrapExceptions(null, new object[] { this, Options })!; + Type propertyInfoType = typeof(JsonPropertyInfo<>).MakeGenericType(propertyType); + jsonPropertyInfo = (JsonPropertyInfo)propertyInfoType.CreateInstanceNoWrapExceptions( + parameterTypes: new Type[] { typeof(Type), typeof(JsonTypeInfo), typeof(JsonSerializerOptions) }, + parameters: new object[] { Type, this, Options })!; } Debug.Assert(jsonPropertyInfo.PropertyType == propertyType); @@ -83,11 +83,6 @@ internal JsonPropertyInfo CreatePropertyUsingReflection(Type propertyType) /// private protected abstract JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, JsonSerializerOptions options); - private static JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, JsonSerializerOptions options) - => new JsonPropertyInfo(declaringTypeInfo.Type, declaringTypeInfo, options); - - private static MethodInfo? s_createJsonPropertyInfo; - // AggressiveInlining used although a large method it is only called from one location and is on a hot path. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal JsonPropertyInfo GetProperty( diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 407cddc0f4144f..ef345aa231d6bd 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Text.Json.Reflection; @@ -634,8 +633,6 @@ public static JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions option return new CustomJsonTypeInfo(converter, options); } - private static MethodInfo? s_createJsonTypeInfo; - /// /// Creates a blank instance. /// @@ -673,9 +670,25 @@ public static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions o ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null); } - s_createJsonTypeInfo ??= typeof(JsonTypeInfo).GetMethod(nameof(CreateJsonTypeInfo), new Type[] { typeof(JsonSerializerOptions) })!; - return (JsonTypeInfo)s_createJsonTypeInfo.MakeGenericMethod(type) - .InvokeNoWrapExceptions(null, new object[] { options })!; + JsonTypeInfo jsonTypeInfo; + JsonConverter converter = DefaultJsonTypeInfoResolver.GetConverterForType(type, options, resolveJsonConverterAttribute: false); + + if (converter.TypeToConvert == type) + { + // For performance, avoid doing a reflection-based instantiation + // if the converter type matches that of the declared type. + jsonTypeInfo = converter.CreateCustomJsonTypeInfo(options); + } + else + { + Type jsonTypeInfoType = typeof(CustomJsonTypeInfo<>).MakeGenericType(type); + jsonTypeInfo = (JsonTypeInfo)jsonTypeInfoType.CreateInstanceNoWrapExceptions( + parameterTypes: new Type[] { typeof(JsonConverter), typeof(JsonSerializerOptions) }, + parameters: new object[] { converter, options })!; + } + + Debug.Assert(jsonTypeInfo.Type == type); + return jsonTypeInfo; } /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs index 1e0aac32bb7e23..72fb5a48ac0ab3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionJsonTypeInfoOfT.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Runtime.CompilerServices; using System.Text.Json.Reflection; namespace System.Text.Json.Serialization.Metadata