diff --git a/decode_slice.go b/decode_slice.go index 0442a75..ea09f88 100644 --- a/decode_slice.go +++ b/decode_slice.go @@ -7,12 +7,13 @@ import ( ) type sliceDecoder struct { - elemType *rtype - valueDecoder decoder - size uintptr - arrayPool sync.Pool - structName string - fieldName string + elemType *rtype + isElemPointerType bool + valueDecoder decoder + size uintptr + arrayPool sync.Pool + structName string + fieldName string } // If use reflect.SliceHeader, data type is uintptr. @@ -30,9 +31,10 @@ const ( func newSliceDecoder(dec decoder, elemType *rtype, size uintptr, structName, fieldName string) *sliceDecoder { return &sliceDecoder{ - valueDecoder: dec, - elemType: elemType, - size: size, + valueDecoder: dec, + elemType: elemType, + isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map, + size: size, arrayPool: sync.Pool{ New: func() interface{} { return &sliceHeader{ @@ -114,7 +116,11 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er dst := sliceHeader{data: data, len: idx, cap: capacity} copySlice(d.elemType, dst, src) } - if err := d.valueDecoder.decodeStream(s, depth, unsafe.Pointer(uintptr(data)+uintptr(idx)*d.size)); err != nil { + ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size) + if d.isElemPointerType { + *(*unsafe.Pointer)(ep) = nil // initialize elem pointer + } + if err := d.valueDecoder.decodeStream(s, depth, ep); err != nil { return err } s.skipWhiteSpace() @@ -224,7 +230,11 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) dst := sliceHeader{data: data, len: idx, cap: capacity} copySlice(d.elemType, dst, src) } - c, err := d.valueDecoder.decode(buf, cursor, depth, unsafe.Pointer(uintptr(data)+uintptr(idx)*d.size)) + ep := unsafe.Pointer(uintptr(data) + uintptr(idx)*d.size) + if d.isElemPointerType { + *(*unsafe.Pointer)(ep) = nil // initialize elem pointer + } + c, err := d.valueDecoder.decode(buf, cursor, depth, ep) if err != nil { return 0, err } diff --git a/decode_test.go b/decode_test.go index d76cf5c..6595f4b 100644 --- a/decode_test.go +++ b/decode_test.go @@ -14,6 +14,7 @@ import ( "strings" "testing" "time" + "unsafe" "github.com/goccy/go-json" ) @@ -2903,3 +2904,28 @@ func TestUnmarshalMaxDepth(t *testing.T) { } } } + +func TestDecodeSlice(t *testing.T) { + type B struct{ Int int32 } + type A struct{ B *B } + type X struct{ A []*A } + + w1 := &X{} + w2 := &X{} + + if err := json.Unmarshal([]byte(`{"a": [ {"b":{"int": 42} } ] }`), w1); err != nil { + t.Fatal(err) + } + w1addr := uintptr(unsafe.Pointer(w1.A[0].B)) + + if err := json.Unmarshal([]byte(`{"a": [ {"b":{"int": 112} } ] }`), w2); err != nil { + t.Fatal(err) + } + if uintptr(unsafe.Pointer(w1.A[0].B)) != w1addr { + t.Fatal("wrong addr") + } + w2addr := uintptr(unsafe.Pointer(w2.A[0].B)) + if w1addr == w2addr { + t.Fatal("invaid address") + } +}