diff --git a/encode_compile.go b/encode_compile.go index 8d81da9..9cab925 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -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 diff --git a/encode_opcode.go b/encode_opcode.go index b43dc41..3b78686 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -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 } } diff --git a/encode_test.go b/encode_test.go index 10bf27e..2c3e25d 100644 --- a/encode_test.go +++ b/encode_test.go @@ -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) + } + }) + } +} diff --git a/encode_vm.go b/encode_vm.go index 8265cde..80fce44 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -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() { diff --git a/struct_field.go b/struct_field.go index b2a6f50..bb822b3 100644 --- a/struct_field.go +++ b/struct_field.go @@ -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 == "-" {