Merge pull request #57 from goccy/feature/fix-anonymous-fields

Fix recursive anonymous field
This commit is contained in:
Masaaki Goshima 2020-11-16 21:34:34 +09:00 committed by GitHub
commit 71cddb3168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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", "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"},

View File

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

View File

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

View File

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

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

View File

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