Skip to content

StandardResolverAllowPrivate overwrites readonly fields set in constructor #1218

@Alxandr

Description

@Alxandr

Bug description

Readonly fields overwritten after constructor when using allow private.

Repro steps

[MessagePackObject]
public class Test
{
    [Key(0)]
    readonly string _test;

    [SerializationConstructor]
    public Test(string test)
    {
        _test = test ?? "I am never null :D";
    }

    [IgnoreMember]
    public string Prop => _test;
}

[Fact]
public void AllowPrivateTest()
{
    // write an empty array to seq
    using var seq = new Sequence<byte>();
    var writer = new MessagePackWriter(seq);
    writer.WriteArrayHeader(0);
    writer.Flush();

    // deserialize as Test
    var data = seq.AsReadOnlySequence;
    var resolver = StandardResolverAllowPrivate.Instance;
    var options = MessagePackSerializerOptions.Standard.WithResolver(StandardResolverAllowPrivate.Instance);
    var test = MessagePackSerializer.Deserialize<Test>(in data, options);

    // check test
    Assert.NotNull(test.Prop);
}

The generated code here produces a call to the constructor (which assigns _test), then an assignment to the field _test.

Expected behavior

Only the constructor should be called, the field should not be overwritten after.

Actual behavior

The field is overwritten after the constructor has returned.

  • Version used: v2.2.85
  • Runtime: (e.g. .NET Framework, .NET Core, Unity, mono, etc.) .NET Core 3.1

Additional context

I believe the issue stems from the following:

member = new EmittableMember
{
    FieldInfo = field,
    IsReadable = allowPrivate || field.IsPublic,
    IsWritable = allowPrivate || (field.IsPublic && !field.IsInitOnly),
    StringKey = firstMemberByName ? item.Name : $"{item.DeclaringType.FullName}.{item.Name}",
};

In here the test for IsWritable will always be true if allowPrivate is true. Rather, it should probably be (allowPrivate || field.IsPublic) && !field.IsInitOnly.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions