Skip to content

Commit 67fe34d

Browse files
authored
Merge pull request #21 from goccy/feature/fix-recursive-struct
Fix recursive definition of struct
2 parents dad89ce + 2a99704 commit 67fe34d

File tree

4 files changed

+146
-10
lines changed

4 files changed

+146
-10
lines changed

encode_compile.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o
749749
func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) {
750750
typeptr := uintptr(unsafe.Pointer(typ))
751751
if withIndent {
752-
if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists {
752+
if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
753753
return (*opcode)(unsafe.Pointer(&recursiveCode{
754754
opcodeHeader: &opcodeHeader{
755755
op: opStructFieldRecursive,
@@ -761,7 +761,7 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
761761
})), nil
762762
}
763763
} else {
764-
if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
764+
if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists {
765765
return (*opcode)(unsafe.Pointer(&recursiveCode{
766766
opcodeHeader: &opcodeHeader{
767767
op: opStructFieldRecursive,
@@ -775,9 +775,9 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
775775
}
776776
compiled := &compiledCode{}
777777
if withIndent {
778-
e.structTypeToCompiledCode[typeptr] = compiled
779-
} else {
780778
e.structTypeToCompiledIndentCode[typeptr] = compiled
779+
} else {
780+
e.structTypeToCompiledCode[typeptr] = compiled
781781
}
782782
// header => code => structField => code => end
783783
// ^ |

encode_opcode.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,3 +1416,134 @@ func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
14161416
}
14171417
return code
14181418
}
1419+
1420+
func newRecursiveCode(recursive *recursiveCode) *opcode {
1421+
code := copyOpcode(recursive.jmp.code)
1422+
head := (*structFieldCode)(unsafe.Pointer(code))
1423+
head.end.next = newEndOp(0)
1424+
code.ptr = recursive.ptr
1425+
1426+
switch code.op {
1427+
case opStructFieldPtrHead:
1428+
code.op = opStructFieldHead
1429+
case opStructFieldPtrHeadInt:
1430+
code.op = opStructFieldHeadInt
1431+
case opStructFieldPtrHeadInt8:
1432+
code.op = opStructFieldHeadInt8
1433+
case opStructFieldPtrHeadInt16:
1434+
code.op = opStructFieldHeadInt16
1435+
case opStructFieldPtrHeadInt32:
1436+
code.op = opStructFieldHeadInt32
1437+
case opStructFieldPtrHeadInt64:
1438+
code.op = opStructFieldHeadInt64
1439+
case opStructFieldPtrHeadUint:
1440+
code.op = opStructFieldHeadUint
1441+
case opStructFieldPtrHeadUint8:
1442+
code.op = opStructFieldHeadUint8
1443+
case opStructFieldPtrHeadUint16:
1444+
code.op = opStructFieldHeadUint16
1445+
case opStructFieldPtrHeadUint32:
1446+
code.op = opStructFieldHeadUint32
1447+
case opStructFieldPtrHeadUint64:
1448+
code.op = opStructFieldHeadUint64
1449+
case opStructFieldPtrHeadFloat32:
1450+
code.op = opStructFieldHeadFloat32
1451+
case opStructFieldPtrHeadFloat64:
1452+
code.op = opStructFieldHeadFloat64
1453+
case opStructFieldPtrHeadString:
1454+
code.op = opStructFieldHeadString
1455+
case opStructFieldPtrHeadBool:
1456+
code.op = opStructFieldHeadBool
1457+
case opStructFieldPtrHeadIndent:
1458+
code.op = opStructFieldHeadIndent
1459+
case opStructFieldPtrHeadIntIndent:
1460+
code.op = opStructFieldHeadIntIndent
1461+
case opStructFieldPtrHeadInt8Indent:
1462+
code.op = opStructFieldHeadInt8Indent
1463+
case opStructFieldPtrHeadInt16Indent:
1464+
code.op = opStructFieldHeadInt16Indent
1465+
case opStructFieldPtrHeadInt32Indent:
1466+
code.op = opStructFieldHeadInt32Indent
1467+
case opStructFieldPtrHeadInt64Indent:
1468+
code.op = opStructFieldHeadInt64Indent
1469+
case opStructFieldPtrHeadUintIndent:
1470+
code.op = opStructFieldHeadUintIndent
1471+
case opStructFieldPtrHeadUint8Indent:
1472+
code.op = opStructFieldHeadUint8Indent
1473+
case opStructFieldPtrHeadUint16Indent:
1474+
code.op = opStructFieldHeadUint16Indent
1475+
case opStructFieldPtrHeadUint32Indent:
1476+
code.op = opStructFieldHeadUint32Indent
1477+
case opStructFieldPtrHeadUint64Indent:
1478+
code.op = opStructFieldHeadUint64Indent
1479+
case opStructFieldPtrHeadFloat32Indent:
1480+
code.op = opStructFieldHeadFloat32Indent
1481+
case opStructFieldPtrHeadFloat64Indent:
1482+
code.op = opStructFieldHeadFloat64Indent
1483+
case opStructFieldPtrHeadStringIndent:
1484+
code.op = opStructFieldHeadStringIndent
1485+
case opStructFieldPtrHeadBoolIndent:
1486+
code.op = opStructFieldHeadBoolIndent
1487+
case opStructFieldPtrHeadOmitEmpty:
1488+
code.op = opStructFieldHeadOmitEmpty
1489+
case opStructFieldPtrHeadIntOmitEmpty:
1490+
code.op = opStructFieldHeadIntOmitEmpty
1491+
case opStructFieldPtrHeadInt8OmitEmpty:
1492+
code.op = opStructFieldHeadInt8OmitEmpty
1493+
case opStructFieldPtrHeadInt16OmitEmpty:
1494+
code.op = opStructFieldHeadInt16OmitEmpty
1495+
case opStructFieldPtrHeadInt32OmitEmpty:
1496+
code.op = opStructFieldHeadInt32OmitEmpty
1497+
case opStructFieldPtrHeadInt64OmitEmpty:
1498+
code.op = opStructFieldHeadInt64OmitEmpty
1499+
case opStructFieldPtrHeadUintOmitEmpty:
1500+
code.op = opStructFieldHeadUintOmitEmpty
1501+
case opStructFieldPtrHeadUint8OmitEmpty:
1502+
code.op = opStructFieldHeadUint8OmitEmpty
1503+
case opStructFieldPtrHeadUint16OmitEmpty:
1504+
code.op = opStructFieldHeadUint16OmitEmpty
1505+
case opStructFieldPtrHeadUint32OmitEmpty:
1506+
code.op = opStructFieldHeadUint32OmitEmpty
1507+
case opStructFieldPtrHeadUint64OmitEmpty:
1508+
code.op = opStructFieldHeadUint64OmitEmpty
1509+
case opStructFieldPtrHeadFloat32OmitEmpty:
1510+
code.op = opStructFieldHeadFloat32OmitEmpty
1511+
case opStructFieldPtrHeadFloat64OmitEmpty:
1512+
code.op = opStructFieldHeadFloat64OmitEmpty
1513+
case opStructFieldPtrHeadStringOmitEmpty:
1514+
code.op = opStructFieldHeadStringOmitEmpty
1515+
case opStructFieldPtrHeadBoolOmitEmpty:
1516+
code.op = opStructFieldHeadBoolOmitEmpty
1517+
case opStructFieldPtrHeadOmitEmptyIndent:
1518+
code.op = opStructFieldHeadOmitEmptyIndent
1519+
case opStructFieldPtrHeadIntOmitEmptyIndent:
1520+
code.op = opStructFieldHeadIntOmitEmptyIndent
1521+
case opStructFieldPtrHeadInt8OmitEmptyIndent:
1522+
code.op = opStructFieldHeadInt8OmitEmptyIndent
1523+
case opStructFieldPtrHeadInt16OmitEmptyIndent:
1524+
code.op = opStructFieldHeadInt16OmitEmptyIndent
1525+
case opStructFieldPtrHeadInt32OmitEmptyIndent:
1526+
code.op = opStructFieldHeadInt32OmitEmptyIndent
1527+
case opStructFieldPtrHeadInt64OmitEmptyIndent:
1528+
code.op = opStructFieldHeadInt64OmitEmptyIndent
1529+
case opStructFieldPtrHeadUintOmitEmptyIndent:
1530+
code.op = opStructFieldHeadUintOmitEmptyIndent
1531+
case opStructFieldPtrHeadUint8OmitEmptyIndent:
1532+
code.op = opStructFieldHeadUint8OmitEmptyIndent
1533+
case opStructFieldPtrHeadUint16OmitEmptyIndent:
1534+
code.op = opStructFieldHeadUint16OmitEmptyIndent
1535+
case opStructFieldPtrHeadUint32OmitEmptyIndent:
1536+
code.op = opStructFieldHeadUint32OmitEmptyIndent
1537+
case opStructFieldPtrHeadUint64OmitEmptyIndent:
1538+
code.op = opStructFieldHeadUint64OmitEmptyIndent
1539+
case opStructFieldPtrHeadFloat32OmitEmptyIndent:
1540+
code.op = opStructFieldHeadFloat32OmitEmptyIndent
1541+
case opStructFieldPtrHeadFloat64OmitEmptyIndent:
1542+
code.op = opStructFieldHeadFloat64OmitEmptyIndent
1543+
case opStructFieldPtrHeadStringOmitEmptyIndent:
1544+
code.op = opStructFieldHeadStringOmitEmptyIndent
1545+
case opStructFieldPtrHeadBoolOmitEmptyIndent:
1546+
code.op = opStructFieldHeadBoolOmitEmptyIndent
1547+
}
1548+
return code
1549+
}

encode_test.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import (
1212
type recursiveT struct {
1313
A *recursiveT `json:"a,omitempty"`
1414
B *recursiveU `json:"b,omitempty"`
15-
C string `json:"c,omitempty"`
15+
C *recursiveU `json:"c,omitempty"`
16+
D string `json:"d,omitempty"`
1617
}
18+
1719
type recursiveU struct {
1820
T *recursiveT `json:"t,omitempty"`
1921
}
@@ -117,13 +119,18 @@ func Test_Marshal(t *testing.T) {
117119
A: &recursiveT{
118120
B: &recursiveU{
119121
T: &recursiveT{
120-
C: "hello",
122+
D: "hello",
123+
},
124+
},
125+
C: &recursiveU{
126+
T: &recursiveT{
127+
D: "world",
121128
},
122129
},
123130
},
124131
})
125132
assertErr(t, err)
126-
assertEq(t, "recursive", `{"a":{"b":{"t":{"c":"hello"}}}}`, string(bytes))
133+
assertEq(t, "recursive", `{"a":{"b":{"t":{"d":"hello"}},"c":{"t":{"d":"world"}}}}`, string(bytes))
127134
})
128135
t.Run("omitempty", func(t *testing.T) {
129136
type T struct {

encode_vm.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,7 @@ func (e *Encoder) run(code *opcode) error {
460460
code = c.next
461461
case opStructFieldRecursive:
462462
recursive := code.toRecursiveCode()
463-
c := copyOpcode(recursive.jmp.code)
464-
c.ptr = recursive.ptr
465-
if err := e.run(c); err != nil {
463+
if err := e.run(newRecursiveCode(recursive)); err != nil {
466464
return err
467465
}
468466
code = recursive.next

0 commit comments

Comments
 (0)