From 60a32331cf6cd315dece5cf06900792e745b27aa Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 10 Jun 2020 18:01:53 -0500 Subject: [PATCH] Reduce CrossGen assembly size --- ...ParameterizedConstructorConverter.Large.cs | 2 +- ...ParameterizedConstructorConverter.Small.cs | 48 +++++++---- .../Text/Json/Serialization/JsonClassInfo.cs | 3 +- .../Text/Json/Serialization/JsonConverter.cs | 2 + .../Serialization/JsonConverterFactory.cs | 11 +++ .../Json/Serialization/JsonConverterOfT.cs | 10 ++- .../Json/Serialization/JsonParameterInfo.cs | 44 ++++------- .../Serialization/JsonParameterInfoOfT.cs | 79 +------------------ 8 files changed, 71 insertions(+), 128 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs index a89d1910c365b..00ad9f21aa4a4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Large.cs @@ -14,7 +14,7 @@ internal sealed class LargeObjectWithParameterizedConstructorConverter : 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) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs index f79b5f6f8350e..088e6c2f9cea6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Object/ObjectWithParameterizedConstructorConverter.Small.cs @@ -30,33 +30,49 @@ protected override bool ReadAndCacheConstructorArgument(ref ReadStack state, ref switch (jsonParameterInfo.Position) { case 0: - success = ((JsonParameterInfo)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg0 arg0); - if (success) { - arguments.Arg0 = arg0!; + var info = (JsonParameterInfo)jsonParameterInfo; + var converter = (JsonConverter)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)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg1 arg1); - if (success) { - arguments.Arg1 = arg1!; + var info = (JsonParameterInfo)jsonParameterInfo; + var converter = (JsonConverter)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)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg2 arg2); - if (success) { - arguments.Arg2 = arg2!; + var info = (JsonParameterInfo)jsonParameterInfo; + var converter = (JsonConverter)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)jsonParameterInfo).ReadJsonTyped(ref state, ref reader, out TArg3 arg3); - if (success) { - arguments.Arg3 = arg3!; + var info = (JsonParameterInfo)jsonParameterInfo; + var converter = (JsonConverter)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(); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs index 5e4e2845e3bfb..631d47ddc0161 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonClassInfo.cs @@ -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, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs index 361236c42d60b..cc73208798ab4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverter.cs @@ -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. 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); /// diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs index 4a26cdaab2d80..5186002fe4077 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterFactory.cs @@ -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, diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index bce4c1224ced7..47dcc37d9b78c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; namespace System.Text.Json.Serialization { @@ -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); @@ -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) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfo.cs index f2d8408205d31..67a54d7deb89c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfo.cs @@ -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. /// - [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!; @@ -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(); - jsonParameterInfo.Options = options; - jsonParameterInfo.ParameterInfo = parameterInfo; - jsonParameterInfo.ShouldDeserialize = false; - - jsonParameterInfo.DetermineParameterName(matchingProperty); - - return jsonParameterInfo; + return new JsonParameterInfo + { + RuntimePropertyType = typeof(sbyte), + NameAsUtf8Bytes = matchingProperty.NameAsUtf8Bytes!, + Options = options + }; } - - public abstract bool ReadJson(ref ReadStack state, ref Utf8JsonReader reader, out object? argument); } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfoOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfoOfT.cs index a03982f4e40d8..09dac26c2ae9c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfoOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonParameterInfoOfT.cs @@ -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 { @@ -14,29 +12,21 @@ namespace System.Text.Json /// internal class JsonParameterInfo : JsonParameterInfo { - private JsonConverter _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)matchingProperty.ConverterBase; - _runtimePropertyType = runtimePropertyType; + ConverterBase = matchingProperty.ConverterBase; if (parameterInfo.HasDefaultValue) { @@ -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; - } } }