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
import (
"fmt"
"reflect"
"strings"
"unicode"
"unsafe"
)
@ -247,7 +249,7 @@ func decodeCompileInterface(typ *rtype, structName, fieldName string) (decoder,
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 {
if _, exists := conflictedMap[k]; exists {
// already conflicted key
@ -257,7 +259,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM
if !exists {
fieldSet := &structFieldSet{
dec: v.dec,
offset: baseOffset + v.offset,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
@ -281,7 +283,7 @@ func decodeRemoveConflictFields(fieldMap map[string]*structFieldSet, conflictedM
if v.isTaggedKey {
fieldSet := &structFieldSet{
dec: v.dec,
offset: baseOffset + v.offset,
offset: field.Offset + v.offset,
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
@ -318,6 +320,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
if isIgnoredStructField(field) {
continue
}
isUnexportedField := unicode.IsLower([]rune(field.Name)[0])
tag := structTagFromField(field)
dec, err := decodeCompile(type2rtype(field.Type), structName, field.Name, structTypeToDecoder)
if err != nil {
@ -329,13 +332,20 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
// recursive definition
continue
}
decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field.Offset)
decodeRemoveConflictFields(fieldMap, conflictedMap, stDec, field)
} else if pdec, ok := dec.(*ptrDecoder); ok {
contentDec := pdec.contentDecoder()
if pdec.typ == typ {
// recursive definition
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 {
for k, v := range dec.fieldMap {
if _, exists := conflictedMap[k]; exists {
@ -350,6 +360,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)
@ -374,6 +385,7 @@ func decodeCompileStruct(typ *rtype, structName, fieldName string, structTypeToD
isTaggedKey: v.isTaggedKey,
key: k,
keyLen: int64(len(k)),
err: fieldSetErr,
}
fieldMap[k] = fieldSet
lower := strings.ToLower(k)

View File

@ -15,6 +15,7 @@ type structFieldSet struct {
isTaggedKey bool
key string
keyLen int64
err error
}
type structDecoder struct {
@ -524,6 +525,9 @@ func (d *structDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
}
}
if field != nil {
if field.err != nil {
return field.err
}
if err := field.dec.decodeStream(s, unsafe.Pointer(uintptr(p)+field.offset)); err != nil {
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)
}
if field != nil {
if field.err != nil {
return 0, field.err
}
c, err := field.dec.decode(buf, cursor, unsafe.Pointer(uintptr(p)+field.offset))
if err != nil {
return 0, err

View File

@ -2539,7 +2539,6 @@ func TestInvalidStringOption(t *testing.T) {
}
*/
/*
// Test unmarshal behavior with regards to embedded unexported structs.
//
// (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}`,
ptr: new(S1),
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.
in: `{"Q":1}`,
@ -2626,7 +2625,7 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
in: `{"R":2,"Q":1}`,
ptr: new(S5),
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.
in: `{"embed1": {"Q": 1}}`,
@ -2670,7 +2669,6 @@ func TestUnmarshalEmbeddedUnexported(t *testing.T) {
}
}
}
*/
/*
func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {