From a1578decd594680fc01f0adfef9eabb31c90cb7d Mon Sep 17 00:00:00 2001 From: Layomi Akinrinade Date: Fri, 16 Jul 2021 10:36:42 -0700 Subject: [PATCH] Reimplement dynamic member access for collection converters correctly --- .../src/System.Text.Json.csproj | 5 ++- .../Collection/IEnumerableConverterFactory.cs | 6 ++-- ...mmutableDictionaryOfTKeyTValueConverter.cs | 31 ++++--------------- ...naryOfTKeyTValueConverterWithReflection.cs | 31 +++++++++++++++++++ .../ImmutableEnumerableOfTConverter.cs | 31 ++++--------------- ...bleEnumerableOfTConverterWithReflection.cs | 30 ++++++++++++++++++ ...dConverter.cs => StackOrQueueConverter.cs} | 25 +++------------ .../StackOrQueueConverterWithReflection.cs | 28 +++++++++++++++++ .../JsonMetadataServices.Collections.cs | 6 ++-- 9 files changed, 115 insertions(+), 78 deletions(-) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs rename src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/{IEnumerableWithAddMethodConverter.cs => StackOrQueueConverter.cs} (63%) create mode 100644 src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs diff --git a/src/libraries/System.Text.Json/src/System.Text.Json.csproj b/src/libraries/System.Text.Json/src/System.Text.Json.csproj index 7d428ffb19484..5a9ea1839ba69 100644 --- a/src/libraries/System.Text.Json/src/System.Text.Json.csproj +++ b/src/libraries/System.Text.Json/src/System.Text.Json.csproj @@ -90,6 +90,9 @@ + + + @@ -121,7 +124,7 @@ - + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs index ca1e09228b106..976d043d688c5 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactory.cs @@ -68,7 +68,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer else if (typeToConvert.IsImmutableDictionaryType()) { genericArgs = typeToConvert.GetGenericArguments(); - converterType = typeof(ImmutableDictionaryOfTKeyTValueConverter<,,>); + converterType = typeof(ImmutableDictionaryOfTKeyTValueConverterWithReflection<,,>); dictionaryKeyType = genericArgs[0]; elementType = genericArgs[1]; } @@ -91,7 +91,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer // Immutable non-dictionaries from System.Collections.Immutable, e.g. ImmutableStack else if (typeToConvert.IsImmutableEnumerableType()) { - converterType = typeof(ImmutableEnumerableOfTConverter<,>); + converterType = typeof(ImmutableEnumerableOfTConverterWithReflection<,>); elementType = typeToConvert.GetGenericArguments()[0]; } // IList<> @@ -163,7 +163,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer } else if (typeToConvert.IsNonGenericStackOrQueue()) { - converterType = typeof(IEnumerableWithAddMethodConverter<>); + converterType = typeof(StackOrQueueConverterWithReflection<>); } else { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs index 316a9a35d9b4a..be6d5d1c032e4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs @@ -3,39 +3,28 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class ImmutableDictionaryOfTKeyTValueConverter + internal class ImmutableDictionaryOfTKeyTValueConverter : DictionaryDefaultConverter where TCollection : IReadOnlyDictionary where TKey : notnull { - [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] - public ImmutableDictionaryOfTKeyTValueConverter() - { - } - - // Used by source-gen initialization for reflection-free serialization. - public ImmutableDictionaryOfTKeyTValueConverter(bool dummy) { } - - protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) + protected sealed override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state) { ((Dictionary)state.Current.ReturnValue!)[key] = value; } - internal override bool CanHaveIdMetadata => false; + internal sealed override bool CanHaveIdMetadata => false; - internal override bool RequiresDynamicMemberAccessors => true; - - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state) { state.Current.ReturnValue = new Dictionary(); } - protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { Func>, TCollection>? creator = (Func>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs; @@ -43,7 +32,7 @@ protected override void ConvertCollection(ref ReadStack state, JsonSerializerOpt state.Current.ReturnValue = creator((Dictionary)state.Current.ReturnValue!); } - protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected internal sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator> enumerator; if (state.Current.CollectionEnumerator == null) @@ -93,13 +82,5 @@ protected internal override bool OnWriteResume(Utf8JsonWriter writer, TCollectio enumerator.Dispose(); return true; } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs new file mode 100644 index 0000000000000..fba0358efbdd1 --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs @@ -0,0 +1,31 @@ +// 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.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class ImmutableDictionaryOfTKeyTValueConverterWithReflection + : ImmutableDictionaryOfTKeyTValueConverter + where TCollection : IReadOnlyDictionary + where TKey : notnull + { + [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] + public ImmutableDictionaryOfTKeyTValueConverterWithReflection() + { + } + + internal override bool RequiresDynamicMemberAccessors => true; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableDictionaryCreateRangeDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs index f2f2810e9baf1..f274a234dafed 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs @@ -3,38 +3,27 @@ using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class ImmutableEnumerableOfTConverter + internal class ImmutableEnumerableOfTConverter : IEnumerableDefaultConverter where TCollection : IEnumerable { - [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] - public ImmutableEnumerableOfTConverter() - { - } - - // Used by source-gen initialization for reflection-free serialization. - public ImmutableEnumerableOfTConverter(bool dummy) { } - - protected override void Add(in TElement value, ref ReadStack state) + protected sealed override void Add(in TElement value, ref ReadStack state) { ((List)state.Current.ReturnValue!).Add(value); } - internal override bool CanHaveIdMetadata => false; + internal sealed override bool CanHaveIdMetadata => false; - internal override bool RequiresDynamicMemberAccessors => true; - - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { state.Current.ReturnValue = new List(); } - protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) + protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; @@ -43,7 +32,7 @@ protected override void ConvertCollection(ref ReadStack state, JsonSerializerOpt state.Current.ReturnValue = creator((List)state.Current.ReturnValue!); } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator enumerator; if (state.Current.CollectionEnumerator == null) @@ -80,13 +69,5 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, enumerator.Dispose(); return true; } - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs new file mode 100644 index 0000000000000..dd6b4345df5ac --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverterWithReflection.cs @@ -0,0 +1,30 @@ +// 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.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class ImmutableEnumerableOfTConverterWithReflection + : ImmutableEnumerableOfTConverter + where TCollection : IEnumerable + { + [RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)] + public ImmutableEnumerableOfTConverterWithReflection() + { + } + + internal override bool RequiresDynamicMemberAccessors => true; + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.CreateObjectWithArgs = options.MemberAccessorStrategy.CreateImmutableEnumerableCreateRangeDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs similarity index 63% rename from src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs rename to src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs index 6363e8aa3c666..04cc6488a529b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableWithAddMethodConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverter.cs @@ -3,29 +3,22 @@ using System.Collections; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.Serialization.Converters { - internal sealed class IEnumerableWithAddMethodConverter + internal class StackOrQueueConverter : IEnumerableDefaultConverter where TCollection : IEnumerable { - [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] - public IEnumerableWithAddMethodConverter() { } - - // Used by source-gen initialization for reflection-free serialization. - public IEnumerableWithAddMethodConverter(bool dummy) { } - - protected override void Add(in object? value, ref ReadStack state) + protected sealed override void Add(in object? value, ref ReadStack state) { var addMethodDelegate = ((Action?)state.Current.JsonTypeInfo.AddMethodDelegate); Debug.Assert(addMethodDelegate != null); addMethodDelegate((TCollection)state.Current.ReturnValue!, value); } - protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) + protected sealed override void CreateCollection(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options) { JsonTypeInfo typeInfo = state.Current.JsonTypeInfo; JsonTypeInfo.ConstructorDelegate? constructorDelegate = typeInfo.CreateObject; @@ -40,7 +33,7 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac Debug.Assert(typeInfo.AddMethodDelegate != null); } - protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) + protected sealed override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, ref WriteStack state) { IEnumerator enumerator; if (state.Current.CollectionEnumerator == null) @@ -75,15 +68,5 @@ protected override bool OnWriteResume(Utf8JsonWriter writer, TCollection value, return true; } - - internal override bool RequiresDynamicMemberAccessors => true; - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", - Justification = "The ctor is marked RequiresUnreferencedCode.")] - internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) - { - Debug.Assert(jsonTypeInfo != null); - jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); - } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs new file mode 100644 index 0000000000000..a3a87f9a50f7d --- /dev/null +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/StackOrQueueConverterWithReflection.cs @@ -0,0 +1,28 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization.Metadata; + +namespace System.Text.Json.Serialization.Converters +{ + internal sealed class StackOrQueueConverterWithReflection + : StackOrQueueConverter + where TCollection : IEnumerable + { + internal override bool RequiresDynamicMemberAccessors => true; + + [RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)] + public StackOrQueueConverterWithReflection() { } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:UnrecognizedReflectionPattern", + Justification = "The ctor is marked RequiresUnreferencedCode.")] + internal override void Initialize(JsonSerializerOptions options, JsonTypeInfo? jsonTypeInfo = null) + { + Debug.Assert(jsonTypeInfo != null); + jsonTypeInfo.AddMethodDelegate = options.MemberAccessorStrategy.CreateAddMethodDelegate(); + } + } +} diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs index ef2b4d1a31972..ceb1fca9ad19f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Collections.cs @@ -124,7 +124,7 @@ public static JsonTypeInfo CreateImmutableDictionaryInfo new JsonTypeInfoInternal( options, createObjectFunc, - () => new ImmutableDictionaryOfTKeyTValueConverter(dummy: false), + () => new ImmutableDictionaryOfTKeyTValueConverter(), keyInfo, valueInfo, numberHandling, @@ -224,7 +224,7 @@ public static JsonTypeInfo CreateImmutableEnumerableInfo new JsonTypeInfoInternal( options, createObjectFunc, - () => new ImmutableEnumerableOfTConverter(dummy: false), + () => new ImmutableEnumerableOfTConverter(), elementInfo, numberHandling, serializeFunc, @@ -525,7 +525,7 @@ public static JsonTypeInfo CreateStackOrQueueInfo( => new JsonTypeInfoInternal( options, createObjectFunc, - () => new IEnumerableWithAddMethodConverter(dummy: false), + () => new StackOrQueueConverter(), elementInfo, numberHandling, serializeFunc,