forked from mirror/go-json
Fix decoding of embedded unexported pointer field
This commit is contained in:
parent
cf6cf56e3d
commit
6eb23deb6f
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue