Skip to content

Commit

Permalink
Ensure reflection property metadata is instantiated lazily (#76828) (#…
Browse files Browse the repository at this point in the history
…76869)

* Ensure reflection property metadata is instantiated lazily.

* Address feedback
  • Loading branch information
eiriktsarpalis authored Oct 11, 2022
1 parent 51758ee commit d25158d
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,6 @@ private void PopulatePropertyList()
{
if (!_isConfigured)
{
// Ensure SourceGen had a chance to add properties
LateAddProperties();
_properties = new(this);
return;
Expand Down Expand Up @@ -882,12 +881,7 @@ internal void InitializePropertyCache()
}
else
{
// Resolver didn't modify properties

// Source gen currently when initializes properties
// also assigns JsonPropertyInfo's JsonTypeInfo which causes SO if there are any
// cycles in the object graph. For that reason properties cannot be added immediately.
// This is a no-op for ReflectionJsonTypeInfo
// Resolver didn't modify any properties, create the property cache from scratch.
LateAddProperties();
PropertyCache ??= CreatePropertyCache(capacity: 0);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o
PopulatePolymorphismMetadata();
MapInterfaceTypesToCallbacks();

if (PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object)
{
AddPropertiesAndParametersUsingReflection();
}

Func<object>? createObject = Options.MemberAccessorStrategy.CreateConstructor(typeof(T));
SetCreateObjectIfCompatible(createObject);
CreateObjectForExtensionDataProperty = createObject;
Expand All @@ -37,11 +32,17 @@ internal ReflectionJsonTypeInfo(JsonConverter converter, JsonSerializerOptions o
converter.ConfigureJsonTypeInfoUsingReflection(this, options);
}

[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
private void AddPropertiesAndParametersUsingReflection()
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:RequiresUnreferencedCode",
Justification = "The ctor is marked RequiresUnreferencedCode.")]
internal override void LateAddProperties()
{
Debug.Assert(PropertyInfoForTypeInfo.ConverterStrategy == ConverterStrategy.Object);
Debug.Assert(!IsConfigured);
Debug.Assert(PropertyCache is null);

if (Kind != JsonTypeInfoKind.Object)
{
return;
}

const BindingFlags BindingFlags =
BindingFlags.Instance |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1700,5 +1700,69 @@ public string? Value
set => _value = value ?? "NULL";
}
}

[Fact]
public static void TypeWithIgnoredUnsupportedType_ShouldBeSupported()
{
// Regression test for https://github.com/dotnet/runtime/issues/76807

// Sanity check -- metadata resolution for the unsupported type is failing
Assert.Throws<InvalidOperationException>(() => JsonSerializerOptions.Default.GetTypeInfo(typeof(UnsupportedType)));

// Serialization works as expected
string json = JsonSerializer.Serialize(new PocoWithIgnoredUnsupportedType());
JsonTestHelper.AssertJsonEqual("{}", json);

// Metadata is reported as expected
JsonTypeInfo jti = JsonSerializerOptions.Default.GetTypeInfo(typeof(PocoWithIgnoredUnsupportedType));
Assert.Equal(1, jti.Properties.Count);
JsonPropertyInfo propertyInfo = jti.Properties[0];
Assert.Null(propertyInfo.Get);
Assert.Null(propertyInfo.Set);
}

public class PocoWithIgnoredUnsupportedType
{
[JsonIgnore]
public UnsupportedType UnsuportedProperty { get; set; }
}

[Fact]
public static void TypeWithUnIgnoredUnsupportedType_CanModifyUnsupportedType()
{
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
static jti =>
{
if (jti.Type == typeof(PocoWithUnIgnoredUnsupportedType))
{
Assert.Equal(1, jti.Properties.Count);
JsonPropertyInfo propertyInfo = jti.Properties[0];
Assert.Equal(typeof(UnsupportedType), propertyInfo.PropertyType);

jti.Properties.Clear();
}
}
}
}
};

string json = JsonSerializer.Serialize(new PocoWithUnIgnoredUnsupportedType(), options);
JsonTestHelper.AssertJsonEqual("{}", json);
}

public class PocoWithUnIgnoredUnsupportedType
{
public UnsupportedType UnsuportedProperty { get; set; }
}

public class UnsupportedType
{
public ReadOnlySpan<byte> Span => Array.Empty<byte>();
}
}
}

0 comments on commit d25158d

Please sign in to comment.