Fix recursive definition of struct

This commit is contained in:
Masaaki Goshima 2020-08-13 15:26:35 +09:00
parent dad89cea0b
commit 2a99704531
4 changed files with 146 additions and 10 deletions

View File

@ -749,7 +749,7 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o
func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) { func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) {
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
if withIndent { if withIndent {
if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists { if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
return (*opcode)(unsafe.Pointer(&recursiveCode{ return (*opcode)(unsafe.Pointer(&recursiveCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive, op: opStructFieldRecursive,
@ -761,7 +761,7 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
})), nil })), nil
} }
} else { } else {
if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists { if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists {
return (*opcode)(unsafe.Pointer(&recursiveCode{ return (*opcode)(unsafe.Pointer(&recursiveCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive, op: opStructFieldRecursive,
@ -775,9 +775,9 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
} }
compiled := &compiledCode{} compiled := &compiledCode{}
if withIndent { if withIndent {
e.structTypeToCompiledCode[typeptr] = compiled
} else {
e.structTypeToCompiledIndentCode[typeptr] = compiled e.structTypeToCompiledIndentCode[typeptr] = compiled
} else {
e.structTypeToCompiledCode[typeptr] = compiled
} }
// header => code => structField => code => end // header => code => structField => code => end
// ^ | // ^ |

View File

@ -1416,3 +1416,134 @@ func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
} }
return code return code
} }
func newRecursiveCode(recursive *recursiveCode) *opcode {
code := copyOpcode(recursive.jmp.code)
head := (*structFieldCode)(unsafe.Pointer(code))
head.end.next = newEndOp(0)
code.ptr = recursive.ptr
switch code.op {
case opStructFieldPtrHead:
code.op = opStructFieldHead
case opStructFieldPtrHeadInt:
code.op = opStructFieldHeadInt
case opStructFieldPtrHeadInt8:
code.op = opStructFieldHeadInt8
case opStructFieldPtrHeadInt16:
code.op = opStructFieldHeadInt16
case opStructFieldPtrHeadInt32:
code.op = opStructFieldHeadInt32
case opStructFieldPtrHeadInt64:
code.op = opStructFieldHeadInt64
case opStructFieldPtrHeadUint:
code.op = opStructFieldHeadUint
case opStructFieldPtrHeadUint8:
code.op = opStructFieldHeadUint8
case opStructFieldPtrHeadUint16:
code.op = opStructFieldHeadUint16
case opStructFieldPtrHeadUint32:
code.op = opStructFieldHeadUint32
case opStructFieldPtrHeadUint64:
code.op = opStructFieldHeadUint64
case opStructFieldPtrHeadFloat32:
code.op = opStructFieldHeadFloat32
case opStructFieldPtrHeadFloat64:
code.op = opStructFieldHeadFloat64
case opStructFieldPtrHeadString:
code.op = opStructFieldHeadString
case opStructFieldPtrHeadBool:
code.op = opStructFieldHeadBool
case opStructFieldPtrHeadIndent:
code.op = opStructFieldHeadIndent
case opStructFieldPtrHeadIntIndent:
code.op = opStructFieldHeadIntIndent
case opStructFieldPtrHeadInt8Indent:
code.op = opStructFieldHeadInt8Indent
case opStructFieldPtrHeadInt16Indent:
code.op = opStructFieldHeadInt16Indent
case opStructFieldPtrHeadInt32Indent:
code.op = opStructFieldHeadInt32Indent
case opStructFieldPtrHeadInt64Indent:
code.op = opStructFieldHeadInt64Indent
case opStructFieldPtrHeadUintIndent:
code.op = opStructFieldHeadUintIndent
case opStructFieldPtrHeadUint8Indent:
code.op = opStructFieldHeadUint8Indent
case opStructFieldPtrHeadUint16Indent:
code.op = opStructFieldHeadUint16Indent
case opStructFieldPtrHeadUint32Indent:
code.op = opStructFieldHeadUint32Indent
case opStructFieldPtrHeadUint64Indent:
code.op = opStructFieldHeadUint64Indent
case opStructFieldPtrHeadFloat32Indent:
code.op = opStructFieldHeadFloat32Indent
case opStructFieldPtrHeadFloat64Indent:
code.op = opStructFieldHeadFloat64Indent
case opStructFieldPtrHeadStringIndent:
code.op = opStructFieldHeadStringIndent
case opStructFieldPtrHeadBoolIndent:
code.op = opStructFieldHeadBoolIndent
case opStructFieldPtrHeadOmitEmpty:
code.op = opStructFieldHeadOmitEmpty
case opStructFieldPtrHeadIntOmitEmpty:
code.op = opStructFieldHeadIntOmitEmpty
case opStructFieldPtrHeadInt8OmitEmpty:
code.op = opStructFieldHeadInt8OmitEmpty
case opStructFieldPtrHeadInt16OmitEmpty:
code.op = opStructFieldHeadInt16OmitEmpty
case opStructFieldPtrHeadInt32OmitEmpty:
code.op = opStructFieldHeadInt32OmitEmpty
case opStructFieldPtrHeadInt64OmitEmpty:
code.op = opStructFieldHeadInt64OmitEmpty
case opStructFieldPtrHeadUintOmitEmpty:
code.op = opStructFieldHeadUintOmitEmpty
case opStructFieldPtrHeadUint8OmitEmpty:
code.op = opStructFieldHeadUint8OmitEmpty
case opStructFieldPtrHeadUint16OmitEmpty:
code.op = opStructFieldHeadUint16OmitEmpty
case opStructFieldPtrHeadUint32OmitEmpty:
code.op = opStructFieldHeadUint32OmitEmpty
case opStructFieldPtrHeadUint64OmitEmpty:
code.op = opStructFieldHeadUint64OmitEmpty
case opStructFieldPtrHeadFloat32OmitEmpty:
code.op = opStructFieldHeadFloat32OmitEmpty
case opStructFieldPtrHeadFloat64OmitEmpty:
code.op = opStructFieldHeadFloat64OmitEmpty
case opStructFieldPtrHeadStringOmitEmpty:
code.op = opStructFieldHeadStringOmitEmpty
case opStructFieldPtrHeadBoolOmitEmpty:
code.op = opStructFieldHeadBoolOmitEmpty
case opStructFieldPtrHeadOmitEmptyIndent:
code.op = opStructFieldHeadOmitEmptyIndent
case opStructFieldPtrHeadIntOmitEmptyIndent:
code.op = opStructFieldHeadIntOmitEmptyIndent
case opStructFieldPtrHeadInt8OmitEmptyIndent:
code.op = opStructFieldHeadInt8OmitEmptyIndent
case opStructFieldPtrHeadInt16OmitEmptyIndent:
code.op = opStructFieldHeadInt16OmitEmptyIndent
case opStructFieldPtrHeadInt32OmitEmptyIndent:
code.op = opStructFieldHeadInt32OmitEmptyIndent
case opStructFieldPtrHeadInt64OmitEmptyIndent:
code.op = opStructFieldHeadInt64OmitEmptyIndent
case opStructFieldPtrHeadUintOmitEmptyIndent:
code.op = opStructFieldHeadUintOmitEmptyIndent
case opStructFieldPtrHeadUint8OmitEmptyIndent:
code.op = opStructFieldHeadUint8OmitEmptyIndent
case opStructFieldPtrHeadUint16OmitEmptyIndent:
code.op = opStructFieldHeadUint16OmitEmptyIndent
case opStructFieldPtrHeadUint32OmitEmptyIndent:
code.op = opStructFieldHeadUint32OmitEmptyIndent
case opStructFieldPtrHeadUint64OmitEmptyIndent:
code.op = opStructFieldHeadUint64OmitEmptyIndent
case opStructFieldPtrHeadFloat32OmitEmptyIndent:
code.op = opStructFieldHeadFloat32OmitEmptyIndent
case opStructFieldPtrHeadFloat64OmitEmptyIndent:
code.op = opStructFieldHeadFloat64OmitEmptyIndent
case opStructFieldPtrHeadStringOmitEmptyIndent:
code.op = opStructFieldHeadStringOmitEmptyIndent
case opStructFieldPtrHeadBoolOmitEmptyIndent:
code.op = opStructFieldHeadBoolOmitEmptyIndent
}
return code
}

View File

@ -12,8 +12,10 @@ import (
type recursiveT struct { type recursiveT struct {
A *recursiveT `json:"a,omitempty"` A *recursiveT `json:"a,omitempty"`
B *recursiveU `json:"b,omitempty"` B *recursiveU `json:"b,omitempty"`
C string `json:"c,omitempty"` C *recursiveU `json:"c,omitempty"`
D string `json:"d,omitempty"`
} }
type recursiveU struct { type recursiveU struct {
T *recursiveT `json:"t,omitempty"` T *recursiveT `json:"t,omitempty"`
} }
@ -117,13 +119,18 @@ func Test_Marshal(t *testing.T) {
A: &recursiveT{ A: &recursiveT{
B: &recursiveU{ B: &recursiveU{
T: &recursiveT{ T: &recursiveT{
C: "hello", D: "hello",
},
},
C: &recursiveU{
T: &recursiveT{
D: "world",
}, },
}, },
}, },
}) })
assertErr(t, err) assertErr(t, err)
assertEq(t, "recursive", `{"a":{"b":{"t":{"c":"hello"}}}}`, string(bytes)) assertEq(t, "recursive", `{"a":{"b":{"t":{"d":"hello"}},"c":{"t":{"d":"world"}}}}`, string(bytes))
}) })
t.Run("omitempty", func(t *testing.T) { t.Run("omitempty", func(t *testing.T) {
type T struct { type T struct {

View File

@ -460,9 +460,7 @@ func (e *Encoder) run(code *opcode) error {
code = c.next code = c.next
case opStructFieldRecursive: case opStructFieldRecursive:
recursive := code.toRecursiveCode() recursive := code.toRecursiveCode()
c := copyOpcode(recursive.jmp.code) if err := e.run(newRecursiveCode(recursive)); err != nil {
c.ptr = recursive.ptr
if err := e.run(c); err != nil {
return err return err
} }
code = recursive.next code = recursive.next