From 902856929da897badedf6501f3362eef58e7df2a Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Mon, 5 Jul 2021 20:30:35 +0900 Subject: [PATCH] Fix indirect layout --- encode_test.go | 29 ++++++++++ internal/encoder/compiler.go | 12 +++-- test/cover/cover_int_test.go | 100 +++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 3 deletions(-) diff --git a/encode_test.go b/encode_test.go index 6167f20e..5b271b02 100644 --- a/encode_test.go +++ b/encode_test.go @@ -2004,3 +2004,32 @@ func TestInterfaceWithPointer(t *testing.T) { } assertEq(t, "interface{}", string(expected), string(actual)) } + +func TestIssue263(t *testing.T) { + type Foo struct { + A []string `json:"a"` + B int `json:"b"` + } + + type MyStruct struct { + Foo *Foo `json:"foo,omitempty"` + } + + s := MyStruct{ + Foo: &Foo{ + A: []string{"ls -lah"}, + B: 0, + }, + } + expected, err := stdjson.Marshal(s) + if err != nil { + t.Fatal(err) + } + actual, err := json.Marshal(s) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(expected, actual) { + t.Fatalf("expected:[%s] but got:[%s]", string(expected), string(actual)) + } +} diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index f930efa8..01a5d4b2 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -1440,8 +1440,10 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) { // if parent is indirect type, set child indirect property to true valueCode.Flags |= IndirectFlags } else { - // if parent is not indirect type and child have only one field, set child indirect property to false - if i == 0 && valueCode.NextField != nil && valueCode.NextField.Op == OpStructEnd { + // if parent is not indirect type, set child indirect property to false. + // but if parent's indirect is false and isPtr is true, then indirect must be true. + // Do this only if indirectConversion is enabled at the end of compileStruct. + if i == 0 { valueCode.Flags &= ^IndirectFlags } } @@ -1544,7 +1546,11 @@ func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) { delete(ctx.structTypeToCompiledCode, typeptr) if !disableIndirectConversion && (head.Flags&IndirectFlags == 0) && isPtr { - head.Flags |= IndirectFlags + headCode := head + for strings.Contains(headCode.Op.String(), "Head") { + headCode.Flags |= IndirectFlags + headCode = headCode.Next + } } return ret, nil diff --git a/test/cover/cover_int_test.go b/test/cover/cover_int_test.go index e76abb9d..b21a528b 100644 --- a/test/cover/cover_int_test.go +++ b/test/cover/cover_int_test.go @@ -631,6 +631,56 @@ func TestCoverInt(t *testing.T) { }{A: -1}}, }, + // HeadIntNotRootMultiFields + { + name: "HeadIntNotRootMultiFields", + data: struct { + A struct { + A int `json:"a"` + B int `json:"b"` + } + }{A: struct { + A int `json:"a"` + B int `json:"b"` + }{A: -1, B: 1}}, + }, + { + name: "HeadIntNotRootOmitEmptyMultiFields", + data: struct { + A struct { + A int `json:"a,omitempty"` + B int `json:"b,omitempty"` + } + }{A: struct { + A int `json:"a,omitempty"` + B int `json:"b,omitempty"` + }{A: -1, B: 1}}, + }, + { + name: "HeadIntNotRootStringMultiFields", + data: struct { + A struct { + A int `json:"a,string"` + B int `json:"b,string"` + } + }{A: struct { + A int `json:"a,string"` + B int `json:"b,string"` + }{A: -1, B: 1}}, + }, + { + name: "HeadIntNotRootStringOmitEmptyMultiFields", + data: struct { + A struct { + A int `json:"a,string,omitempty"` + B int `json:"b,string,omitempty"` + } + }{A: struct { + A int `json:"a,string,omitempty"` + B int `json:"b,string,omitempty"` + }{A: -1, B: 1}}, + }, + // HeadIntPtrNotRoot { name: "HeadIntPtrNotRoot", @@ -791,6 +841,56 @@ func TestCoverInt(t *testing.T) { }{A: -1})}, }, + // PtrHeadIntNotRootMultiFields + { + name: "PtrHeadIntNotRootMultiFields", + data: struct { + A *struct { + A int `json:"a"` + B int `json:"b"` + } + }{A: &(struct { + A int `json:"a"` + B int `json:"b"` + }{A: -1, B: 1})}, + }, + { + name: "PtrHeadIntNotRootOmitEmptyMultiFields", + data: struct { + A *struct { + A int `json:"a,omitempty"` + B int `json:"b,omitempty"` + } + }{A: &(struct { + A int `json:"a,omitempty"` + B int `json:"b,omitempty"` + }{A: -1, B: 1})}, + }, + { + name: "PtrHeadIntNotRootStringMultiFields", + data: struct { + A *struct { + A int `json:"a,string"` + B int `json:"b,string"` + } + }{A: &(struct { + A int `json:"a,string"` + B int `json:"b,string"` + }{A: -1, B: 1})}, + }, + { + name: "PtrHeadIntNotRootStringOmitEmptyMultiFields", + data: struct { + A *struct { + A int `json:"a,string,omitempty"` + B int `json:"b,string,omitempty"` + } + }{A: &(struct { + A int `json:"a,string,omitempty"` + B int `json:"b,string,omitempty"` + }{A: -1, B: 1})}, + }, + // PtrHeadIntPtrNotRoot { name: "PtrHeadIntPtrNotRoot",