Fix decoding of embedded unexported pointer field

This commit is contained in:
Masaaki Goshima 2021-02-18 17:46:28 +09:00
parent cf6cf56e3d
commit 6eb23deb6f
3 changed files with 25 additions and 8 deletions

View File

@ -1,8 +1,10 @@
package json package json
import ( import (
"fmt"
"reflect" "reflect"
"strings" "strings"
"unicode"
"unsafe" "unsafe"
) )
@ -247,7 +249,7 @@ func decodeCompileInterface(typ *rtype, structName, fieldName string) (decoder,
return newInterfaceDecoder(typ, structName, fieldName), nil return newInterfaceDecoder(typ, structName, fieldName), nil
} }
func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, baseOffset uintptr) { func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) {
for k, v := range dec.fieldMap { for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists { if _, exists := conflictedMap[k]; exists {
// already conflicted key // already conflicted key
@ -257,7 +259,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM
if !exists { if !exists {
fieldSet := &structFieldSet{ fieldSet := &structFieldSet{
dec: v.dec, dec: v.dec,
offset: baseOffset + v.offset, offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey, isTaggedKey: v.isTaggedKey,
key: k, key: k,
keyLen: int64(len(k)), keyLen: int64(len(k)),
@ -281,7 +283,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM
if v.isTaggedKey { if v.isTaggedKey {
fieldSet := &structFieldSet{ fieldSet := &structFieldSet{
dec: v.dec, dec: v.dec,
offset: baseOffset + v.offset, offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey, isTaggedKey: v.isTaggedKey,
key: k, key: k,
keyLen: int64(len(k)), keyLen: int64(len(k)),
@ -318,6 +320,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
if isIgnoredStructField(field) { if isIgnoredStructField(field) {
continue continue
} }
isUnexportedField := unicode.IsLower([]rune(field.Name)[0])
tag := structTagFromField(field) tag := structTagFromField(field)
dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder) dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder)
if err != nil { if err != nil {
@ -329,13 +332,20 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
// recursive definition // recursive definition
continue continue
} }
decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field.Offset) decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field)
} else if pdec, ok := dec.(*ptrDecoder); ok { } else if pdec, ok := dec.(*ptrDecoder); ok {
contentDec := pdec.contentDecoder() contentDec := pdec.contentDecoder()
if pdec.typ == typ { if pdec.typ == typ {
// recursive definition // recursive definition
continue continue
} }
var fieldSetErr error
if isUnexportedField {
fieldSetErr = fmt.Errorf(
"json: cannot set embedded pointer to unexported struct: %v",
field.Type.Elem(),
)
}
if dec, ok := contentDec.(*structDecoder); ok { if dec, ok := contentDec.(*structDecoder); ok {
for k, v := range dec.fieldMap { for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists { if _, exists := conflictedMap[k]; exists {
@ -350,6 +360,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
isTaggedKey: v.isTaggedKey, isTaggedKey: v.isTaggedKey,
key: k, key: k,
keyLen: int64(len(k)), keyLen: int64(len(k)),
err: fieldSetErr,
} }
fieldMap[k] = fieldSet fieldMap[k] = fieldSet
lower := strings.ToLower(k) lower := strings.ToLower(k)
@ -374,6 +385,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
isTaggedKey: v.isTaggedKey, isTaggedKey: v.isTaggedKey,
key: k, key: k,
keyLen: int64(len(k)), keyLen: int64(len(k)),
err: fieldSetErr,
} }
fieldMap[k] = fieldSet fieldMap[k] = fieldSet
lower := strings.ToLower(k) lower := strings.ToLower(k)

View File

@ -15,6 +15,7 @@ type structFieldSet struct {
isTaggedKey bool isTaggedKey bool
key string key string
keyLen int64 keyLen int64
err error
} }
type structDecoder struct { type structDecoder struct {
@ -524,6 +525,9 @@ func (d *structDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
} }
} }
if field != nil { if field != nil {
if field.err != nil {
return field.err
}
if err := field.dec.decodeStream(s, unsafe.Pointer(uintptr(p)+field.offset)); err != nil { if err := field.dec.decodeStream(s, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
return err return err
} }
@ -591,6 +595,9 @@ func (d *structDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int6
return 0, errExpected("object value after colon", cursor) return 0, errExpected("object value after colon", cursor)
} }
if field != nil { if field != nil {
if field.err != nil {
return 0, field.err
}
c, err := field.dec.decode(buf, cursor, unsafe.Pointer(uintptr(p)+field.offset)) c, err := field.dec.decode(buf, cursor, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil { if err != nil {
return 0, err return 0, err

View File

@ -2539,7 +2539,6 @@ func TestInvalidStringOption(t *testing.T) {
} }
*/ */
/*
// Test unmarshal behavior with regards to embedded unexported structs. // Test unmarshal behavior with regards to embedded unexported structs.
// //
// (Issue 21357) If the embedded struct is a pointer and is unallocated, // (Issue 21357) If the embedded struct is a pointer and is unallocated,
@ -2604,7 +2603,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
in: `{"R":2,"Q":1}`, in: `{"R":2,"Q":1}`,
ptr: new(S1), ptr: new(S1),
out: &S1{R: 2}, out: &S1{R: 2},
err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"), err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json_test.embed1"),
}, { }, {
// The top level Q field takes precedence. // The top level Q field takes precedence.
in: `{"Q":1}`, in: `{"Q":1}`,
@ -2626,7 +2625,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
in: `{"R":2,"Q":1}`, in: `{"R":2,"Q":1}`,
ptr: new(S5), ptr: new(S5),
out: &S5{R: 2}, out: &S5{R: 2},
err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"), err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json_test.embed3"),
}, { }, {
// Issue 24152, ensure decodeState.indirect does not panic. // Issue 24152, ensure decodeState.indirect does not panic.
in: `{"embed1": {"Q": 1}}`, in: `{"embed1": {"Q": 1}}`,
@ -2670,7 +2669,6 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
} }
} }
} }
*/
/* /*
func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) { func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {