Add nilcheck property

This commit is contained in:
Masaaki Goshima 2021-03-05 15:10:31 +09:00
parent d3b031cef2
commit 249c4da6dd
4 changed files with 242 additions and 122 deletions

View File

@ -609,28 +609,47 @@ func TestCoverMarshalJSON(t *testing.T) {
}{A: nil, B: nil, C: nil}, }{A: nil, B: nil, C: nil},
}, },
/*
// PtrHeadMarshalJSONZeroMultiFields // PtrHeadMarshalJSONZeroMultiFields
{ {
name: "PtrHeadMarshalJSONZeroMultiFields", name: "PtrHeadMarshalJSONZeroMultiFields",
data: &struct { data: &struct {
A [2]int `json:"a"` A coverMarshalJSON `json:"a"`
B [2]int `json:"b"` B coverMarshalJSON `json:"b"`
}{}, }{},
}, },
{ {
name: "PtrHeadMarshalJSONZeroMultiFieldsOmitEmpty", name: "PtrHeadMarshalJSONZeroMultiFieldsOmitEmpty",
data: &struct { data: &struct {
A [2]int `json:"a,omitempty"` A coverMarshalJSON `json:"a,omitempty"`
B [2]int `json:"b,omitempty"` B coverMarshalJSON `json:"b,omitempty"`
}{}, }{},
}, },
{ {
name: "PtrHeadMarshalJSONZeroMultiFieldsString", name: "PtrHeadMarshalJSONZeroMultiFieldsString",
data: &struct { data: &struct {
A [2]int `json:"a,string"` A coverMarshalJSON `json:"a,string"`
B [2]int `json:"b,string"` B coverMarshalJSON `json:"b,string"`
}{},
},
{
name: "PtrHeadPtrMarshalJSONZeroMultiFields",
data: &struct {
A coverPtrMarshalJSON `json:"a"`
B coverPtrMarshalJSON `json:"b"`
}{},
},
{
name: "PtrHeadPtrMarshalJSONZeroMultiFieldsOmitEmpty",
data: &struct {
A coverPtrMarshalJSON `json:"a,omitempty"`
B coverPtrMarshalJSON `json:"b,omitempty"`
}{},
},
{
name: "PtrHeadPtrMarshalJSONZeroMultiFieldsString",
data: &struct {
A coverPtrMarshalJSON `json:"a,string"`
B coverPtrMarshalJSON `json:"b,string"`
}{}, }{},
}, },
@ -638,68 +657,131 @@ func TestCoverMarshalJSON(t *testing.T) {
{ {
name: "PtrHeadMarshalJSONMultiFields", name: "PtrHeadMarshalJSONMultiFields",
data: &struct { data: &struct {
A [2]int `json:"a"` A coverMarshalJSON `json:"a"`
B [2]int `json:"b"` B coverMarshalJSON `json:"b"`
}{A: [2]int{-1}, B: [2]int{1}}, }{A: coverMarshalJSON{}, B: coverMarshalJSON{}},
}, },
{ {
name: "PtrHeadMarshalJSONMultiFieldsOmitEmpty", name: "PtrHeadMarshalJSONMultiFieldsOmitEmpty",
data: &struct { data: &struct {
A [2]int `json:"a,omitempty"` A coverMarshalJSON `json:"a,omitempty"`
B [2]int `json:"b,omitempty"` B coverMarshalJSON `json:"b,omitempty"`
}{A: [2]int{-1}, B: [2]int{1}}, }{A: coverMarshalJSON{}, B: coverMarshalJSON{}},
}, },
{ {
name: "PtrHeadMarshalJSONMultiFieldsString", name: "PtrHeadMarshalJSONMultiFieldsString",
data: &struct { data: &struct {
A [2]int `json:"a,string"` A coverMarshalJSON `json:"a,string"`
B [2]int `json:"b,string"` B coverMarshalJSON `json:"b,string"`
}{A: [2]int{-1}, B: [2]int{1}}, }{A: coverMarshalJSON{}, B: coverMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONMultiFields",
data: &struct {
A coverPtrMarshalJSON `json:"a"`
B coverPtrMarshalJSON `json:"b"`
}{A: coverPtrMarshalJSON{}, B: coverPtrMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONMultiFieldsOmitEmpty",
data: &struct {
A coverPtrMarshalJSON `json:"a,omitempty"`
B coverPtrMarshalJSON `json:"b,omitempty"`
}{A: coverPtrMarshalJSON{}, B: coverPtrMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONMultiFieldsString",
data: &struct {
A coverPtrMarshalJSON `json:"a,string"`
B coverPtrMarshalJSON `json:"b,string"`
}{A: coverPtrMarshalJSON{}, B: coverPtrMarshalJSON{}},
}, },
// PtrHeadMarshalJSONPtrMultiFields // PtrHeadMarshalJSONPtrMultiFields
{ {
name: "PtrHeadMarshalJSONPtrMultiFields", name: "PtrHeadMarshalJSONPtrMultiFields",
data: &struct { data: &struct {
A *[2]int `json:"a"` A *coverMarshalJSON `json:"a"`
B *[2]int `json:"b"` B *coverMarshalJSON `json:"b"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})}, }{A: &coverMarshalJSON{}, B: &coverMarshalJSON{}},
}, },
{ {
name: "PtrHeadMarshalJSONPtrMultiFieldsOmitEmpty", name: "PtrHeadMarshalJSONPtrMultiFieldsOmitEmpty",
data: &struct { data: &struct {
A *[2]int `json:"a,omitempty"` A *coverMarshalJSON `json:"a,omitempty"`
B *[2]int `json:"b,omitempty"` B *coverMarshalJSON `json:"b,omitempty"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})}, }{A: &coverMarshalJSON{}, B: &coverMarshalJSON{}},
}, },
{ {
name: "PtrHeadMarshalJSONPtrMultiFieldsString", name: "PtrHeadMarshalJSONPtrMultiFieldsString",
data: &struct { data: &struct {
A *[2]int `json:"a,string"` A *coverMarshalJSON `json:"a,string"`
B *[2]int `json:"b,string"` B *coverMarshalJSON `json:"b,string"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})}, }{A: &coverMarshalJSON{}, B: &coverMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONPtrMultiFields",
data: &struct {
A *coverPtrMarshalJSON `json:"a"`
B *coverPtrMarshalJSON `json:"b"`
}{A: &coverPtrMarshalJSON{}, B: &coverPtrMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONPtrMultiFieldsOmitEmpty",
data: &struct {
A *coverPtrMarshalJSON `json:"a,omitempty"`
B *coverPtrMarshalJSON `json:"b,omitempty"`
}{A: &coverPtrMarshalJSON{}, B: &coverPtrMarshalJSON{}},
},
{
name: "PtrHeadPtrMarshalJSONPtrMultiFieldsString",
data: &struct {
A *coverPtrMarshalJSON `json:"a,string"`
B *coverPtrMarshalJSON `json:"b,string"`
}{A: &coverPtrMarshalJSON{}, B: &coverPtrMarshalJSON{}},
}, },
// PtrHeadMarshalJSONPtrNilMultiFields // PtrHeadMarshalJSONPtrNilMultiFields
{ {
name: "PtrHeadMarshalJSONPtrNilMultiFields", name: "PtrHeadMarshalJSONPtrNilMultiFields",
data: &struct { data: &struct {
A *[2]int `json:"a"` A *coverMarshalJSON `json:"a"`
B *[2]int `json:"b"` B *coverMarshalJSON `json:"b"`
}{A: nil, B: nil}, }{A: nil, B: nil},
}, },
{ {
name: "PtrHeadMarshalJSONPtrNilMultiFieldsOmitEmpty", name: "PtrHeadMarshalJSONPtrNilMultiFieldsOmitEmpty",
data: &struct { data: &struct {
A *[2]int `json:"a,omitempty"` A *coverMarshalJSON `json:"a,omitempty"`
B *[2]int `json:"b,omitempty"` B *coverMarshalJSON `json:"b,omitempty"`
}{A: nil, B: nil}, }{A: nil, B: nil},
}, },
{ {
name: "PtrHeadMarshalJSONPtrNilMultiFieldsString", name: "PtrHeadMarshalJSONPtrNilMultiFieldsString",
data: &struct { data: &struct {
A *[2]int `json:"a,string"` A *coverMarshalJSON `json:"a,string"`
B *[2]int `json:"b,string"` B *coverMarshalJSON `json:"b,string"`
}{A: nil, B: nil},
},
{
name: "PtrHeadPtrMarshalJSONPtrNilMultiFields",
data: &struct {
A *coverPtrMarshalJSON `json:"a"`
B *coverPtrMarshalJSON `json:"b"`
}{A: nil, B: nil},
},
{
name: "PtrHeadPtrMarshalJSONPtrNilMultiFieldsOmitEmpty",
data: &struct {
A *coverPtrMarshalJSON `json:"a,omitempty"`
B *coverPtrMarshalJSON `json:"b,omitempty"`
}{A: nil, B: nil},
},
{
name: "PtrHeadPtrMarshalJSONPtrNilMultiFieldsString",
data: &struct {
A *coverPtrMarshalJSON `json:"a,string"`
B *coverPtrMarshalJSON `json:"b,string"`
}{A: nil, B: nil}, }{A: nil, B: nil},
}, },
@ -707,24 +789,47 @@ func TestCoverMarshalJSON(t *testing.T) {
{ {
name: "PtrHeadMarshalJSONNilMultiFields", name: "PtrHeadMarshalJSONNilMultiFields",
data: (*struct { data: (*struct {
A [2]int `json:"a"` A coverMarshalJSON `json:"a"`
B [2]int `json:"b"` B coverMarshalJSON `json:"b"`
})(nil), })(nil),
}, },
{ {
name: "PtrHeadMarshalJSONNilMultiFieldsOmitEmpty", name: "PtrHeadMarshalJSONNilMultiFieldsOmitEmpty",
data: (*struct { data: (*struct {
A [2]int `json:"a,omitempty"` A coverMarshalJSON `json:"a,omitempty"`
B [2]int `json:"b,omitempty"` B coverMarshalJSON `json:"b,omitempty"`
})(nil), })(nil),
}, },
{ {
name: "PtrHeadMarshalJSONNilMultiFieldsString", name: "PtrHeadMarshalJSONNilMultiFieldsString",
data: (*struct { data: (*struct {
A [2]int `json:"a,string"` A coverMarshalJSON `json:"a,string"`
B [2]int `json:"b,string"` B coverMarshalJSON `json:"b,string"`
})(nil), })(nil),
}, },
{
name: "PtrHeadPtrMarshalJSONNilMultiFields",
data: (*struct {
A coverPtrMarshalJSON `json:"a"`
B coverPtrMarshalJSON `json:"b"`
})(nil),
},
{
name: "PtrHeadPtrMarshalJSONNilMultiFieldsOmitEmpty",
data: (*struct {
A coverPtrMarshalJSON `json:"a,omitempty"`
B coverPtrMarshalJSON `json:"b,omitempty"`
})(nil),
},
{
name: "PtrHeadPtrMarshalJSONNilMultiFieldsString",
data: (*struct {
A coverPtrMarshalJSON `json:"a,string"`
B coverPtrMarshalJSON `json:"b,string"`
})(nil),
},
/*
// PtrHeadMarshalJSONNilMultiFields // PtrHeadMarshalJSONNilMultiFields
{ {

View File

@ -1231,6 +1231,7 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
fieldNum := typ.NumField() fieldNum := typ.NumField()
indirect := ifaceIndir(typ) indirect := ifaceIndir(typ)
fieldIdx := 0 fieldIdx := 0
disableIndirectConversion := false
var ( var (
head *opcode head *opcode
code *opcode code *opcode
@ -1263,6 +1264,7 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
fieldOpcodeIndex := ctx.opcodeIndex fieldOpcodeIndex := ctx.opcodeIndex
fieldPtrIndex := ctx.ptrIndex fieldPtrIndex := ctx.ptrIndex
ctx.incIndex() ctx.incIndex()
nilcheck := indirect
var valueCode *opcode var valueCode *opcode
if i == 0 && fieldNum == 1 && isPtr && rtype_ptrTo(fieldType).Implements(marshalJSONType) && !fieldType.Implements(marshalJSONType) { if i == 0 && fieldNum == 1 && isPtr && rtype_ptrTo(fieldType).Implements(marshalJSONType) && !fieldType.Implements(marshalJSONType) {
// *struct{ field implementedMarshalJSONType } => struct { field *implementedMarshalJSONType } // *struct{ field implementedMarshalJSONType } => struct { field *implementedMarshalJSONType }
@ -1273,8 +1275,17 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
return nil, err return nil, err
} }
valueCode = code valueCode = code
nilcheck = false
indirect = false indirect = false
isPtr = false disableIndirectConversion = true
} else if isPtr && fieldNum > 1 && fieldType.Kind() != reflect.Ptr && !fieldType.Implements(marshalJSONType) && rtype_ptrTo(fieldType).Implements(marshalJSONType) {
ctx.typ = rtype_ptrTo(fieldType)
code, err := encodeCompileMarshalJSON(ctx)
if err != nil {
return nil, err
}
nilcheck = false
valueCode = code
} else { } else {
code, err := encodeCompile(ctx.withType(fieldType), i == 0 && isPtr) code, err := encodeCompile(ctx.withType(fieldType), i == 0 && isPtr)
if err != nil { if err != nil {
@ -1301,6 +1312,7 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
key := fmt.Sprintf(`"%s":`, tag.key) key := fmt.Sprintf(`"%s":`, tag.key)
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key))) escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
valueCode.indirect = indirect valueCode.indirect = indirect
valueCode.nilcheck = nilcheck
fieldCode := &opcode{ fieldCode := &opcode{
typ: valueCode.typ, typ: valueCode.typ,
displayIdx: fieldOpcodeIndex, displayIdx: fieldOpcodeIndex,
@ -1314,6 +1326,7 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
displayKey: tag.key, displayKey: tag.key,
offset: field.Offset, offset: field.Offset,
indirect: indirect, indirect: indirect,
nilcheck: nilcheck,
} }
if fieldIdx == 0 { if fieldIdx == 0 {
fieldCode.headIdx = fieldCode.idx fieldCode.headIdx = fieldCode.idx
@ -1373,8 +1386,8 @@ func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error)
delete(ctx.structTypeToCompiledCode, typeptr) delete(ctx.structTypeToCompiledCode, typeptr)
if !code.indirect && isPtr { if !disableIndirectConversion && !head.indirect && isPtr {
code.indirect = true head.indirect = true
} }
return ret, nil return ret, nil

View File

@ -20,6 +20,7 @@ type opcode struct {
anonymousKey bool // whether anonymous key anonymousKey bool // whether anonymous key
root bool // whether root root bool // whether root
indirect bool // whether indirect or not indirect bool // whether indirect or not
nilcheck bool // whether needs to nilcheck or not
rshiftNum uint8 // use to take bit for judging whether negative integer or not rshiftNum uint8 // use to take bit for judging whether negative integer or not
mask uint64 // mask for number mask uint64 // mask for number
indent int // indent number indent int // indent number
@ -93,6 +94,7 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
anonymousKey: c.anonymousKey, anonymousKey: c.anonymousKey,
root: c.root, root: c.root,
indirect: c.indirect, indirect: c.indirect,
nilcheck: c.nilcheck,
indent: c.indent, indent: c.indent,
idx: c.idx, idx: c.idx,
headIdx: c.headIdx, headIdx: c.headIdx,

View File

@ -3234,7 +3234,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
} }
b = append(b, '{') b = append(b, '{')
b = append(b, code.key...) b = append(b, code.key...)
if p == 0 || code.indirect && code.typ.Kind() == reflect.Ptr && ptrToPtr(p) == 0 { if p == 0 || code.nilcheck && code.typ.Kind() == reflect.Ptr && ptrToPtr(p) == 0 {
b = encodeNull(b) b = encodeNull(b)
} else { } else {
bb, err := encodeMarshalJSON(b, ptrToInterface(code, p+code.offset)) bb, err := encodeMarshalJSON(b, ptrToInterface(code, p+code.offset))
@ -3264,7 +3264,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
break break
} }
b = append(b, '{') b = append(b, '{')
if p == 0 || code.indirect && code.typ.Kind() == reflect.Ptr && ptrToPtr(p) == 0 { if p == 0 || code.nilcheck && code.typ.Kind() == reflect.Ptr && ptrToPtr(p) == 0 {
code = code.nextField code = code.nextField
} else { } else {
b = append(b, code.key...) b = append(b, code.key...)
@ -4093,7 +4093,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
p := load(ctxptr, code.headIdx) p := load(ctxptr, code.headIdx)
b = append(b, code.key...) b = append(b, code.key...)
p += code.offset p += code.offset
if code.typ.Kind() == reflect.Ptr && p != 0 && ptrToPtr(p) == 0 { if code.nilcheck && code.typ.Kind() == reflect.Ptr && p != 0 && ptrToPtr(p) == 0 {
b = encodeNull(b) b = encodeNull(b)
} else { } else {
v := ptrToInterface(code, p) v := ptrToInterface(code, p)
@ -4108,7 +4108,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
case opStructFieldOmitEmptyMarshalJSON: case opStructFieldOmitEmptyMarshalJSON:
ptr := load(ctxptr, code.headIdx) ptr := load(ctxptr, code.headIdx)
p := ptr + code.offset p := ptr + code.offset
if code.typ.Kind() == reflect.Ptr && p != 0 && ptrToPtr(p) == 0 { if code.nilcheck && code.typ.Kind() == reflect.Ptr && p != 0 && ptrToPtr(p) == 0 {
code = code.nextField code = code.nextField
break break
} }