Skip to content

Commit

Permalink
Reduce CrossGen assembly size
Browse files Browse the repository at this point in the history
  • Loading branch information
steveharter committed Jun 11, 2020
1 parent 7ced31c commit 60a3233
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class LargeObjectWithParameterizedConstructorConverter<T> : Obje
{
protected override bool ReadAndCacheConstructorArgument(ref ReadStack state, ref Utf8JsonReader reader, JsonParameterInfo jsonParameterInfo)
{
bool success = jsonParameterInfo.ReadJson(ref state, ref reader, out object? arg);
bool success = jsonParameterInfo.ConverterBase.TryReadAsObject(ref reader, jsonParameterInfo.Options, ref state, out object? arg);

if (success)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,49 @@ protected override bool ReadAndCacheConstructorArgument(ref ReadStack state, ref
switch (jsonParameterInfo.Position)
{
case 0:
success = ((JsonParameterInfo<TArg0>)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg0 arg0);
if (success)
{
arguments.Arg0 = arg0!;
var info = (JsonParameterInfo<TArg0>)jsonParameterInfo;
var converter = (JsonConverter<TArg0>)jsonParameterInfo.ConverterBase;
success = converter.TryRead(ref reader, info.RuntimePropertyType, info.Options, ref state, out TArg0 arg0);
if (success)
{
arguments.Arg0 = arg0!;
}
break;
}
break;
case 1:
success = ((JsonParameterInfo<TArg1>)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg1 arg1);
if (success)
{
arguments.Arg1 = arg1!;
var info = (JsonParameterInfo<TArg1>)jsonParameterInfo;
var converter = (JsonConverter<TArg1>)jsonParameterInfo.ConverterBase;
success = converter.TryRead(ref reader, info.RuntimePropertyType, info.Options, ref state, out TArg1 arg1);
if (success)
{
arguments.Arg1 = arg1!;
}
break;
}
break;
case 2:
success = ((JsonParameterInfo<TArg2>)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg2 arg2);
if (success)
{
arguments.Arg2 = arg2!;
var info = (JsonParameterInfo<TArg2>)jsonParameterInfo;
var converter = (JsonConverter<TArg2>)jsonParameterInfo.ConverterBase;
success = converter.TryRead(ref reader, info.RuntimePropertyType, info.Options, ref state, out TArg2 arg2);
if (success)
{
arguments.Arg2 = arg2!;
}
break;
}
break;
case 3:
success = ((JsonParameterInfo<TArg3>)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg3 arg3);
if (success)
{
arguments.Arg3 = arg3!;
var info = (JsonParameterInfo<TArg3>)jsonParameterInfo;
var converter = (JsonConverter<TArg3>)jsonParameterInfo.ConverterBase;
success = converter.TryRead(ref reader, info.RuntimePropertyType, info.Options, ref state, out TArg3 arg3);
if (success)
{
arguments.Arg3 = arg3!;
}
break;
}
break;
default:
Debug.Fail("More than 4 params: we should be in override for LargeObjectWithParameterizedConstructorConverter.");
throw new InvalidOperationException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,14 +342,13 @@ private static JsonParameterInfo AddConstructorParameter(
{
if (jsonPropertyInfo.IsIgnored)
{
return JsonParameterInfo.CreateIgnoredParameterPlaceholder(parameterInfo, jsonPropertyInfo, options);
return JsonParameterInfo.CreateIgnoredParameterPlaceholder(jsonPropertyInfo, options);
}

JsonConverter converter = jsonPropertyInfo.ConverterBase;

JsonParameterInfo jsonParameterInfo = converter.CreateJsonParameterInfo();
jsonParameterInfo.Initialize(
jsonPropertyInfo.DeclaredPropertyType,
jsonPropertyInfo.RuntimePropertyType!,
parameterInfo,
jsonPropertyInfo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ internal bool ShouldFlush(Utf8JsonWriter writer, ref WriteStack state)
// This is used internally to quickly determine the type being converted for JsonConverter<T>.
internal abstract Type TypeToConvert { get; }

internal abstract bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value);

internal abstract bool TryWriteAsObject(Utf8JsonWriter writer, object? value, JsonSerializerOptions options, ref WriteStack state);

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ internal sealed override object ReadCoreAsObject(
throw new InvalidOperationException();
}

internal sealed override bool TryReadAsObject(
ref Utf8JsonReader reader,
JsonSerializerOptions options,
ref ReadStack state,
out object? value)
{
Debug.Fail("We should never get here.");

throw new InvalidOperationException();
}

internal sealed override bool TryWriteAsObject(
Utf8JsonWriter writer,
object? value,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;

namespace System.Text.Json.Serialization
{
Expand Down Expand Up @@ -219,7 +220,7 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
VerifyRead(
state.Current.OriginalTokenType,
state.Current.OriginalDepth,
bytesConsumed : 0,
bytesConsumed: 0,
isValueConverter: false,
ref reader);

Expand All @@ -231,6 +232,13 @@ internal bool TryRead(ref Utf8JsonReader reader, Type typeToConvert, JsonSeriali
return success;
}

internal override sealed bool TryReadAsObject(ref Utf8JsonReader reader, JsonSerializerOptions options, ref ReadStack state, out object? value)
{
bool success = TryRead(ref reader, TypeToConvert, options, ref state, out T typedValue);
value = typedValue;
return success;
}

internal bool TryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, ref WriteStack state)
{
if (writer.CurrentDepth >= options.EffectiveMaxDepth)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,16 @@ namespace System.Text.Json
/// Holds relevant state about a method parameter, like the default value of
/// the parameter, and the position in the method's parameter list.
/// </summary>
[DebuggerDisplay("ParameterInfo={ParameterInfo}")]
internal abstract class JsonParameterInfo
{
private Type _runtimePropertyType = null!;

public abstract JsonConverter ConverterBase { get; }
public JsonConverter ConverterBase { get; set; } = null!;

// The default value of the parameter. This is `DefaultValue` of the `ParameterInfo`, if specified, or the CLR `default` for the `ParameterType`.
public object? DefaultValue { get; protected set; }

// Options can be referenced here since all JsonPropertyInfos originate from a JsonClassInfo that is cached on JsonSerializerOptions.
protected JsonSerializerOptions Options { get; set; } = null!; // initialized in Init method

public ParameterInfo ParameterInfo { get; private set; } = null!;
protected internal JsonSerializerOptions Options { get; set; } = null!; // initialized in Init method

// The name of the parameter as UTF-8 bytes.
public byte[] NameAsUtf8Bytes { get; private set; } = null!;
Expand All @@ -40,54 +36,42 @@ public JsonClassInfo RuntimeClassInfo
{
if (_runtimeClassInfo == null)
{
_runtimeClassInfo = Options.GetOrAddClass(_runtimePropertyType);
_runtimeClassInfo = Options.GetOrAddClass(RuntimePropertyType);
}

return _runtimeClassInfo;
}
}

internal Type RuntimePropertyType { get; set; } = null!;

public bool ShouldDeserialize { get; private set; }

public virtual void Initialize(
Type declaredPropertyType,
Type runtimePropertyType,
ParameterInfo parameterInfo,
JsonPropertyInfo matchingProperty,
JsonSerializerOptions options)
{
_runtimePropertyType = runtimePropertyType;

Options = options;
ParameterInfo = parameterInfo;
RuntimePropertyType = runtimePropertyType;
Position = parameterInfo.Position;
ShouldDeserialize = true;

DetermineParameterName(matchingProperty);
}

private void DetermineParameterName(JsonPropertyInfo matchingProperty)
{
NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!;
Options = options;
ShouldDeserialize = true;
}

// Create a parameter that is ignored at run-time. It uses the same type (typeof(sbyte)) to help
// prevent issues with unsupported types and helps ensure we don't accidently (de)serialize it.
public static JsonParameterInfo CreateIgnoredParameterPlaceholder(
ParameterInfo parameterInfo,
JsonPropertyInfo matchingProperty,
JsonSerializerOptions options)
{
JsonParameterInfo jsonParameterInfo = new JsonParameterInfo<sbyte>();
jsonParameterInfo.Options = options;
jsonParameterInfo.ParameterInfo = parameterInfo;
jsonParameterInfo.ShouldDeserialize = false;

jsonParameterInfo.DetermineParameterName(matchingProperty);

return jsonParameterInfo;
return new JsonParameterInfo<sbyte>
{
RuntimePropertyType = typeof(sbyte),
NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!,
Options = options
};
}

public abstract bool ReadJson(ref ReadStack state, ref Utf8JsonReader reader, out object? argument);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json.Serialization;

namespace System.Text.Json
{
Expand All @@ -14,29 +12,21 @@ namespace System.Text.Json
/// </summary>
internal class JsonParameterInfo<T> : JsonParameterInfo
{
private JsonConverter<T> _converter = null!;
private Type _runtimePropertyType = null!;

public override JsonConverter ConverterBase => _converter;

public T TypedDefaultValue { get; private set; } = default!;

public override void Initialize(
Type declaredPropertyType,
Type runtimePropertyType,
ParameterInfo parameterInfo,
JsonPropertyInfo matchingProperty,
JsonSerializerOptions options)
{
base.Initialize(
declaredPropertyType,
runtimePropertyType,
parameterInfo,
matchingProperty,
options);

_converter = (JsonConverter<T>)matchingProperty.ConverterBase;
_runtimePropertyType = runtimePropertyType;
ConverterBase = matchingProperty.ConverterBase;

if (parameterInfo.HasDefaultValue)
{
Expand All @@ -48,72 +38,5 @@ public override void Initialize(
DefaultValue = TypedDefaultValue;
}
}

public override bool ReadJson(ref ReadStack state, ref Utf8JsonReader reader, out object? value)
{
bool success;
bool isNullToken = reader.TokenType == JsonTokenType.Null;

if (isNullToken && !_converter.HandleNull && !state.IsContinuation)
{
if (!_converter.CanBeNull)
{
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(_converter.TypeToConvert);
}

// Don't have to check for IgnoreNullValue option here because we set the default value regardless.
value = DefaultValue;
return true;
}
else
{
// Optimize for internal converters by avoiding the extra call to TryRead.
if (_converter.CanUseDirectReadOrWrite)
{
value = _converter.Read(ref reader, _runtimePropertyType, Options);
return true;
}
else
{
success = _converter.TryRead(ref reader, _runtimePropertyType, Options, ref state, out T typedValue);
value = typedValue;
}
}

return success;
}

public bool ReadJsonTyped(ref ReadStack state, ref Utf8JsonReader reader, [MaybeNull] out T value)
{
bool success;
bool isNullToken = reader.TokenType == JsonTokenType.Null;

if (isNullToken && !_converter.HandleNull && !state.IsContinuation)
{
if (!_converter.CanBeNull)
{
ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(_converter.TypeToConvert);
}

// Don't have to check for IgnoreNullValue option here because we set the default value regardless.
value = TypedDefaultValue;
return true;
}
else
{
// Optimize for internal converters by avoiding the extra call to TryRead.
if (_converter.CanUseDirectReadOrWrite)
{
value = _converter.Read(ref reader, _runtimePropertyType, Options);
return true;
}
else
{
success = _converter.TryRead(ref reader, _runtimePropertyType, Options, ref state, out value);
}
}

return success;
}
}
}

0 comments on commit 60a3233

Please sign in to comment.