Merge pull request #40 from goccy/feature/fix-anonymous-struct

Fix anonymous struct fields
This commit is contained in:
Masaaki Goshima 2020-08-22 18:30:21 +09:00 committed by GitHub
commit 76cbb4ce68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 310 additions and 55 deletions

View File

@ -622,7 +622,6 @@ type structFieldPair struct {
}
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *structFieldCode) map[string][]structFieldPair {
//fmt.Println("type = ", typ, "valueCode = ", valueCode.dump())
anonymousFields := map[string][]structFieldPair{}
f := valueCode
var prevAnonymousField *structFieldCode

View File

@ -112,15 +112,24 @@ func (c *opcode) dump() string {
codes := []string{}
for code := c; code.op != opEnd; {
indent := strings.Repeat(" ", code.indent)
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
switch code.op.codeType() {
case codeArrayElem:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toArrayElemCode().end
case codeSliceElem:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toSliceElemCode().end
case codeMapKey:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.toMapKeyCode().end
case codeStructField:
sf := code.toStructFieldCode()
key := sf.displayKey
offset := sf.offset
codes = append(codes, fmt.Sprintf("%s%s [%s:%d] ( %p )", indent, code.op, key, offset, unsafe.Pointer(code)))
code = code.next
default:
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
code = code.next
}
}

View File

@ -1392,3 +1392,180 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
t.Fatalf("Marshal: got %s want %s", got, want)
}
}
func TestAnonymousFields(t *testing.T) {
tests := []struct {
label string // Test name
makeInput func() interface{} // Function to create input value
want string // Expected JSON output
}{{
// Both S1 and S2 have a field named X. From the perspective of S,
// it is ambiguous which one X refers to.
// This should not serialize either field.
label: "AmbiguousField",
makeInput: func() interface{} {
type (
S1 struct{ x, X int }
S2 struct{ x, X int }
S struct {
S1
S2
}
)
return S{S1{1, 2}, S2{3, 4}}
},
want: `{}`,
}, {
label: "DominantField",
// Both S1 and S2 have a field named X, but since S has an X field as
// well, it takes precedence over S1.X and S2.X.
makeInput: func() interface{} {
type (
S1 struct{ x, X int }
S2 struct{ x, X int }
S struct {
S1
S2
x, X int
}
)
return S{S1{1, 2}, S2{3, 4}, 5, 6}
},
want: `{"X":6}`,
}, {
// Unexported embedded field of non-struct type should not be serialized.
label: "UnexportedEmbeddedInt",
makeInput: func() interface{} {
type (
myInt int
S struct{ myInt }
)
return S{5}
},
want: `{}`,
}, {
// Exported embedded field of non-struct type should be serialized.
label: "ExportedEmbeddedInt",
makeInput: func() interface{} {
type (
MyInt int
S struct{ MyInt }
)
return S{5}
},
want: `{"MyInt":5}`,
}, {
// Unexported embedded field of pointer to non-struct type
// should not be serialized.
label: "UnexportedEmbeddedIntPointer",
makeInput: func() interface{} {
type (
myInt int
S struct{ *myInt }
)
s := S{new(myInt)}
*s.myInt = 5
return s
},
want: `{}`,
}, {
// Exported embedded field of pointer to non-struct type
// should be serialized.
label: "ExportedEmbeddedIntPointer",
makeInput: func() interface{} {
type (
MyInt int
S struct{ *MyInt }
)
s := S{new(MyInt)}
*s.MyInt = 5
return s
},
want: `{"MyInt":5}`,
}, {
// Exported fields of embedded structs should have their
// exported fields be serialized regardless of whether the struct types
// themselves are exported.
label: "EmbeddedStruct",
makeInput: func() interface{} {
type (
s1 struct{ x, X int }
S2 struct{ y, Y int }
S struct {
s1
S2
}
)
return S{s1{1, 2}, S2{3, 4}}
},
want: `{"X":2,"Y":4}`,
}, {
// Exported fields of pointers to embedded structs should have their
// exported fields be serialized regardless of whether the struct types
// themselves are exported.
label: "EmbeddedStructPointer",
makeInput: func() interface{} {
type (
s1 struct{ x, X int }
S2 struct{ y, Y int }
S struct {
*s1
*S2
}
)
return S{&s1{1, 2}, &S2{3, 4}}
},
want: `{"X":2,"Y":4}`,
}, {
// Exported fields on embedded unexported structs at multiple levels
// of nesting should still be serialized.
label: "NestedStructAndInts",
makeInput: func() interface{} {
type (
MyInt1 int
MyInt2 int
myInt int
s2 struct {
MyInt2
myInt
}
s1 struct {
MyInt1
myInt
s2
}
S struct {
s1
myInt
}
)
return S{s1{1, 2, s2{3, 4}}, 6}
},
want: `{"MyInt1":1,"MyInt2":3}`,
}, {
// If an anonymous struct pointer field is nil, we should ignore
// the embedded fields behind it. Not properly doing so may
// result in the wrong output or reflect panics.
label: "EmbeddedFieldBehindNilPointer",
makeInput: func() interface{} {
type (
S2 struct{ Field string }
S struct{ *S2 }
)
return S{}
},
want: `{}`,
}}
for i, tt := range tests {
t.Run(tt.label, func(t *testing.T) {
b, err := json.Marshal(tt.makeInput())
if err != nil {
t.Fatalf("%d: Marshal() = %v, want nil error", i, err)
}
if string(b) != tt.want {
t.Fatalf("%d: Marshal() = %q, want %q", i, b, tt.want)
}
})
}
}

View File

@ -538,7 +538,11 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHead {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end.next
} else {
e.encodeByte('{')
@ -566,12 +570,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadInt {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeInt(e.ptrToInt(ptr))
e.encodeInt(e.ptrToInt(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -585,7 +593,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeInt(e.ptrToInt(ptr))
e.encodeInt(e.ptrToInt(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -596,12 +604,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadInt8 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeInt8(e.ptrToInt8(ptr))
e.encodeInt8(e.ptrToInt8(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -615,7 +627,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeInt8(e.ptrToInt8(ptr))
e.encodeInt8(e.ptrToInt8(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -626,12 +638,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadInt16 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeInt16(e.ptrToInt16(ptr))
e.encodeInt16(e.ptrToInt16(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -645,7 +661,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeInt16(e.ptrToInt16(ptr))
e.encodeInt16(e.ptrToInt16(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -656,12 +672,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadInt32 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeInt32(e.ptrToInt32(ptr))
e.encodeInt32(e.ptrToInt32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -675,7 +695,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeInt32(e.ptrToInt32(ptr))
e.encodeInt32(e.ptrToInt32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -686,12 +706,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadInt64 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeInt64(e.ptrToInt64(ptr))
e.encodeInt64(e.ptrToInt64(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -705,7 +729,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeInt64(e.ptrToInt64(ptr))
e.encodeInt64(e.ptrToInt64(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -716,12 +740,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadUint {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeUint(e.ptrToUint(ptr))
e.encodeUint(e.ptrToUint(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -735,7 +763,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeUint(e.ptrToUint(ptr))
e.encodeUint(e.ptrToUint(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -746,12 +774,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadUint8 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeUint8(e.ptrToUint8(ptr))
e.encodeUint8(e.ptrToUint8(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -765,7 +797,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeUint8(e.ptrToUint8(ptr))
e.encodeUint8(e.ptrToUint8(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -776,12 +808,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadUint16 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeUint16(e.ptrToUint16(ptr))
e.encodeUint16(e.ptrToUint16(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -795,7 +831,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeUint16(e.ptrToUint16(ptr))
e.encodeUint16(e.ptrToUint16(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -806,12 +842,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadUint32 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeUint32(e.ptrToUint32(ptr))
e.encodeUint32(e.ptrToUint32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -825,7 +865,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeUint32(e.ptrToUint32(ptr))
e.encodeUint32(e.ptrToUint32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -836,12 +876,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadUint64 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeUint64(e.ptrToUint64(ptr))
e.encodeUint64(e.ptrToUint64(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -855,7 +899,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeUint64(e.ptrToUint64(ptr))
e.encodeUint64(e.ptrToUint64(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -866,12 +910,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadFloat32 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeFloat32(e.ptrToFloat32(ptr))
e.encodeFloat32(e.ptrToFloat32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -885,7 +933,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeFloat32(e.ptrToFloat32(ptr))
e.encodeFloat32(e.ptrToFloat32(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -896,10 +944,14 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadFloat64 {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
v := e.ptrToFloat64(ptr)
v := e.ptrToFloat64(ptr + field.offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return &UnsupportedValueError{
Value: reflect.ValueOf(v),
@ -921,7 +973,7 @@ func (e *Encoder) run(code *opcode) error {
if ptr == 0 {
code = field.end
} else {
v := e.ptrToFloat64(ptr)
v := e.ptrToFloat64(ptr + field.offset)
if math.IsInf(v, 0) || math.IsNaN(v) {
return &UnsupportedValueError{
Value: reflect.ValueOf(v),
@ -940,12 +992,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadString {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeString(e.ptrToString(ptr))
e.encodeString(e.ptrToString(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -959,7 +1015,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeString(e.ptrToString(ptr))
e.encodeString(e.ptrToString(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -970,12 +1026,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadBool {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
e.encodeBool(e.ptrToBool(ptr))
e.encodeBool(e.ptrToBool(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -989,7 +1049,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
e.encodeBool(e.ptrToBool(ptr))
e.encodeBool(e.ptrToBool(ptr + field.offset))
field.nextField.ptr = ptr
code = field.next
}
@ -1000,12 +1060,16 @@ func (e *Encoder) run(code *opcode) error {
field := code.toStructFieldCode()
ptr := field.ptr
if ptr == 0 {
e.encodeNull()
if code.op == opStructFieldPtrHeadBytes {
e.encodeNull()
} else {
e.encodeBytes([]byte{'{', '}'})
}
code = field.end
} else {
e.encodeByte('{')
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(ptr))
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(ptr + field.offset))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
@ -1022,7 +1086,7 @@ func (e *Encoder) run(code *opcode) error {
code = field.end
} else {
e.encodeBytes(field.key)
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(code.ptr))
s := base64.StdEncoding.EncodeToString(e.ptrToBytes(code.ptr + field.offset))
e.encodeByte('"')
e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s)))
e.encodeByte('"')
@ -1043,7 +1107,7 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBytes(field.key)
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
typ: code.typ,
ptr: unsafe.Pointer(ptr),
ptr: unsafe.Pointer(ptr + field.offset),
}))
rv := reflect.ValueOf(v)
if rv.Type().Kind() == reflect.Interface && rv.IsNil() {
@ -1084,7 +1148,7 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBytes(field.key)
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
typ: code.typ,
ptr: unsafe.Pointer(ptr),
ptr: unsafe.Pointer(ptr + field.offset),
}))
rv := reflect.ValueOf(v)
if rv.Type().Kind() == reflect.Interface && rv.IsNil() {
@ -1127,7 +1191,7 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBytes(field.key)
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
typ: code.typ,
ptr: unsafe.Pointer(ptr),
ptr: unsafe.Pointer(ptr + field.offset),
}))
rv := reflect.ValueOf(v)
if rv.Type().Kind() == reflect.Interface && rv.IsNil() {
@ -1158,7 +1222,7 @@ func (e *Encoder) run(code *opcode) error {
e.encodeBytes(field.key)
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
typ: code.typ,
ptr: unsafe.Pointer(ptr),
ptr: unsafe.Pointer(ptr + field.offset),
}))
rv := reflect.ValueOf(v)
if rv.Type().Kind() == reflect.Interface && rv.IsNil() {

View File

@ -10,9 +10,15 @@ func getTag(field reflect.StructField) string {
}
func isIgnoredStructField(field reflect.StructField) bool {
if field.PkgPath != "" && !field.Anonymous {
// private field
return true
if field.PkgPath != "" {
if field.Anonymous {
if !(field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) && field.Type.Kind() != reflect.Struct {
return true
}
} else {
// private field
return true
}
}
tag := getTag(field)
if tag == "-" {