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

Recoup some of the perf losses in cold start serialization #73497

Merged
merged 1 commit into from
Aug 8, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,19 @@ private static bool HasCustomAttributeWithName(this ICustomAttributeProvider mem
/// <summary>
/// Polyfill for BindingFlags.DoNotWrapExceptions
/// </summary>
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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TTarget> CreateCastingConverter<TTarget>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<T>(this, options);
}

internal sealed override JsonTypeInfo CreateCustomJsonTypeInfo(JsonSerializerOptions options)
{
return new CustomJsonTypeInfo<T>(this, options);
}

internal sealed override JsonParameterInfo CreateJsonParameterInfo()
{
return new JsonParameterInfo<T>();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<T> CreateReflectionJsonTypeInfo<T>(JsonSerializerOptions options)
{
JsonConverter converter = GetConverterForType(typeof(T), options);
return new ReflectionJsonTypeInfo<T>(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;
}

/// <summary>
/// Gets a list of user-defined callbacks that can be used to modify the initial contract.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
{
Expand All @@ -69,9 +68,10 @@ internal JsonPropertyInfo CreatePropertyUsingReflection(Type propertyType)
{
// Metadata for `propertyType` has not been registered yet.
// Use reflection to instantiate the correct JsonPropertyInfo<T>
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);
Expand All @@ -83,11 +83,6 @@ internal JsonPropertyInfo CreatePropertyUsingReflection(Type propertyType)
/// </summary>
private protected abstract JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, JsonSerializerOptions options);

private static JsonPropertyInfo CreateJsonPropertyInfo<T>(JsonTypeInfo declaringTypeInfo, JsonSerializerOptions options)
=> new JsonPropertyInfo<T>(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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -634,8 +633,6 @@ public static JsonTypeInfo<T> CreateJsonTypeInfo<T>(JsonSerializerOptions option
return new CustomJsonTypeInfo<T>(converter, options);
}

private static MethodInfo? s_createJsonTypeInfo;

/// <summary>
/// Creates a blank <see cref="JsonTypeInfo"/> instance.
/// </summary>
Expand Down Expand Up @@ -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;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down