Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(aarch64): bug when unmarshal into non-nil interface #759

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions internal/decoder/optdec/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
31 changes: 19 additions & 12 deletions issue_test/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
7 changes: 5 additions & 2 deletions issue_test/issue750_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"]`),
})
}
144 changes: 144 additions & 0 deletions issue_test/issue758_test.go
Original file line number Diff line number Diff line change
@@ -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() {

}


Loading