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