diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs index 1c18b1ca722b4..b975961d21c57 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ICollectionOfTConverter.cs @@ -38,10 +38,10 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as List for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new List(); + jsonTypeInfo.CreateObject = () => new List(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs index b2b57f9e72255..3d86186659976 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryConverter.cs @@ -98,10 +98,10 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TDictionar internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as Dictionary for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new Dictionary(); + jsonTypeInfo.CreateObject = () => new Dictionary(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs index ca68c752b1bc7..4102404974c1c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IDictionaryOfTKeyTValueConverter.cs @@ -40,10 +40,10 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as Dictionary for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(Dictionary))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new Dictionary(); + jsonTypeInfo.CreateObject = () => new Dictionary(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs index 119732900cb39..7c54b27ad93b5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListConverter.cs @@ -77,10 +77,10 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as List for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new List(); + jsonTypeInfo.CreateObject = () => new List(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs index df8aed6fac884..4de171768d793 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IListOfTConverter.cs @@ -38,10 +38,10 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as List for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(List))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new List(); + jsonTypeInfo.CreateObject = () => new List(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs index dd81bf84b1f8f..2c1fd6b756f3e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ISetOfTConverter.cs @@ -35,10 +35,10 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) { // Deserialize as HashSet for interface types that support it. - if (jsonTypeInfo.UntypedCreateObject is null && TypeToConvert.IsAssignableFrom(typeof(HashSet))) + if (jsonTypeInfo.CreateObject is null && TypeToConvert.IsAssignableFrom(typeof(HashSet))) { Debug.Assert(TypeToConvert.IsInterface); - jsonTypeInfo.UntypedCreateObject = () => new HashSet(); + jsonTypeInfo.CreateObject = () => new HashSet(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs index dc8b453ef9e70..2ee506e720496 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonCollectionConverter.cs @@ -26,7 +26,7 @@ protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - if (typeInfo.UntypedCreateObject is null) + if (typeInfo.CreateObject is null) { // The contract model was not able to produce a default constructor for two possible reasons: // 1. Either the declared collection type is abstract and cannot be instantiated. @@ -41,7 +41,7 @@ protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack } } - state.Current.ReturnValue = typeInfo.UntypedCreateObject(); + state.Current.ReturnValue = typeInfo.CreateObject(); Debug.Assert(state.Current.ReturnValue is TCollection); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs index 17245f246e90b..78660b8f6aa8c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/JsonDictionaryConverter.cs @@ -41,7 +41,7 @@ protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - if (typeInfo.UntypedCreateObject is null) + if (typeInfo.CreateObject is null) { // The contract model was not able to produce a default constructor for two possible reasons: // 1. Either the declared collection type is abstract and cannot be instantiated. @@ -56,7 +56,7 @@ protected virtual void CreateCollection(ref Utf8JsonReader reader, ref ReadStack } } - state.Current.ReturnValue = typeInfo.UntypedCreateObject()!; + state.Current.ReturnValue = typeInfo.CreateObject()!; Debug.Assert(state.Current.ReturnValue is TDictionary); } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs index 192cc5a42550c..35e398fe5869f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ListOfTConverter.cs @@ -17,12 +17,12 @@ protected override void Add(in TElement value, ref ReadStack state) protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { - if (state.Current.JsonTypeInfo.UntypedCreateObject == null) + if (state.Current.JsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type); } - state.Current.ReturnValue = state.Current.JsonTypeInfo.UntypedCreateObject(); + state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject(); } protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs index 37c914dbe0292..6e28f4ef2458b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/QueueOfTConverter.cs @@ -16,12 +16,12 @@ protected override void Add(in TElement value, ref ReadStack state) protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { - if (state.Current.JsonTypeInfo.UntypedCreateObject == null) + if (state.Current.JsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type); } - state.Current.ReturnValue = state.Current.JsonTypeInfo.UntypedCreateObject(); + state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs index ed6b4d17d573f..565754d6a24b8 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOfTConverter.cs @@ -17,12 +17,12 @@ protected override void Add(in TElement value, ref ReadStack state) protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { - if (state.Current.JsonTypeInfo.UntypedCreateObject == null) + if (state.Current.JsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_SerializationNotSupported(state.Current.JsonTypeInfo.Type); } - state.Current.ReturnValue = state.Current.JsonTypeInfo.UntypedCreateObject(); + state.Current.ReturnValue = state.Current.JsonTypeInfo.CreateObject(); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 2719129f84600..59f3f3c336ec9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -21,7 +21,7 @@ protected sealed override void Add(in object? value, ref ReadStack state) protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; - Func? constructorDelegate = typeInfo.UntypedCreateObject; + Func? constructorDelegate = typeInfo.CreateObject; if (constructorDelegate == null) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs index 0b2bcd76bc680..19a14dac9980f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectDefaultConverter.cs @@ -31,12 +31,12 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(TypeToConvert); } - if (jsonTypeInfo.UntypedCreateObject == null) + if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } - obj = jsonTypeInfo.UntypedCreateObject()!; + obj = jsonTypeInfo.CreateObject()!; if (obj is IJsonOnDeserializing onDeserializing) { @@ -127,12 +127,12 @@ internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, return true; } - if (jsonTypeInfo.UntypedCreateObject == null) + if (jsonTypeInfo.CreateObject == null) { ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(jsonTypeInfo.Type, ref reader, ref state); } - obj = jsonTypeInfo.UntypedCreateObject()!; + obj = jsonTypeInfo.CreateObject()!; if (state.Current.MetadataPropertyNames.HasFlag(MetadataPropertyName.Id)) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs index 414ea46bc5486..43729ade8971b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Read.HandlePropertyName.cs @@ -121,7 +121,7 @@ internal static void CreateDataExtensionProperty( genericArgs[1].UnderlyingSystemType == typeof(JsonElement) || genericArgs[1].UnderlyingSystemType == typeof(Nodes.JsonNode)); #endif - if (jsonPropertyInfo.JsonTypeInfo.UntypedCreateObject == null) + if (jsonPropertyInfo.JsonTypeInfo.CreateObject == null) { // Avoid a reference to the JsonNode type for trimming if (jsonPropertyInfo.PropertyType.FullName == JsonTypeInfo.JsonObjectTypeName) @@ -135,7 +135,7 @@ internal static void CreateDataExtensionProperty( } else { - extensionData = jsonPropertyInfo.JsonTypeInfo.UntypedCreateObject(); + extensionData = jsonPropertyInfo.JsonTypeInfo.CreateObject(); } jsonPropertyInfo.SetExtensionDictionaryAsObject(obj, extensionData); 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 fcb715f91c31e..ad54a32ff2db6 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 @@ -28,10 +28,16 @@ public abstract partial class JsonTypeInfo /// public Func? CreateObject { - get => UntypedCreateObjectAbstract; - set => UntypedCreateObjectAbstract = value; + get => _createObject; + set + { + SetCreateObject(value); + } } + private protected abstract void SetCreateObject(Delegate? createObject); + private protected Func? _createObject; + /// /// Gets JsonPropertyInfo list. Only applicable when Kind is Object. /// @@ -69,13 +75,6 @@ public IList Properties } } - // Untyped CreateObject is non-virtual public API so we pretend it's virtual by using this indirection - // We need it to be abstract so that we can keep typed value in sync - internal abstract Func? UntypedCreateObjectAbstract { get; set; } - - // Actual value of UntypedCreateObject, for perf it's non-virtual - internal Func? UntypedCreateObject { get; set; } - internal object? CreateObjectWithArgs { get; set; } // Add method delegate for non-generic Stack and Queue; and types that derive from them. diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs index c2c5003ea1ca6..2c70e9314122c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoOfT.cs @@ -21,23 +21,36 @@ public abstract class JsonTypeInfo : JsonTypeInfo /// public new Func? CreateObject { - get => _typedCreateObject ??= UntypedCreateObject == null ? null : () => (T)UntypedCreateObject(); + get => _typedCreateObject; set { - _typedCreateObject = value; - UntypedCreateObject = value == null ? null : () => value()!; + SetCreateObject(value); } } - internal override Func? UntypedCreateObjectAbstract + private protected override void SetCreateObject(Delegate? createObject) { - get => UntypedCreateObject; - set + Debug.Assert(createObject is null or Func or Func); + + if (createObject is null) { - UntypedCreateObject = value; - // We invalidate the cached typed value + _createObject = null; _typedCreateObject = null; + return; } + + if (createObject is Func untypedDelegate) + { + _createObject = untypedDelegate; + _typedCreateObject = () => (T)untypedDelegate(); + return; + } + + Debug.Assert(createObject is Func); + + Func typedDelegate = (Func)createObject; + _createObject = () => typedDelegate()!; + _typedCreateObject = typedDelegate; } internal JsonTypeInfo(JsonConverter converter, JsonSerializerOptions options) 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 891b092e2a941..98aad3fc70915 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 @@ -40,7 +40,7 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o AddPropertiesAndParametersUsingReflection(); } - UntypedCreateObject = Options.MemberAccessorStrategy.CreateConstructor(typeof(T)); + ((JsonTypeInfo)this).CreateObject = Options.MemberAccessorStrategy.CreateConstructor(typeof(T)); } [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",