Skip to content

Commit

Permalink
Cleanup JsonTypeInfo construction (#67700)
Browse files Browse the repository at this point in the history
  • Loading branch information
krwq committed Apr 7, 2022
1 parent 7b26638 commit 02f0b21
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options)
// 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;
throw null!;
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,7 @@ public static JsonPropertyInfo CreatePropertyInfo<T>(JsonSerializerOptions optio
public static JsonTypeInfo<T> CreateValueInfo<T>(JsonSerializerOptions options, JsonConverter converter)
{
JsonTypeInfo<T> info = new SourceGenJsonTypeInfo<T>(converter, options);
info.PropertyInfoForTypeInfo = CreateJsonPropertyInfoForClassInfo(typeof(T), info, converter, options);
return info;
}

internal static JsonPropertyInfo CreateJsonPropertyInfoForClassInfo(
Type type,
JsonTypeInfo typeInfo,
JsonConverter converter,
JsonSerializerOptions options)
{
JsonPropertyInfo propertyInfo = converter.CreateJsonPropertyInfo();
propertyInfo.InitializeForTypeInfo(type, typeInfo, converter, options);
return propertyInfo;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,21 @@ internal void Configure()
_isConfigured = true;
}

internal virtual void GetPolicies(JsonIgnoreCondition? ignoreCondition)
internal void GetPolicies(JsonIgnoreCondition? ignoreCondition)
{
if (!IsForTypeInfo)
{
Debug.Assert(MemberInfo != null);
DetermineSerializationCapabilities(ignoreCondition);
DeterminePropertyName();
DetermineIgnoreCondition(ignoreCondition);

JsonPropertyOrderAttribute? orderAttr = GetAttribute<JsonPropertyOrderAttribute>(MemberInfo);
if (orderAttr != null)
{
Order = orderAttr.Order;
}
Debug.Assert(MemberInfo != null);
DetermineSerializationCapabilities(ignoreCondition);
DeterminePropertyName();
DetermineIgnoreCondition(ignoreCondition);

JsonNumberHandlingAttribute? attribute = GetAttribute<JsonNumberHandlingAttribute>(MemberInfo);
NumberHandling = attribute?.Handling;
JsonPropertyOrderAttribute? orderAttr = GetAttribute<JsonPropertyOrderAttribute>(MemberInfo);
if (orderAttr != null)
{
Order = orderAttr.Order;
}

JsonNumberHandlingAttribute? attribute = GetAttribute<JsonNumberHandlingAttribute>(MemberInfo);
NumberHandling = attribute?.Handling;
}

private void DeterminePropertyName()
Expand Down Expand Up @@ -330,33 +327,16 @@ private bool NumberHandingIsApplicable()
internal bool HasGetter { get; set; }
internal bool HasSetter { get; set; }

internal virtual void Initialize(
internal abstract void Initialize(
Type parentClassType,
Type declaredPropertyType,
ConverterStrategy converterStrategy,
MemberInfo? memberInfo,
bool isVirtual,
JsonConverter converter,
JsonIgnoreCondition? ignoreCondition,
JsonNumberHandling? parentTypeNumberHandling,
JsonSerializerOptions options)
{
Debug.Assert(converter != null);

DeclaringType = parentClassType;
PropertyType = declaredPropertyType;
ConverterStrategy = converterStrategy;
MemberInfo = memberInfo;
IsVirtual = isVirtual;
ConverterBase = converter;
Options = options;
}

internal abstract void InitializeForTypeInfo(
Type declaredType,
JsonTypeInfo runtimeTypeInfo,
JsonConverter converter,
JsonSerializerOptions options);
JsonSerializerOptions options,
JsonTypeInfo? jsonTypeInfo = null);

internal bool IgnoreDefaultValuesOnRead { get; private set; }
internal bool IgnoreDefaultValuesOnWrite { get; private set; }
Expand Down Expand Up @@ -483,7 +463,7 @@ internal bool ReadJsonExtensionDataValue(ref ReadStack state, ref Utf8JsonReader

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

internal MemberInfo? MemberInfo { get; private set; }
internal MemberInfo? MemberInfo { get; set; }

internal JsonTypeInfo JsonTypeInfo
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,78 +43,89 @@ internal sealed class JsonPropertyInfo<T> : JsonPropertyInfo
bool isVirtual,
JsonConverter converter,
JsonIgnoreCondition? ignoreCondition,
JsonNumberHandling? parentTypeNumberHandling,
JsonSerializerOptions options)
JsonSerializerOptions options,
JsonTypeInfo? jsonTypeInfo = null)
{
base.Initialize(
parentClassType,
declaredPropertyType,
converterStrategy,
memberInfo,
isVirtual,
converter,
ignoreCondition,
parentTypeNumberHandling,
options);

switch (memberInfo)
Debug.Assert(converter != null);

PropertyType = declaredPropertyType;
ConverterStrategy = converterStrategy;
if (jsonTypeInfo != null)
{
case PropertyInfo propertyInfo:
{
bool useNonPublicAccessors = GetAttribute<JsonIncludeAttribute>(propertyInfo) != null;
JsonTypeInfo = jsonTypeInfo;
}

MethodInfo? getMethod = propertyInfo.GetMethod;
if (getMethod != null && (getMethod.IsPublic || useNonPublicAccessors))
{
HasGetter = true;
Get = options.MemberAccessorStrategy.CreatePropertyGetter<T>(propertyInfo);
}
ConverterBase = converter;
Options = options;
DeclaringType = parentClassType;
MemberInfo = memberInfo;
IsVirtual = isVirtual;

MethodInfo? setMethod = propertyInfo.SetMethod;
if (setMethod != null && (setMethod.IsPublic || useNonPublicAccessors))
if (memberInfo != null)
{
switch (memberInfo)
{
case PropertyInfo propertyInfo:
{
HasSetter = true;
Set = options.MemberAccessorStrategy.CreatePropertySetter<T>(propertyInfo);
}
bool useNonPublicAccessors = GetAttribute<JsonIncludeAttribute>(propertyInfo) != null;

MemberType = MemberTypes.Property;
MethodInfo? getMethod = propertyInfo.GetMethod;
if (getMethod != null && (getMethod.IsPublic || useNonPublicAccessors))
{
HasGetter = true;
Get = options.MemberAccessorStrategy.CreatePropertyGetter<T>(propertyInfo);
}

break;
}
MethodInfo? setMethod = propertyInfo.SetMethod;
if (setMethod != null && (setMethod.IsPublic || useNonPublicAccessors))
{
HasSetter = true;
Set = options.MemberAccessorStrategy.CreatePropertySetter<T>(propertyInfo);
}

case FieldInfo fieldInfo:
{
Debug.Assert(fieldInfo.IsPublic);
MemberType = MemberTypes.Property;

HasGetter = true;
Get = options.MemberAccessorStrategy.CreateFieldGetter<T>(fieldInfo);
break;
}

if (!fieldInfo.IsInitOnly)
case FieldInfo fieldInfo:
{
HasSetter = true;
Set = options.MemberAccessorStrategy.CreateFieldSetter<T>(fieldInfo);
}
Debug.Assert(fieldInfo.IsPublic);

MemberType = MemberTypes.Field;
HasGetter = true;
Get = options.MemberAccessorStrategy.CreateFieldGetter<T>(fieldInfo);

break;
}
if (!fieldInfo.IsInitOnly)
{
HasSetter = true;
Set = options.MemberAccessorStrategy.CreateFieldSetter<T>(fieldInfo);
}

default:
{
IsForTypeInfo = true;
HasGetter = true;
HasSetter = true;
MemberType = MemberTypes.Field;

break;
}
}
break;
}

_converterIsExternalAndPolymorphic = !converter.IsInternalConverter && PropertyType != converter.TypeToConvert;
PropertyTypeCanBeNull = PropertyType.CanBeNull();
_propertyTypeEqualsTypeToConvert = typeof(T) == PropertyType;
default:
{
Debug.Fail($"Invalid memberInfo type: {memberInfo.GetType().FullName}");
break;
}
}

// TODO (perf): can we pre-compute some of these values during source gen?
_converterIsExternalAndPolymorphic = !converter.IsInternalConverter && PropertyType != converter.TypeToConvert;
PropertyTypeCanBeNull = PropertyType.CanBeNull();
_propertyTypeEqualsTypeToConvert = typeof(T) == PropertyType;

GetPolicies(ignoreCondition);
GetPolicies(ignoreCondition);
}
else
{
IsForTypeInfo = true;
HasGetter = true;
HasSetter = true;
}
}

internal void InitializeForSourceGen(JsonSerializerOptions options, JsonPropertyInfoValues<T> propertyInfo)
Expand Down Expand Up @@ -189,30 +200,6 @@ internal void InitializeForSourceGen(JsonSerializerOptions options, JsonProperty
}
}

/// <summary>
/// Create a <see cref="JsonPropertyInfo"/> for a given Type.
/// See <seealso cref="JsonTypeInfo.PropertyInfoForTypeInfo"/>.
/// </summary>
internal override void InitializeForTypeInfo(
Type declaredType,
JsonTypeInfo runtimeTypeInfo,
JsonConverter converter,
JsonSerializerOptions options)
{
PropertyType = declaredType;
ConverterStrategy = converter.ConverterStrategy;
JsonTypeInfo = runtimeTypeInfo;
ConverterBase = converter;
Options = options;
IsForTypeInfo = true;
HasGetter = true;
HasSetter = true;
// TODO (perf): can we pre-compute some of these values during source gen?
_converterIsExternalAndPolymorphic = !converter.IsInternalConverter && declaredType != converter.TypeToConvert;
PropertyTypeCanBeNull = declaredType.CanBeNull();
_propertyTypeEqualsTypeToConvert = typeof(T) == declaredType;
}

internal override JsonConverter ConverterBase
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ public partial class JsonTypeInfo
bool isVirtual,
JsonConverter converter,
JsonSerializerOptions options,
JsonNumberHandling? parentTypeNumberHandling = null,
JsonIgnoreCondition? ignoreCondition = null)
JsonIgnoreCondition? ignoreCondition = null,
JsonTypeInfo? jsonTypeInfo = null)
{
// Create the JsonPropertyInfo instance.
JsonPropertyInfo jsonPropertyInfo = converter.CreateJsonPropertyInfo();
Expand All @@ -72,8 +72,8 @@ public partial class JsonTypeInfo
isVirtual,
converter,
ignoreCondition,
parentTypeNumberHandling,
options);
options,
jsonTypeInfo);

return jsonPropertyInfo;
}
Expand All @@ -85,17 +85,17 @@ public partial class JsonTypeInfo
private static JsonPropertyInfo CreatePropertyInfoForTypeInfo(
Type declaredPropertyType,
JsonConverter converter,
JsonNumberHandling? numberHandling,
JsonSerializerOptions options)
JsonSerializerOptions options,
JsonTypeInfo? jsonTypeInfo = null)
{
JsonPropertyInfo jsonPropertyInfo = CreateProperty(
declaredPropertyType: declaredPropertyType,
memberInfo: null, // Not a real property so this is null.
parentClassType: ObjectType, // a dummy value (not used)
isVirtual: false,
converter,
options,
parentTypeNumberHandling: numberHandling);
converter: converter,
options: options,
jsonTypeInfo: jsonTypeInfo);

Debug.Assert(jsonPropertyInfo.IsForTypeInfo);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ internal void ValidateCanBeUsedForDeserialization()
/// TypeInfo (for the cases mentioned above). In addition, methods that have a JsonPropertyInfo argument would also likely
/// need to add an argument for JsonTypeInfo.
/// </remarks>
internal JsonPropertyInfo PropertyInfoForTypeInfo { get; set; }
internal JsonPropertyInfo PropertyInfoForTypeInfo { get; private set; }

/// <summary>
/// Returns a helper class used for computing the default value.
Expand All @@ -162,24 +162,11 @@ internal void ValidateCanBeUsedForDeserialization()

internal JsonNumberHandling? NumberHandling { get; set; }

internal JsonTypeInfo()
{
Debug.Assert(false, "This constructor should not be called.");
}

internal JsonTypeInfo(Type type, JsonSerializerOptions options!!)
{
Type = type;
Options = options;
// Setting this option is deferred to the initialization methods of the various metadada info types.
PropertyInfoForTypeInfo = null!;
}

internal JsonTypeInfo(Type type, JsonConverter converter, JsonSerializerOptions options)
{
Type = type;
Options = options;
PropertyInfoForTypeInfo = CreatePropertyInfoForTypeInfo(Type, converter, NumberHandling, Options);
PropertyInfoForTypeInfo = CreatePropertyInfoForTypeInfo(Type, converter, Options, this);
ElementType = converter.ElementType;

switch (PropertyInfoForTypeInfo.ConverterStrategy)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,10 @@ public abstract class JsonTypeInfo<T> : JsonTypeInfo
{
private Action<Utf8JsonWriter, T>? _serialize;

internal JsonTypeInfo(Type type, JsonSerializerOptions options) :
base(type, options)
{ }

internal JsonTypeInfo(JsonConverter converter, JsonSerializerOptions options)
: base(typeof(T), converter, options)
{ }

internal JsonTypeInfo()
{
Debug.Assert(false, "This constructor should not be called.");
}

/// <summary>
/// Serializes an instance of <typeparamref name="T"/> using
/// <see cref="JsonSourceGenerationOptionsAttribute"/> values specified at design time.
Expand Down
Loading

0 comments on commit 02f0b21

Please sign in to comment.