From 78e2bf25020a34b61b20685ad191dc0f5468983b Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Fri, 7 May 2021 21:31:35 +0900 Subject: [PATCH] Fix decoding of slice element --- decode_slice.go | 36 ++++++++++++++--------------- decode_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/decode_slice.go b/decode_slice.go index b00a2bb..df79aab 100644 --- a/decode_slice.go +++ b/decode_slice.go @@ -7,14 +7,13 @@ import ( ) type sliceDecoder struct { - elemType *rtype - isElemPointerType bool - isElemUnmarshalJSONType bool - 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. @@ -32,11 +31,10 @@ const ( func newSliceDecoder(dec decoder, elemType *rtype, size uintptr, structName, fieldName string) *sliceDecoder { return &sliceDecoder{ - valueDecoder: dec, - elemType: elemType, - isElemPointerType: elemType.Kind() == reflect.Ptr || elemType.Kind() == reflect.Map, - isElemUnmarshalJSONType: rtype_ptrTo(elemType).Implements(unmarshalJSONType), - 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{ @@ -123,11 +121,11 @@ func (d *sliceDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) er copySlice(d.elemType, dst, src) } 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 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 { return err @@ -236,11 +234,11 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) copySlice(d.elemType, dst, src) } 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 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) if err != nil { diff --git a/decode_test.go b/decode_test.go index 002a8eb..bcdff45 100644 --- a/decode_test.go +++ b/decode_test.go @@ -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) + } + }) + } +}