Fix recursive anonymous field

This commit is contained in:
Masaaki Goshima 2020-11-16 21:28:33 +09:00
parent fda849e8f0
commit 3e1a1ac1ad
7 changed files with 1159 additions and 796 deletions

View File

@ -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"},

View File

@ -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

View File

@ -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

View File

@ -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 {

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -317,6 +317,9 @@ func (n Number) Int64() (int64, error) {
}
func (n Number) MarshalJSON() ([]byte, error) {
if n == "" {
return []byte("0"), nil
}
if _, err := n.Float64(); err != nil {
return nil, err
}