Skip to content

Commit

Permalink
Split Reflection and SourceGen TypeInfos (#67526)
Browse files Browse the repository at this point in the history
* Split Reflection and SourceGen TypeInfos

* Apply PR feedback

* improve exception unwrapping
  • Loading branch information
krwq authored Apr 7, 2022
1 parent cca6d24 commit 98e0173
Show file tree
Hide file tree
Showing 34 changed files with 794 additions and 607 deletions.
3 changes: 2 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 @@ -122,6 +122,7 @@ System.Text.Json.Nodes.JsonValue</PackageDescription>
<Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Node.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializerContext.cs" />
<Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.Caching.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\ReflectionJsonTypeInfoOfT.cs" />
<Compile Include="System\Text\Json\Serialization\PolymorphicSerializationState.cs" />
<Compile Include="System\Text\Json\Serialization\ReferenceEqualsWrapper.cs" />
<Compile Include="System\Text\Json\Serialization\ConverterStrategy.cs" />
Expand Down Expand Up @@ -242,7 +243,7 @@ System.Text.Json.Nodes.JsonValue</PackageDescription>
<Compile Include="System\Text\Json\Serialization\Metadata\JsonPropertyInfo.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonPropertyInfoOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonPropertyInfoValuesOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfoInternalOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\SourceGenJsonTypeInfoOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfoOfT.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfo.Cache.cs" />
<Compile Include="System\Text\Json\Serialization\Metadata\JsonTypeInfo.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,7 @@ public JsonMetadataServicesConverter(Func<JsonConverter<T>> converterCreator!!,
}

internal override bool OnTryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, ref ReadStack state, out T? value)
{
JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;

if (_converterStrategy == ConverterStrategy.Object)
{
if (jsonTypeInfo.PropertyCache == null)
{
jsonTypeInfo.InitializePropCache();
}

if (jsonTypeInfo.ParameterCache == null && jsonTypeInfo.IsObjectWithParameterizedCtor)
{
jsonTypeInfo.InitializeParameterCache();
}
}

return Converter.OnTryRead(ref reader, typeToConvert, options, ref state, out value);
}
=> Converter.OnTryRead(ref reader, typeToConvert, options, ref state, out value);

internal override bool OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
{
Expand All @@ -84,11 +67,6 @@ jsonTypeInfo is JsonTypeInfo<T> info &&
return true;
}

if (_converterStrategy == ConverterStrategy.Object && jsonTypeInfo.PropertyCache == null)
{
jsonTypeInfo.InitializePropCache();
}

return Converter.OnTryWrite(writer, value, options, ref state);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ internal sealed override bool OnTryWrite(
{
// Remember the current property for JsonPath support if an exception is thrown.
state.Current.JsonPropertyInfo = jsonPropertyInfo;
state.Current.NumberHandling = jsonPropertyInfo.NumberHandling;
state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;

bool success = jsonPropertyInfo.GetMemberAndWriteJson(obj, ref state, writer);
// Converters only return 'false' when out of data which is not possible in fast path.
Expand All @@ -303,7 +303,7 @@ internal sealed override bool OnTryWrite(
{
// Remember the current property for JsonPath support if an exception is thrown.
state.Current.JsonPropertyInfo = dataExtensionProperty;
state.Current.NumberHandling = dataExtensionProperty.NumberHandling;
state.Current.NumberHandling = dataExtensionProperty.EffectiveNumberHandling;

bool success = dataExtensionProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer);
Debug.Assert(success);
Expand Down Expand Up @@ -340,7 +340,7 @@ internal sealed override bool OnTryWrite(
if (jsonPropertyInfo.ShouldSerialize)
{
state.Current.JsonPropertyInfo = jsonPropertyInfo;
state.Current.NumberHandling = jsonPropertyInfo.NumberHandling;
state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;

if (!jsonPropertyInfo.GetMemberAndWriteJson(obj!, ref state, writer))
{
Expand Down Expand Up @@ -370,7 +370,7 @@ internal sealed override bool OnTryWrite(
{
// Remember the current property for JsonPath support if an exception is thrown.
state.Current.JsonPropertyInfo = dataExtensionProperty;
state.Current.NumberHandling = dataExtensionProperty.NumberHandling;
state.Current.NumberHandling = dataExtensionProperty.EffectiveNumberHandling;

if (!dataExtensionProperty.GetMemberAndWriteJsonExtensionData(obj, ref state, writer))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,9 @@ protected sealed override void InitializeConstructorArgumentCaches(ref ReadStack
{
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;

// Ensure property cache has been initialized.
Debug.Assert(typeInfo.PropertyCache != null);
Debug.Assert(typeInfo.ParameterCache != null);

if (typeInfo.ParameterCache == null)
{
typeInfo.InitializePropCache();
}

List<KeyValuePair<string, JsonParameterInfo?>> cache = typeInfo.ParameterCache!.List;
List<KeyValuePair<string, JsonParameterInfo?>> cache = typeInfo.ParameterCache.List;
object?[] arguments = ArrayPool<object>.Shared.Rent(cache.Count);

for (int i = 0; i < typeInfo.ParameterCount; i++)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal sealed override bool OnTryRead(ref Utf8JsonReader reader, Type typeToCo

state.Current.JsonPropertyName = propertyNameArray;
state.Current.JsonPropertyInfo = jsonPropertyInfo;
state.Current.NumberHandling = jsonPropertyInfo.NumberHandling;
state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;

bool useExtensionProperty = dataExtKey != null;

Expand Down Expand Up @@ -505,7 +505,11 @@ private static bool HandlePropertyWithContinuation(
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BeginRead(ref ReadStack state, ref Utf8JsonReader reader, JsonSerializerOptions options)
{
if (state.Current.JsonTypeInfo.ParameterCount != state.Current.JsonTypeInfo.ParameterCache!.Count)
JsonTypeInfo jsonTypeInfo = state.Current.JsonTypeInfo;

jsonTypeInfo.ValidateCanBeUsedForDeserialization();

if (jsonTypeInfo.ParameterCount != jsonTypeInfo.ParameterCache!.Count)
{
ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(TypeToConvert);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ internal static JsonPropertyInfo LookupProperty(
}

state.Current.JsonPropertyInfo = jsonPropertyInfo;
state.Current.NumberHandling = jsonPropertyInfo.NumberHandling;
state.Current.NumberHandling = jsonPropertyInfo.EffectiveNumberHandling;
return jsonPropertyInfo;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static partial class JsonSerializer
var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);

ReadStack state = default;
jsonTypeInfo.EnsureConfigured();
state.Initialize(jsonTypeInfo);

TValue? value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ static async IAsyncEnumerable<TValue> CreateAsyncEnumerableDeserializer(
JsonConverter converter = QueueOfTConverter<Queue<TValue>, TValue>.Instance;
JsonTypeInfo jsonTypeInfo = CreateQueueJsonTypeInfo<TValue>(converter, options);
ReadStack readStack = default;
jsonTypeInfo.EnsureConfigured();
readStack.Initialize(jsonTypeInfo, supportContinuation: true);
var jsonReaderState = new JsonReaderState(options.GetReaderOptions());

Expand Down Expand Up @@ -334,7 +335,7 @@ static async IAsyncEnumerable<TValue> CreateAsyncEnumerableDeserializer(
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
Justification = "Workaround for https://github.com/mono/linker/issues/1416. All usages are marked as unsafe.")]
private static JsonTypeInfo CreateQueueJsonTypeInfo<TValue>(JsonConverter queueConverter, JsonSerializerOptions queueOptions) =>
new JsonTypeInfo(typeof(Queue<TValue>), queueConverter, queueOptions);
new ReflectionJsonTypeInfo<Queue<TValue>>(queueConverter, queueOptions);

internal static async ValueTask<TValue?> ReadAllAsync<TValue>(
Stream utf8Json,
Expand All @@ -344,6 +345,7 @@ private static JsonTypeInfo CreateQueueJsonTypeInfo<TValue>(JsonConverter queueC
JsonSerializerOptions options = jsonTypeInfo.Options;
var bufferState = new ReadBufferState(options.DefaultBufferSize);
ReadStack readStack = default;
jsonTypeInfo.EnsureConfigured();
readStack.Initialize(jsonTypeInfo, supportContinuation: true);
JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase;
var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
Expand Down Expand Up @@ -374,6 +376,7 @@ private static JsonTypeInfo CreateQueueJsonTypeInfo<TValue>(JsonConverter queueC
JsonSerializerOptions options = jsonTypeInfo.Options;
var bufferState = new ReadBufferState(options.DefaultBufferSize);
ReadStack readStack = default;
jsonTypeInfo.EnsureConfigured();
readStack.Initialize(jsonTypeInfo, supportContinuation: true);
JsonConverter converter = readStack.Current.JsonPropertyInfo!.ConverterBase;
var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ public static partial class JsonSerializer

private static TValue? ReadFromSpan<TValue>(ReadOnlySpan<char> json, JsonTypeInfo jsonTypeInfo)
{
jsonTypeInfo.EnsureConfigured();
byte[]? tempArray = null;

// For performance, avoid obtaining actual byte count unless memory usage is higher than the threshold.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ public static partial class JsonSerializer
private static TValue? Read<TValue>(ref Utf8JsonReader reader, JsonTypeInfo jsonTypeInfo)
{
ReadStack state = default;
jsonTypeInfo.EnsureConfigured();
state.Initialize(jsonTypeInfo);

JsonReaderState readerState = reader.CurrentState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ jsonTypeInfo is not JsonTypeInfo<TValue> ||
"Incorrect method called. WriteUsingGeneratedSerializer() should have been called instead.");

WriteStack state = default;
jsonTypeInfo.EnsureConfigured();
state.Initialize(jsonTypeInfo, supportContinuation: false, supportAsync: false);

JsonConverter converter = jsonTypeInfo.PropertyInfoForTypeInfo.ConverterBase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ private static async Task WriteStreamAsync<TValue>(
using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions))
{
WriteStack state = new WriteStack { CancellationToken = cancellationToken };
jsonTypeInfo.EnsureConfigured();
JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true, supportAsync: true);

bool isFinalBlock;
Expand Down Expand Up @@ -329,6 +330,7 @@ private static void WriteStream<TValue>(
using (var writer = new Utf8JsonWriter(bufferWriter, writerOptions))
{
WriteStack state = default;
jsonTypeInfo.EnsureConfigured();
JsonConverter converter = state.Initialize(jsonTypeInfo, supportContinuation: true, supportAsync: false);

bool isFinalBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Text.Json.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Converters;
Expand Down Expand Up @@ -41,7 +42,33 @@ private static void RootReflectionSerializerDependencies()
}

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) => new JsonTypeInfo(type, options);
static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
{
JsonTypeInfo.ValidateType(type, null, null, options);

MethodInfo methodInfo = typeof(JsonSerializerOptions).GetMethod(nameof(CreateReflectionJsonTypeInfo), BindingFlags.NonPublic | BindingFlags.Instance)!;
#if NETCOREAPP
return (JsonTypeInfo)methodInfo.MakeGenericMethod(type).Invoke(options, BindingFlags.NonPublic | BindingFlags.DoNotWrapExceptions, null, null, null)!;
#else
try
{
return (JsonTypeInfo)methodInfo.MakeGenericMethod(type).Invoke(options, null)!;
}
catch (TargetInvocationException ex)
{
// Some of the validation is done during construction (i.e. validity of JsonConverter, inner types etc.)
// therefore we need to unwrap TargetInvocationException for better user experience
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
throw ex.InnerException;
}
#endif
}
}

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
private JsonTypeInfo<T> CreateReflectionJsonTypeInfo<T>()
{
return new ReflectionJsonTypeInfo<T>(this);
}

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -600,21 +600,22 @@ internal void InitializeForReflectionSerializer()
private JsonTypeInfo GetJsonTypeInfoFromContextOrCreate(Type type)
{
JsonTypeInfo? info = _serializerContext?.GetTypeInfo(type);
if (info != null)
if (info == null && IsInitializedForReflectionSerializer)
{
return info;
Debug.Assert(
s_typeInfoCreationFunc != null,
"Reflection-based JsonTypeInfo creator should be initialized if IsInitializedForReflectionSerializer is true.");
info = s_typeInfoCreationFunc(type, this);
}

if (!IsInitializedForReflectionSerializer)
if (info == null)
{
ThrowHelper.ThrowNotSupportedException_NoMetadataForType(type);
return null!;
}

Debug.Assert(
s_typeInfoCreationFunc != null,
"Reflection-based JsonTypeInfo creator should be initialized if IsInitializedForReflectionSerializer is true.");
return s_typeInfoCreationFunc(type, this);
info.EnsureConfigured();
return info;
}

internal JsonDocumentOptions GetDocumentOptions()
Expand Down
Loading

0 comments on commit 98e0173

Please sign in to comment.