Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement dynamic member access for collection converters correctly #55846

Merged
merged 1 commit into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
<Compile Include="System\Text\Json\Serialization\Attributes\JsonNumberHandlingAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonPropertyNameAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Attributes\JsonPropertyOrderAttribute.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ImmutableDictionaryOfTKeyTValueConverterWithReflection.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ImmutableEnumerableOfTConverterWithReflection.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\StackOrQueueConverterWithReflection.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\JsonMetadataServicesConverter.cs" />
<Compile Include="System\Text\Json\Serialization\IgnoreReferenceResolver.cs" />
<Compile Include="System\Text\Json\Serialization\IJsonOnDeserialized.cs" />
Expand Down Expand Up @@ -121,7 +124,7 @@
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IEnumerableConverterFactoryHelpers.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IEnumerableDefaultConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IEnumerableOfTConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IEnumerableWithAddMethodConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\StackOrQueueConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IListConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\IListOfTConverter.cs" />
<Compile Include="System\Text\Json\Serialization\Converters\Collection\ImmutableDictionaryOfTKeyTValueConverter.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
Expand All @@ -91,7 +91,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer
// Immutable non-dictionaries from System.Collections.Immutable, e.g. ImmutableStack<T>
else if (typeToConvert.IsImmutableEnumerableType())
{
converterType = typeof(ImmutableEnumerableOfTConverter<,>);
converterType = typeof(ImmutableEnumerableOfTConverterWithReflection<,>);
elementType = typeToConvert.GetGenericArguments()[0];
}
// IList<>
Expand Down Expand Up @@ -163,7 +163,7 @@ public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializer
}
else if (typeToConvert.IsNonGenericStackOrQueue())
{
converterType = typeof(IEnumerableWithAddMethodConverter<>);
converterType = typeof(StackOrQueueConverterWithReflection<>);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,36 @@

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<TCollection, TKey, TValue>
internal class ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
: DictionaryDefaultConverter<TCollection, TKey, TValue>
where TCollection : IReadOnlyDictionary<TKey, TValue>
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<TKey, TValue>)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<TKey, TValue>();
}

protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
{
Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>? creator =
(Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>?)state.Current.JsonTypeInfo.CreateObjectWithArgs;
Debug.Assert(creator != null);
state.Current.ReturnValue = creator((Dictionary<TKey, TValue>)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<KeyValuePair<TKey, TValue>> enumerator;
if (state.Current.CollectionEnumerator == null)
Expand Down Expand Up @@ -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<TCollection, TKey, TValue>();
}
}
}
Original file line number Diff line number Diff line change
@@ -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<TCollection, TKey, TValue>
: ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>
where TCollection : IReadOnlyDictionary<TKey, TValue>
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<TCollection, TKey, TValue>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TCollection, TElement>
internal class ImmutableEnumerableOfTConverter<TCollection, TElement>
: IEnumerableDefaultConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
[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<TElement>)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<TElement>();
}

protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
protected sealed override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;

Expand All @@ -43,7 +32,7 @@ protected override void ConvertCollection(ref ReadStack state, JsonSerializerOpt
state.Current.ReturnValue = creator((List<TElement>)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<TElement> enumerator;
if (state.Current.CollectionEnumerator == null)
Expand Down Expand Up @@ -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<TCollection, TElement>();
}
}
}
Original file line number Diff line number Diff line change
@@ -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<TCollection, TElement>
: ImmutableEnumerableOfTConverter<TCollection, TElement>
where TCollection : IEnumerable<TElement>
{
[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<TCollection, TElement>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TCollection>
internal class StackOrQueueConverter<TCollection>
: IEnumerableDefaultConverter<TCollection, object?>
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<TCollection, object?>?)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;
Expand All @@ -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)
Expand Down Expand Up @@ -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<TCollection>();
}
}
}
Original file line number Diff line number Diff line change
@@ -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<TCollection>
: StackOrQueueConverter<TCollection>
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<TCollection>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public static JsonTypeInfo<TCollection> CreateImmutableDictionaryInfo<TCollectio
=> new JsonTypeInfoInternal<TCollection>(
options,
createObjectFunc,
() => new ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>(dummy: false),
() => new ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey, TValue>(),
keyInfo,
valueInfo,
numberHandling,
Expand Down Expand Up @@ -224,7 +224,7 @@ public static JsonTypeInfo<TCollection> CreateImmutableEnumerableInfo<TCollectio
=> new JsonTypeInfoInternal<TCollection>(
options,
createObjectFunc,
() => new ImmutableEnumerableOfTConverter<TCollection, TElement>(dummy: false),
() => new ImmutableEnumerableOfTConverter<TCollection, TElement>(),
elementInfo,
numberHandling,
serializeFunc,
Expand Down Expand Up @@ -525,7 +525,7 @@ public static JsonTypeInfo<TCollection> CreateStackOrQueueInfo<TCollection>(
=> new JsonTypeInfoInternal<TCollection>(
options,
createObjectFunc,
() => new IEnumerableWithAddMethodConverter<TCollection>(dummy: false),
() => new StackOrQueueConverter<TCollection>(),
elementInfo,
numberHandling,
serializeFunc,
Expand Down