Fix decoding of slice element

This commit is contained in:
Masaaki Goshima 2021-05-07 21:31:35 +09:00
parent 46bdba4a65
commit 78e2bf2502
2 changed files with 78 additions and 19 deletions

View File

@ -7,14 +7,13 @@ import (
) )
type sliceDecoder struct { type sliceDecoder struct {
elemType *rtype elemType *rtype
isElemPointerType bool isElemPointerType bool
isElemUnmarshalJSONType bool valueDecoder decoder
valueDecoder decoder size uintptr
size uintptr arrayPool sync.Pool
arrayPool sync.Pool structName string
structName string fieldName string
fieldName string
} }
// If use reflect.SliceHeader, data type is uintptr. // If use reflect.SliceHeader, data type is uintptr.
@ -32,11 +31,10 @@ const (
func newSliceDecoder(dec decoder, elemType *rtype, size uintptr, structName, fieldName string) *sliceDecoder { func newSliceDecoder(dec decoder, elemType *rtype, size uintptr, structName, fieldName string) *sliceDecoder {
return &sliceDecoder{ return &sliceDecoder{
valueDecoder: dec, valueDecoder: dec,
elemType: elemType, elemType: elemType,
isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map, isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map,
isElemUnmarshalJSONType: rtype_ptrTo(elemType).Implements(unmarshalJSONType), size: size,
size: size,
arrayPool: sync.Pool{ arrayPool: sync.Pool{
New: func() interface{} { New: func() interface{} {
return &sliceHeader{ return &sliceHeader{
@ -123,11 +121,11 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er
copySlice(d.elemType, dst, src) copySlice(d.elemType, dst, src)
} }
ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size) ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
if d.isElemUnmarshalJSONType { if d.isElemPointerType {
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
} else {
// assign new element to the slice // assign new element to the slice
typedmemmove(d.elemType, ep, unsafe_New(d.elemType)) typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
} else if d.isElemPointerType {
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
} }
if err := d.valueDecoder.decodeStream(s, depth, ep); err != nil { if err := d.valueDecoder.decodeStream(s, depth, ep); err != nil {
return err return err
@ -236,11 +234,11 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
copySlice(d.elemType, dst, src) copySlice(d.elemType, dst, src)
} }
ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size) ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size)
if d.isElemUnmarshalJSONType { if d.isElemPointerType {
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
} else {
// assign new element to the slice // assign new element to the slice
typedmemmove(d.elemType, ep, unsafe_New(d.elemType)) typedmemmove(d.elemType, ep, unsafe_New(d.elemType))
} else if d.isElemPointerType {
**(**unsafe.Pointer)(unsafe.Pointer(&ep)) = nil // initialize elem pointer
} }
c, err := d.valueDecoder.decode(buf, cursor, depth, ep) c, err := d.valueDecoder.decode(buf, cursor, depth, ep)
if err != nil { if err != nil {

View File

@ -3508,3 +3508,64 @@ func TestDecodeBackSlash(t *testing.T) {
}) })
}) })
} }
func TestIssue218(t *testing.T) {
type A struct {
X int
}
type B struct {
Y int
}
type S struct {
A *A `json:"a,omitempty"`
B *B `json:"b,omitempty"`
}
tests := []struct {
name string
given []S
expected []S
}{
{
name: "A should be correct",
given: []S{{
A: &A{
X: 1,
},
}},
expected: []S{{
A: &A{
X: 1,
},
}},
},
{
name: "B should be correct",
given: []S{{
B: &B{
Y: 2,
},
}},
expected: []S{{
B: &B{
Y: 2,
},
}},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(test.given); err != nil {
t.Fatal(err)
}
var actual []S
if err := json.NewDecoder(bytes.NewReader(buf.Bytes())).Decode(&actual); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(test.expected, actual) {
t.Fatalf("mismatch value: expected %v but got %v", test.expected, actual)
}
})
}
}