From a6f8114f67616d6a66f52813839452d956d7a250 Mon Sep 17 00:00:00 2001 From: liu Date: Thu, 27 Feb 2025 20:30:00 +0800 Subject: [PATCH] fix(aarch64): bug when unmarshal into non-nil interface (#759) --- internal/decoder/optdec/interface.go | 21 ++-- issue_test/common_test.go | 31 +++--- issue_test/issue750_test.go | 7 +- issue_test/issue758_test.go | 144 +++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 20 deletions(-) create mode 100644 issue_test/issue758_test.go diff --git a/internal/decoder/optdec/interface.go b/internal/decoder/optdec/interface.go index 0c063d55f..64268e53d 100644 --- a/internal/decoder/optdec/interface.go +++ b/internal/decoder/optdec/interface.go @@ -19,14 +19,19 @@ func (d *efaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error } eface := *(*rt.GoEface)(vp) - - // not pointer type, or nil pointer, or *interface{} - if eface.Value == nil || eface.Type.Kind() != reflect.Ptr || rt.PtrElem(eface.Type) == anyType { + /* + not pointer type, or nil pointer, or self-pointed interface{}, such as + ```go + var v interface{} + v = &v + return v + ``` see `issue758_test.go`. + */ + if eface.Value == nil || eface.Type.Kind() != reflect.Ptr || eface.Value == vp { ret, err := node.AsEface(ctx) if err != nil { return err } - *(*interface{})(vp) = ret return nil } @@ -54,6 +59,7 @@ type ifaceDecoder struct { } func (d *ifaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error { + if node.IsNull() { *(*unsafe.Pointer)(vp) = nil return nil @@ -65,9 +71,12 @@ func (d *ifaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error } vt := iface.Itab.Vt + if vt.Kind() != reflect.Ptr { + return error_type(d.typ) + } - // not pointer type, or nil pointer, or *interface{} - if vp == nil || vt.Kind() != reflect.Ptr || rt.PtrElem(vt) == anyType { + // nil pointer + if vp == nil { ret, err := node.AsEface(ctx) if err != nil { return err diff --git a/issue_test/common_test.go b/issue_test/common_test.go index 282478763..d1cfa8b9b 100644 --- a/issue_test/common_test.go +++ b/issue_test/common_test.go @@ -16,23 +16,30 @@ package issue_test import ( "testing" - "encoding/json" + "github.com/bytedance/sonic" + "github.com/davecgh/go-spew/spew" "github.com/stretchr/testify/assert" ) -func assertUnmarshal(t *testing.T, api sonic.API, input []byte, newfn func() interface{}) { - sv, jv := newfn(), newfn() - serr := api.Unmarshal(input, sv) - jerr := json.Unmarshal(input, jv) - assert.Equal(t, jv, sv) - assert.Equal(t, serr == nil, jerr == nil) +type unmTestCase struct { + name string + data []byte + newfn func() interface{} +} + +func assertUnmarshal(t *testing.T, api sonic.API, cas unmTestCase) { + sv, jv := cas.newfn(), cas.newfn() + serr := api.Unmarshal(cas.data, sv) + jerr := json.Unmarshal(cas.data, jv) + assert.Equal(t, jv, sv, spew.Sdump(jv, sv)) + assert.Equal(t, serr == nil, jerr == nil, spew.Sdump(jerr, serr)) } -func assertMarshal(t *testing.T, api sonic.API, v interface{}) { - sout, serr := api.Marshal(&v) - jout, jerr := json.Marshal(&v) - assert.Equal(t, jerr == nil, serr == nil, jerr) - assert.Equal(t, jout, sout, v) +func assertMarshal(t *testing.T, api sonic.API, obj interface{}) { + sout, serr := api.Marshal(&obj) + jout, jerr := json.Marshal(&obj) + assert.Equal(t, jerr == nil, serr == nil, spew.Sdump(jerr, serr)) + assert.Equal(t, jout, sout, spew.Sdump(jout, sout)) } diff --git a/issue_test/issue750_test.go b/issue_test/issue750_test.go index f8aef23f0..9604497ab 100644 --- a/issue_test/issue750_test.go +++ b/issue_test/issue750_test.go @@ -26,6 +26,9 @@ func genSlice() interface{} { } func TestSlicePointer_Issue750(t *testing.T) { - assertUnmarshal(t, sonic.ConfigStd, []byte(`[["one","2"]]`), genSlice) - + assertUnmarshal(t, sonic.ConfigStd, unmTestCase{ + name: "non-empty eface slice", + newfn: genSlice, + data: []byte(`["one","2"]`), + }) } \ No newline at end of file diff --git a/issue_test/issue758_test.go b/issue_test/issue758_test.go new file mode 100644 index 000000000..8d129e081 --- /dev/null +++ b/issue_test/issue758_test.go @@ -0,0 +1,144 @@ +package issue_test + +import ( + "testing" + + "github.com/bytedance/sonic" + "encoding/json" +) + +var _ = json.Marshal + +var _ = sonic.Marshal + +func TestIssue758_UnmarshalIntoAnyPointer(t *testing.T) { + for _, cas := range []unmTestCase { + { + name: "non-nil typed pointer", + data: []byte(`["one","2"]`), + newfn: func() interface{} { + a := []string{} + var aPtr interface{} = &a + b := interface{}(&aPtr) + return &b + }, + }, + { + name: "nil typed pointer", + data: []byte(`["one","2"]`), + newfn: func() interface{} { + var aPtr interface{} = (*[]string)(nil) + b := interface{}(&aPtr) + return &b + }, + }, + { + name: "non-nil eface pointer recursive1", + data: []byte(`{"a": "b"}`), + newfn: func() interface{} { + var v interface{} + v = &v + return v + }, + }, + // TODO: the case is also failed for encoding/json + // { + // name: "non-nil eface pointer recursive2", + // data: []byte(`{"a": "b"}`), + // newfn: func() interface{} { + // var v interface{} + // var v1 = &v + // v = &v1 + // return v + // }, + // }, + { + name: "non-nil eface pointer", + data: []byte(`{"a": "b"}`), + newfn: func() interface{} { + var v1 interface{} = & struct { + A string `json:"a"` + B string `json:"b"` + } { + A: "c", + B: "d", + } + var v = (*interface{})(&v1) + return v + }, + }, + { + name: "nil eface pointer", + data: []byte(`{"a": "b"}`), + newfn: func() interface{} { + var v interface{} + v = (*interface{})(nil) + return v + }, + }, + { + name: "non-nil iface pointer", + data: []byte(`{"id": "2"}`), + newfn: func() interface{} { + var a MockEface = &fooEface{} + var aPtr interface{} = &a + b := interface{}(&aPtr) + return &b + }, + }, + { + name: "root nil iface pointer shoule be error", + data: []byte(`{"id": "2"}`), + newfn: func() interface{} { + var aPtr interface{} = (*MockEface)(nil) + return aPtr + }, + }, + { + name: "nil iface pointer to be eface", + data: []byte(`{"id": "2"}`), + newfn: func() interface{} { + var aPtr interface{} = (*MockEface)(nil) + var a interface{} = &aPtr + return a + }, + }, + { + name: "iface type", + data: []byte(`{"id": "2"}`), + newfn: func() interface{} { + var a MockEface = fooEface3{} + var aPtr interface{} = &a + b := interface{}(&aPtr) + return &b + }, + }, + } { + t.Run(cas.name, func(t *testing.T) { + assertUnmarshal(t, sonic.ConfigDefault, cas) + }) + } +} + + +type MockEface interface { + MyMock() +} + +type fooEface struct { + Id *string `json:"id"` +} + +func (self *fooEface) MyMock() { + +} + +type fooEface3 struct { + Id *string `json:"id"` +} + +func (self fooEface3) MyMock() { + +} + +