Skip to content

Commit 71cddb3

Browse files
authored
Merge pull request #57 from goccy/feature/fix-anonymous-fields
Fix recursive anonymous field
2 parents fda849e + 3e1a1ac commit 71cddb3

File tree

7 files changed

+1159
-796
lines changed

7 files changed

+1159
-796
lines changed

cmd/generator/main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func (t opType) fieldToStringTagField() opType {
213213
"int", "int8", "int16", "int32", "int64",
214214
"uint", "uint8", "uint16", "uint32", "uint64",
215215
"float32", "float64", "bool", "string", "bytes",
216-
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText",
216+
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
217217
}
218218
primitiveTypesUpper := []string{}
219219
for _, typ := range primitiveTypes {
@@ -252,7 +252,6 @@ func (t opType) fieldToStringTagField() opType {
252252
{"StructField", "StructFieldIndent", "StructField"},
253253
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
254254
{"StructFieldStringTag", "StructFieldStringTagIndent", "StructField"},
255-
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
256255
{"StructFieldRecursiveEnd", "StructFieldRecursiveEndIndent", "Op"},
257256
{"StructEnd", "StructEndIndent", "StructField"},
258257
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},

decode_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1716,7 +1716,6 @@ func TestMarshalBadUTF8(t *testing.T) {
17161716
}
17171717
}
17181718

1719-
/*
17201719
func TestMarshalNumberZeroVal(t *testing.T) {
17211720
var n json.Number
17221721
out, err := json.Marshal(n)
@@ -1771,6 +1770,7 @@ func TestMarshalEmbeds(t *testing.T) {
17711770
}
17721771
}
17731772

1773+
/*
17741774
func equalError(a, b error) bool {
17751775
if a == nil {
17761776
return b == nil

encode_compile.go

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ func (e *Encoder) isNotExistsField(head *opcode) bool {
785785
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
786786
code := head
787787
var prev *opcode
788+
removedFields := map[*opcode]struct{}{}
788789
for {
789790
if code.op == opStructEnd || code.op == opStructEndIndent {
790791
break
@@ -798,7 +799,7 @@ func (e *Encoder) optimizeAnonymousFields(head *opcode) {
798799
for i := 0; i < diff; i++ {
799800
code.next.decOpcodeIndex()
800801
}
801-
linkPrevToNextField(prev, code)
802+
linkPrevToNextField(code, removedFields)
802803
code = prev
803804
}
804805
}
@@ -815,27 +816,30 @@ type structFieldPair struct {
815816
linked bool
816817
}
817818

818-
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *opcode) map[string][]structFieldPair {
819+
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
819820
anonymousFields := map[string][]structFieldPair{}
820821
f := valueCode
821822
var prevAnonymousField *opcode
823+
removedFields := map[*opcode]struct{}{}
822824
for {
823825
existsKey := tags.existsKey(f.displayKey)
824826
op := f.op.headToAnonymousHead()
825-
if op != f.op {
827+
if existsKey && (f.next.op == opStructFieldPtrAnonymousHeadRecursive || f.next.op == opStructFieldAnonymousHeadRecursive) {
828+
// through
829+
} else if op != f.op {
826830
if existsKey {
827831
f.op = opStructFieldAnonymousHead
828-
} else {
832+
} else if named == "" {
829833
f.op = op
830834
}
831-
} else if f.op == opStructEnd {
835+
} else if named == "" && f.op == opStructEnd {
832836
f.op = opStructAnonymousEnd
833837
} else if existsKey {
834838
diff := f.nextField.displayIdx - f.displayIdx
835839
for i := 0; i < diff; i++ {
836840
f.nextField.decOpcodeIndex()
837841
}
838-
linkPrevToNextField(prevAnonymousField, f)
842+
linkPrevToNextField(f, removedFields)
839843
}
840844

841845
if f.displayKey == "" {
@@ -847,13 +851,14 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
847851
continue
848852
}
849853

850-
anonymousFields[f.displayKey] = append(anonymousFields[f.displayKey], structFieldPair{
854+
key := fmt.Sprintf("%s.%s", named, f.displayKey)
855+
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
851856
prevField: prevAnonymousField,
852857
curField: f,
853858
isTaggedKey: f.isTaggedKey,
854859
})
855860
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
856-
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next) {
861+
for k, v := range e.anonymousStructFieldPairMap(typ, tags, named, f.next) {
857862
anonymousFields[k] = append(anonymousFields[k], v...)
858863
}
859864
}
@@ -867,6 +872,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, value
867872
}
868873

869874
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
875+
removedFields := map[*opcode]struct{}{}
870876
for _, fieldPairs := range anonymousFields {
871877
if len(fieldPairs) == 1 {
872878
continue
@@ -886,7 +892,8 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
886892
for i := 0; i < diff; i++ {
887893
fieldPair.curField.nextField.decOpcodeIndex()
888894
}
889-
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
895+
removedFields[fieldPair.curField] = struct{}{}
896+
linkPrevToNextField(fieldPair.curField, removedFields)
890897
}
891898
fieldPair.linked = true
892899
}
@@ -900,10 +907,11 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
900907
fieldPair.curField.op = opStructFieldAnonymousHead
901908
} else {
902909
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
910+
removedFields[fieldPair.curField] = struct{}{}
903911
for i := 0; i < diff; i++ {
904912
fieldPair.curField.nextField.decOpcodeIndex()
905913
}
906-
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
914+
linkPrevToNextField(fieldPair.curField, removedFields)
907915
}
908916
fieldPair.linked = true
909917
}
@@ -970,7 +978,17 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
970978
}
971979

972980
if field.Anonymous {
973-
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode) {
981+
if valueCode.op == opPtr && valueCode.next.op == opStructFieldRecursive {
982+
valueCode = valueCode.next
983+
valueCode.decOpcodeIndex()
984+
ctx.decIndex()
985+
valueCode.op = opStructFieldPtrHeadRecursive
986+
}
987+
tagKey := ""
988+
if tag.isTaggedKey {
989+
tagKey = tag.key
990+
}
991+
for k, v := range e.anonymousStructFieldPairMap(typ, tags, tagKey, valueCode) {
974992
anonymousFields[k] = append(anonymousFields[k], v...)
975993
}
976994
}
@@ -1015,6 +1033,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
10151033
code.next = fieldCode
10161034
code = e.structField(ctx, fieldCode, valueCode, tag)
10171035
prevField.nextField = fieldCode
1036+
fieldCode.prevField = prevField
10181037
prevField = fieldCode
10191038
}
10201039
fieldIdx++
@@ -1039,6 +1058,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
10391058
indent: ctx.indent,
10401059
nextField: structEndCode,
10411060
}
1061+
structEndCode.prevField = head
10421062
ctx.incIndex()
10431063
if ctx.withIndent {
10441064
head.op = opStructFieldHeadIndent
@@ -1056,14 +1076,13 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
10561076

10571077
if prevField != nil && prevField.nextField == nil {
10581078
prevField.nextField = structEndCode
1079+
structEndCode.prevField = prevField
10591080
}
10601081

10611082
head.end = structEndCode
10621083
code.next = structEndCode
1063-
10641084
e.optimizeConflictAnonymousFields(anonymousFields)
10651085
e.optimizeAnonymousFields(head)
1066-
10671086
ret := (*opcode)(unsafe.Pointer(head))
10681087
compiled.code = ret
10691088

encode_opcode.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type opcode struct {
3333
mapValue *opcode // map value
3434
elem *opcode // array/slice elem
3535
end *opcode // array/slice/struct/map end
36+
prevField *opcode // prev struct field
3637
nextField *opcode // next struct field
3738
next *opcode // next opcode
3839
jmp *compiledCode // for recursive call
@@ -102,6 +103,7 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
102103
copied.mapValue = c.mapValue.copy(codeMap)
103104
copied.elem = c.elem.copy(codeMap)
104105
copied.end = c.end.copy(codeMap)
106+
copied.prevField = c.prevField.copy(codeMap)
105107
copied.nextField = c.nextField.copy(codeMap)
106108
copied.next = c.next.copy(codeMap)
107109
copied.jmp = c.jmp
@@ -309,8 +311,23 @@ func (c *opcode) dump() string {
309311
return strings.Join(codes, "\n")
310312
}
311313

312-
func linkPrevToNextField(prev, cur *opcode) {
313-
prev.nextField = cur.nextField
314+
func prevField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
315+
if _, exists := removedFields[code]; exists {
316+
return prevField(code.prevField, removedFields)
317+
}
318+
return code
319+
}
320+
321+
func nextField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
322+
if _, exists := removedFields[code]; exists {
323+
return nextField(code.nextField, removedFields)
324+
}
325+
return code
326+
}
327+
328+
func linkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
329+
prev := prevField(cur.prevField, removedFields)
330+
prev.nextField = nextField(cur.nextField, removedFields)
314331
code := prev
315332
fcode := cur
316333
for {

0 commit comments

Comments
 (0)