Skip to content

Commit

Permalink
fix(aarch64): bug when unmarshal into non-nil interface (#759)
Browse files Browse the repository at this point in the history
  • Loading branch information
liuq19 authored Feb 27, 2025
1 parent 86fae91 commit a6f8114
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 20 deletions.
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() {

}


0 comments on commit a6f8114

Please sign in to comment.