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,123 +609,228 @@ func TestCoverMarshalJSON(t *testing.T) {
}{A: nil, B: nil, C: nil},
},
// PtrHeadMarshalJSONZeroMultiFields
{
name: "PtrHeadMarshalJSONZeroMultiFields",
data: &struct {
A coverMarshalJSON `json:"a"`
B coverMarshalJSON `json:"b"`
}{},
},
{
name: "PtrHeadMarshalJSONZeroMultiFieldsOmitEmpty",
data: &struct {
A coverMarshalJSON `json:"a,omitempty"`
B coverMarshalJSON `json:"b,omitempty"`
}{},
},
{
name: "PtrHeadMarshalJSONZeroMultiFieldsString",
data: &struct {
A coverMarshalJSON `json:"a,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"`
}{},
},
// PtrHeadMarshalJSONMultiFields
{
name: "PtrHeadMarshalJSONMultiFields",
data: &struct {
A coverMarshalJSON `json:"a"`
B coverMarshalJSON `json:"b"`
}{A: coverMarshalJSON{}, B: coverMarshalJSON{}},
},
{
name: "PtrHeadMarshalJSONMultiFieldsOmitEmpty",
data: &struct {
A coverMarshalJSON `json:"a,omitempty"`
B coverMarshalJSON `json:"b,omitempty"`
}{A: coverMarshalJSON{}, B: coverMarshalJSON{}},
},
{
name: "PtrHeadMarshalJSONMultiFieldsString",
data: &struct {
A coverMarshalJSON `json:"a,string"`
B coverMarshalJSON `json:"b,string"`
}{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
{
name: "PtrHeadMarshalJSONPtrMultiFields",
data: &struct {
A *coverMarshalJSON `json:"a"`
B *coverMarshalJSON `json:"b"`
}{A: &coverMarshalJSON{}, B: &coverMarshalJSON{}},
},
{
name: "PtrHeadMarshalJSONPtrMultiFieldsOmitEmpty",
data: &struct {
A *coverMarshalJSON `json:"a,omitempty"`
B *coverMarshalJSON `json:"b,omitempty"`
}{A: &coverMarshalJSON{}, B: &coverMarshalJSON{}},
},
{
name: "PtrHeadMarshalJSONPtrMultiFieldsString",
data: &struct {
A *coverMarshalJSON `json:"a,string"`
B *coverMarshalJSON `json:"b,string"`
}{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
{
name: "PtrHeadMarshalJSONPtrNilMultiFields",
data: &struct {
A *coverMarshalJSON `json:"a"`
B *coverMarshalJSON `json:"b"`
}{A: nil, B: nil},
},
{
name: "PtrHeadMarshalJSONPtrNilMultiFieldsOmitEmpty",
data: &struct {
A *coverMarshalJSON `json:"a,omitempty"`
B *coverMarshalJSON `json:"b,omitempty"`
}{A: nil, B: nil},
},
{
name: "PtrHeadMarshalJSONPtrNilMultiFieldsString",
data: &struct {
A *coverMarshalJSON `json:"a,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},
},
// PtrHeadMarshalJSONNilMultiFields
{
name: "PtrHeadMarshalJSONNilMultiFields",
data: (*struct {
A coverMarshalJSON `json:"a"`
B coverMarshalJSON `json:"b"`
})(nil),
},
{
name: "PtrHeadMarshalJSONNilMultiFieldsOmitEmpty",
data: (*struct {
A coverMarshalJSON `json:"a,omitempty"`
B coverMarshalJSON `json:"b,omitempty"`
})(nil),
},
{
name: "PtrHeadMarshalJSONNilMultiFieldsString",
data: (*struct {
A coverMarshalJSON `json:"a,string"`
B coverMarshalJSON `json:"b,string"`
})(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),
},
/*
// PtrHeadMarshalJSONZeroMultiFields
{
name: "PtrHeadMarshalJSONZeroMultiFields",
data: &struct {
A [2]int `json:"a"`
B [2]int `json:"b"`
}{},
},
{
name: "PtrHeadMarshalJSONZeroMultiFieldsOmitEmpty",
data: &struct {
A [2]int `json:"a,omitempty"`
B [2]int `json:"b,omitempty"`
}{},
},
{
name: "PtrHeadMarshalJSONZeroMultiFieldsString",
data: &struct {
A [2]int `json:"a,string"`
B [2]int `json:"b,string"`
}{},
},
// PtrHeadMarshalJSONMultiFields
{
name: "PtrHeadMarshalJSONMultiFields",
data: &struct {
A [2]int `json:"a"`
B [2]int `json:"b"`
}{A: [2]int{-1}, B: [2]int{1}},
},
{
name: "PtrHeadMarshalJSONMultiFieldsOmitEmpty",
data: &struct {
A [2]int `json:"a,omitempty"`
B [2]int `json:"b,omitempty"`
}{A: [2]int{-1}, B: [2]int{1}},
},
{
name: "PtrHeadMarshalJSONMultiFieldsString",
data: &struct {
A [2]int `json:"a,string"`
B [2]int `json:"b,string"`
}{A: [2]int{-1}, B: [2]int{1}},
},
// PtrHeadMarshalJSONPtrMultiFields
{
name: "PtrHeadMarshalJSONPtrMultiFields",
data: &struct {
A *[2]int `json:"a"`
B *[2]int `json:"b"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})},
},
{
name: "PtrHeadMarshalJSONPtrMultiFieldsOmitEmpty",
data: &struct {
A *[2]int `json:"a,omitempty"`
B *[2]int `json:"b,omitempty"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})},
},
{
name: "PtrHeadMarshalJSONPtrMultiFieldsString",
data: &struct {
A *[2]int `json:"a,string"`
B *[2]int `json:"b,string"`
}{A: arrayptr([2]int{-1}), B: arrayptr([2]int{-2})},
},
// PtrHeadMarshalJSONPtrNilMultiFields
{
name: "PtrHeadMarshalJSONPtrNilMultiFields",
data: &struct {
A *[2]int `json:"a"`
B *[2]int `json:"b"`
}{A: nil, B: nil},
},
{
name: "PtrHeadMarshalJSONPtrNilMultiFieldsOmitEmpty",
data: &struct {
A *[2]int `json:"a,omitempty"`
B *[2]int `json:"b,omitempty"`
}{A: nil, B: nil},
},
{
name: "PtrHeadMarshalJSONPtrNilMultiFieldsString",
data: &struct {
A *[2]int `json:"a,string"`
B *[2]int `json:"b,string"`
}{A: nil, B: nil},
},
// PtrHeadMarshalJSONNilMultiFields
{
name: "PtrHeadMarshalJSONNilMultiFields",
data: (*struct {
A [2]int `json:"a"`
B [2]int `json:"b"`
})(nil),
},
{
name: "PtrHeadMarshalJSONNilMultiFieldsOmitEmpty",
data: (*struct {
A [2]int `json:"a,omitempty"`
B [2]int `json:"b,omitempty"`
})(nil),
},
{
name: "PtrHeadMarshalJSONNilMultiFieldsString",
data: (*struct {
A [2]int `json:"a,string"`
B [2]int `json:"b,string"`
})(nil),
},
// PtrHeadMarshalJSONNilMultiFields
{
name: "PtrHeadMarshalJSONNilMultiFields",

View File

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

View File

@ -20,6 +20,7 @@ type opcode struct {
anonymousKey bool // whether anonymous key
root bool // whether root
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
mask uint64 // mask for number
indent int // indent number
@ -93,6 +94,7 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
anonymousKey: c.anonymousKey,
root: c.root,
indirect: c.indirect,
nilcheck: c.nilcheck,
indent: c.indent,
idx: c.idx,
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, 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)
} else {
bb, err := encodeMarshalJSON(b, ptrToInterface(code, p+code.offset))
@ -3264,7 +3264,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
break
}
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
} else {
b = append(b, code.key...)
@ -4093,7 +4093,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
p := load(ctxptr, code.headIdx)
b = append(b, code.key...)
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)
} else {
v := ptrToInterface(code, p)
@ -4108,7 +4108,7 @@ func encodeRun(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt Enco
case opStructFieldOmitEmptyMarshalJSON:
ptr := load(ctxptr, code.headIdx)
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
break
}