mirror of https://github.com/goccy/go-json.git
Fix recursive anonymous field
This commit is contained in:
parent
fda849e8f0
commit
3e1a1ac1ad
|
@ -213,7 +213,7 @@ func (t opType) fieldToStringTagField() opType {
|
|||
"int", "int8", "int16", "int32", "int64",
|
||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"float32", "float64", "bool", "string", "bytes",
|
||||
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText",
|
||||
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
|
||||
}
|
||||
primitiveTypesUpper := []string{}
|
||||
for _, typ := range primitiveTypes {
|
||||
|
@ -252,7 +252,6 @@ func (t opType) fieldToStringTagField() opType {
|
|||
{"StructField", "StructFieldIndent", "StructField"},
|
||||
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldStringTag", "StructFieldStringTagIndent", "StructField"},
|
||||
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
|
||||
{"StructFieldRecursiveEnd", "StructFieldRecursiveEndIndent", "Op"},
|
||||
{"StructEnd", "StructEndIndent", "StructField"},
|
||||
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},
|
||||
|
|
|
@ -1716,7 +1716,6 @@ func TestMarshalBadUTF8(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestMarshalNumberZeroVal(t *testing.T) {
|
||||
var n json.Number
|
||||
out, err := json.Marshal(n)
|
||||
|
@ -1771,6 +1770,7 @@ func TestMarshalEmbeds(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func equalError(a, b error) bool {
|
||||
if a == nil {
|
||||
return b == nil
|
||||
|
|
|
@ -785,6 +785,7 @@ func (e *Encoder) isNotExistsField(head *opcode) bool {
|
|||
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
|
||||
code := head
|
||||
var prev *opcode
|
||||
removedFields := map[*opcode]struct{}{}
|
||||
for {
|
||||
if code.op == opStructEnd || code.op == opStructEndIndent {
|
||||
break
|
||||
|
@ -798,7 +799,7 @@ func (e *Encoder) optimizeAnonymousFields(head *opcode) {
|
|||
for i := 0; i < diff; i++ {
|
||||
code.next.decOpcodeIndex()
|
||||
}
|
||||
linkPrevToNextField(prev, code)
|
||||
linkPrevToNextField(code, removedFields)
|
||||
code = prev
|
||||
}
|
||||
}
|
||||
|
@ -815,27 +816,30 @@ type structFieldPair struct {
|
|||
linked bool
|
||||
}
|
||||
|
||||
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *opcode) map[string][]structFieldPair {
|
||||
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
|
||||
anonymousFields := map[string][]structFieldPair{}
|
||||
f := valueCode
|
||||
var prevAnonymousField *opcode
|
||||
removedFields := map[*opcode]struct{}{}
|
||||
for {
|
||||
existsKey := tags.existsKey(f.displayKey)
|
||||
op := f.op.headToAnonymousHead()
|
||||
if op != f.op {
|
||||
if existsKey && (f.next.op == opStructFieldPtrAnonymousHeadRecursive || f.next.op == opStructFieldAnonymousHeadRecursive) {
|
||||
// through
|
||||
} else if op != f.op {
|
||||
if existsKey {
|
||||
f.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
} else if named == "" {
|
||||
f.op = op
|
||||
}
|
||||
} else if f.op == opStructEnd {
|
||||
} else if named == "" && f.op == opStructEnd {
|
||||
f.op = opStructAnonymousEnd
|
||||
} else if existsKey {
|
||||
diff := f.nextField.displayIdx - f.displayIdx
|
||||
for i := 0; i < diff; i++ {
|
||||
f.nextField.decOpcodeIndex()
|
||||
}
|
||||
linkPrevToNextField(prevAnonymousField, f)
|
||||
linkPrevToNextField(f, removedFields)
|
||||
}
|
||||
|
||||
if f.displayKey == "" {
|
||||
|
@ -847,13 +851,14 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
|
|||
continue
|
||||
}
|
||||
|
||||
anonymousFields[f.displayKey] = append(anonymousFields[f.displayKey], structFieldPair{
|
||||
key := fmt.Sprintf("%s.%s", named, f.displayKey)
|
||||
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
|
||||
prevField: prevAnonymousField,
|
||||
curField: f,
|
||||
isTaggedKey: f.isTaggedKey,
|
||||
})
|
||||
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next) {
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, named, f.next) {
|
||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||
}
|
||||
}
|
||||
|
@ -867,6 +872,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
|
|||
}
|
||||
|
||||
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
|
||||
removedFields := map[*opcode]struct{}{}
|
||||
for _, fieldPairs := range anonymousFields {
|
||||
if len(fieldPairs) == 1 {
|
||||
continue
|
||||
|
@ -886,7 +892,8 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
|
|||
for i := 0; i < diff; i++ {
|
||||
fieldPair.curField.nextField.decOpcodeIndex()
|
||||
}
|
||||
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
|
||||
removedFields[fieldPair.curField] = struct{}{}
|
||||
linkPrevToNextField(fieldPair.curField, removedFields)
|
||||
}
|
||||
fieldPair.linked = true
|
||||
}
|
||||
|
@ -900,10 +907,11 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
|
|||
fieldPair.curField.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
|
||||
removedFields[fieldPair.curField] = struct{}{}
|
||||
for i := 0; i < diff; i++ {
|
||||
fieldPair.curField.nextField.decOpcodeIndex()
|
||||
}
|
||||
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
|
||||
linkPrevToNextField(fieldPair.curField, removedFields)
|
||||
}
|
||||
fieldPair.linked = true
|
||||
}
|
||||
|
@ -970,7 +978,17 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
}
|
||||
|
||||
if field.Anonymous {
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode) {
|
||||
if valueCode.op == opPtr && valueCode.next.op == opStructFieldRecursive {
|
||||
valueCode = valueCode.next
|
||||
valueCode.decOpcodeIndex()
|
||||
ctx.decIndex()
|
||||
valueCode.op = opStructFieldPtrHeadRecursive
|
||||
}
|
||||
tagKey := ""
|
||||
if tag.isTaggedKey {
|
||||
tagKey = tag.key
|
||||
}
|
||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, tagKey, valueCode) {
|
||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||
}
|
||||
}
|
||||
|
@ -1015,6 +1033,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
code.next = fieldCode
|
||||
code = e.structField(ctx, fieldCode, valueCode, tag)
|
||||
prevField.nextField = fieldCode
|
||||
fieldCode.prevField = prevField
|
||||
prevField = fieldCode
|
||||
}
|
||||
fieldIdx++
|
||||
|
@ -1039,6 +1058,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
indent: ctx.indent,
|
||||
nextField: structEndCode,
|
||||
}
|
||||
structEndCode.prevField = head
|
||||
ctx.incIndex()
|
||||
if ctx.withIndent {
|
||||
head.op = opStructFieldHeadIndent
|
||||
|
@ -1056,14 +1076,13 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
|||
|
||||
if prevField != nil && prevField.nextField == nil {
|
||||
prevField.nextField = structEndCode
|
||||
structEndCode.prevField = prevField
|
||||
}
|
||||
|
||||
head.end = structEndCode
|
||||
code.next = structEndCode
|
||||
|
||||
e.optimizeConflictAnonymousFields(anonymousFields)
|
||||
e.optimizeAnonymousFields(head)
|
||||
|
||||
ret := (*opcode)(unsafe.Pointer(head))
|
||||
compiled.code = ret
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ type opcode struct {
|
|||
mapValue *opcode // map value
|
||||
elem *opcode // array/slice elem
|
||||
end *opcode // array/slice/struct/map end
|
||||
prevField *opcode // prev struct field
|
||||
nextField *opcode // next struct field
|
||||
next *opcode // next opcode
|
||||
jmp *compiledCode // for recursive call
|
||||
|
@ -102,6 +103,7 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
copied.mapValue = c.mapValue.copy(codeMap)
|
||||
copied.elem = c.elem.copy(codeMap)
|
||||
copied.end = c.end.copy(codeMap)
|
||||
copied.prevField = c.prevField.copy(codeMap)
|
||||
copied.nextField = c.nextField.copy(codeMap)
|
||||
copied.next = c.next.copy(codeMap)
|
||||
copied.jmp = c.jmp
|
||||
|
@ -309,8 +311,23 @@ func (c *opcode) dump() string {
|
|||
return strings.Join(codes, "\n")
|
||||
}
|
||||
|
||||
func linkPrevToNextField(prev, cur *opcode) {
|
||||
prev.nextField = cur.nextField
|
||||
func prevField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return prevField(code.prevField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func nextField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
|
||||
if _, exists := removedFields[code]; exists {
|
||||
return nextField(code.nextField, removedFields)
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func linkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
|
||||
prev := prevField(cur.prevField, removedFields)
|
||||
prev.nextField = nextField(cur.nextField, removedFields)
|
||||
code := prev
|
||||
fcode := cur
|
||||
for {
|
||||
|
|
1876
encode_optype.go
1876
encode_optype.go
File diff suppressed because it is too large
Load Diff
|
@ -1000,6 +1000,11 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
|||
e.buf = e.buf[:pos[0]]
|
||||
e.buf = append(e.buf, buf...)
|
||||
code = code.next
|
||||
case opStructFieldPtrAnonymousHeadRecursive:
|
||||
store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx)))
|
||||
fallthrough
|
||||
case opStructFieldAnonymousHeadRecursive:
|
||||
fallthrough
|
||||
case opStructFieldRecursive:
|
||||
ptr := load(ctxptr, code.idx)
|
||||
if ptr != 0 {
|
||||
|
|
Loading…
Reference in New Issue