Skip to content

Commit

Permalink
[release/6.0] Handle parameterless ctors in structs in STJ's Reflecti…
Browse files Browse the repository at this point in the history
…onEmitMemberAccessor (#67901)

* [release/6.0] backport #62989 "Handle parameterless ctors in structs in STJ's ReflectionEmitMemberAccessor"

* Add explicit parameterless constructors in the tests

* Update ConstructorTests.ParameterMatching.cs

* address feedback

Co-authored-by: Eirik Tsarpalis <[email protected]>
  • Loading branch information
EgorBo and eiriktsarpalis authored May 4, 2022
1 parent cc3b551 commit dbd2a6b
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/libraries/System.Text.Json/src/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
<Nullable>enable</Nullable>
<IncludeInternalObsoleteAttribute>true</IncludeInternalObsoleteAttribute>
<IsPackable>true</IsPackable>
<ServicingVersion>4</ServicingVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<ServicingVersion>5</ServicingVersion>
<PackageDescription>Provides high-performance and low-allocating types that serialize objects to JavaScript Object Notation (JSON) text and deserialize JSON text to objects, with UTF-8 support built-in. Also provides types to read and write JSON text encoded as UTF-8, and to create an in-memory document object model (DOM), that is read-only, for random access of the JSON elements within a structured view of the data.

Commonly Used Types:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ internal sealed class ReflectionEmitMemberAccessor : MemberAccessor
else
{
generator.Emit(OpCodes.Newobj, realMethod);
if (type.IsValueType)
{
// Since C# 10 it's now possible to have parameterless constructors in structs
generator.Emit(OpCodes.Box, type);
}
}

generator.Emit(OpCodes.Ret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,71 @@ public class ClassWithIgnoredSameType
public ClassWithIgnoredSameType(ClassWithIgnoredSameType prop) { }
}

[Fact]
public void StructWithPropertyInit_DeseralizeEmptyObject()
{
string json = @"{}";
var obj = JsonSerializer.Deserialize<StructWithPropertyInit>(json);
Assert.Equal(42, obj.A);
Assert.Equal(0, obj.B);
}

[Fact]
public void StructWithPropertyInit_OverrideInitedProperty()
{
string json = @"{""A"":43}";
var obj = JsonSerializer.Deserialize<StructWithPropertyInit>(json);
Assert.Equal(43, obj.A);
Assert.Equal(0, obj.B);

json = @"{""A"":0,""B"":44}";
obj = JsonSerializer.Deserialize<StructWithPropertyInit>(json);
Assert.Equal(0, obj.A);
Assert.Equal(44, obj.B);

json = @"{""B"":45}";
obj = JsonSerializer.Deserialize<StructWithPropertyInit>(json);
Assert.Equal(42, obj.A); // JSON doesn't set A property so it's expected to be 42
Assert.Equal(45, obj.B);
}

public struct StructWithPropertyInit
{
public StructWithPropertyInit() {}
public long A { get; set; } = 42;
public long B { get; set; } = 0;
}

[Fact]
public void StructWithFieldInit_DeseralizeEmptyObject()
{
string json = @"{}";
var obj = JsonSerializer.Deserialize<StructWithFieldInit>(json);
Assert.Equal(0, obj.A);
Assert.Equal(42, obj.B);
}

public struct StructWithFieldInit
{
public StructWithFieldInit() {}
public long A = 0;
public long B = 42;
}

[Fact]
public void StructWithExplicitParameterlessCtor_DeseralizeEmptyObject()
{
string json = @"{}";
var obj = JsonSerializer.Deserialize<StructWithExplicitParameterlessCtor>(json);
Assert.Equal(42, obj.A);
}

public struct StructWithExplicitParameterlessCtor
{
public long A;
public StructWithExplicitParameterlessCtor() => A = 42;
}

public async Task TestClassWithDefaultCtorParams()
{
ClassWithDefaultCtorParams obj = new ClassWithDefaultCtorParams(
Expand Down

0 comments on commit dbd2a6b

Please sign in to comment.