Skip to content

Commit d7e6898

Browse files
committed
fix: panic on embedded struct with recursive
fixes goccy#459
1 parent df897ae commit d7e6898

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

internal/encoder/compiler.go

+24-18
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,13 @@ func getFilteredCodeSetIfNeeded(ctx *RuntimeContext, codeSet *OpcodeSet) (*Opcod
8686

8787
type Compiler struct {
8888
structTypeToCode map[uintptr]*StructCode
89+
anonymousStructTypeToCode map[uintptr]*StructCode
8990
}
9091

9192
func newCompiler() *Compiler {
9293
return &Compiler{
9394
structTypeToCode: map[uintptr]*StructCode{},
95+
anonymousStructTypeToCode: map[uintptr]*StructCode{},
9496
}
9597
}
9698

@@ -169,11 +171,11 @@ func (c *Compiler) typeToCode(typ *runtime.Type) (Code, error) {
169171
return c.sliceCode(typ)
170172
case reflect.Map:
171173
if isPtr {
172-
return c.ptrCode(runtime.PtrTo(typ))
174+
return c.ptrCode(runtime.PtrTo(typ), false)
173175
}
174176
return c.mapCode(typ)
175177
case reflect.Struct:
176-
return c.structCode(typ, isPtr)
178+
return c.structCode(typ, isPtr, false)
177179
case reflect.Int:
178180
return c.intCode(typ, isPtr)
179181
case reflect.Int8:
@@ -208,11 +210,11 @@ func (c *Compiler) typeToCode(typ *runtime.Type) (Code, error) {
208210
if isPtr && typ.Implements(marshalTextType) {
209211
typ = orgType
210212
}
211-
return c.typeToCodeWithPtr(typ, isPtr)
213+
return c.typeToCodeWithPtr(typ, isPtr, false)
212214
}
213215
}
214216

215-
func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr bool) (Code, error) {
217+
func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr, isAnonymous bool) (Code, error) {
216218
switch {
217219
case c.implementsMarshalJSON(typ):
218220
return c.marshalJSONCode(typ)
@@ -221,7 +223,7 @@ func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr bool) (Code, error
221223
}
222224
switch typ.Kind() {
223225
case reflect.Ptr:
224-
return c.ptrCode(typ)
226+
return c.ptrCode(typ, isAnonymous)
225227
case reflect.Slice:
226228
elem := typ.Elem()
227229
if elem.Kind() == reflect.Uint8 {
@@ -236,7 +238,7 @@ func (c *Compiler) typeToCodeWithPtr(typ *runtime.Type, isPtr bool) (Code, error
236238
case reflect.Map:
237239
return c.mapCode(typ)
238240
case reflect.Struct:
239-
return c.structCode(typ, isPtr)
241+
return c.structCode(typ, isPtr, isAnonymous)
240242
case reflect.Interface:
241243
return c.interfaceCode(typ, false)
242244
case reflect.Int:
@@ -424,8 +426,8 @@ func (c *Compiler) marshalTextCode(typ *runtime.Type) (*MarshalTextCode, error)
424426
}, nil
425427
}
426428

427-
func (c *Compiler) ptrCode(typ *runtime.Type) (*PtrCode, error) {
428-
code, err := c.typeToCodeWithPtr(typ.Elem(), true)
429+
func (c *Compiler) ptrCode(typ *runtime.Type, isAnonymous bool) (*PtrCode, error) {
430+
code, err := c.typeToCodeWithPtr(typ.Elem(), true, isAnonymous)
429431
if err != nil {
430432
return nil, err
431433
}
@@ -485,12 +487,12 @@ func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) {
485487
case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
486488
return c.marshalTextCode(typ)
487489
case typ.Kind() == reflect.Map:
488-
return c.ptrCode(runtime.PtrTo(typ))
490+
return c.ptrCode(runtime.PtrTo(typ), false)
489491
default:
490492
// isPtr was originally used to indicate whether the type of top level is pointer.
491493
// However, since the slice/array element is a specification that can get the pointer address, explicitly set isPtr to true.
492494
// See here for related issues: https://github.com/goccy/go-json/issues/370
493-
code, err := c.typeToCodeWithPtr(typ, true)
495+
code, err := c.typeToCodeWithPtr(typ, true, false)
494496
if err != nil {
495497
return nil, err
496498
}
@@ -511,7 +513,7 @@ func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
511513
}
512514
switch typ.Kind() {
513515
case reflect.Ptr:
514-
return c.ptrCode(typ)
516+
return c.ptrCode(typ, false)
515517
case reflect.String:
516518
return c.stringCode(typ, false)
517519
case reflect.Int:
@@ -543,9 +545,9 @@ func (c *Compiler) mapKeyCode(typ *runtime.Type) (Code, error) {
543545
func (c *Compiler) mapValueCode(typ *runtime.Type) (Code, error) {
544546
switch typ.Kind() {
545547
case reflect.Map:
546-
return c.ptrCode(runtime.PtrTo(typ))
548+
return c.ptrCode(runtime.PtrTo(typ), false)
547549
default:
548-
code, err := c.typeToCodeWithPtr(typ, false)
550+
code, err := c.typeToCodeWithPtr(typ, false, false)
549551
if err != nil {
550552
return nil, err
551553
}
@@ -559,16 +561,20 @@ func (c *Compiler) mapValueCode(typ *runtime.Type) (Code, error) {
559561
}
560562
}
561563

562-
func (c *Compiler) structCode(typ *runtime.Type, isPtr bool) (*StructCode, error) {
564+
func (c *Compiler) structCode(typ *runtime.Type, isPtr, isAnonymous bool) (*StructCode, error) {
563565
typeptr := uintptr(unsafe.Pointer(typ))
564-
if code, exists := c.structTypeToCode[typeptr]; exists {
566+
structTypeToCode := c.structTypeToCode
567+
if isAnonymous {
568+
structTypeToCode = c.anonymousStructTypeToCode
569+
}
570+
if code, exists := structTypeToCode[typeptr]; exists {
565571
derefCode := *code
566572
derefCode.isRecursive = true
567573
return &derefCode, nil
568574
}
569575
indirect := runtime.IfaceIndir(typ)
570576
code := &StructCode{typ: typ, isPtr: isPtr, isIndirect: indirect}
571-
c.structTypeToCode[typeptr] = code
577+
structTypeToCode[typeptr] = code
572578

573579
fieldNum := typ.NumField()
574580
tags := c.typeToStructTags(typ)
@@ -613,7 +619,7 @@ func (c *Compiler) structCode(typ *runtime.Type, isPtr bool) (*StructCode, error
613619
if !code.disableIndirectConversion && !indirect && isPtr {
614620
code.enableIndirect()
615621
}
616-
delete(c.structTypeToCode, typeptr)
622+
delete(structTypeToCode, typeptr)
617623
return code, nil
618624
}
619625

@@ -680,7 +686,7 @@ func (c *Compiler) structFieldCode(structCode *StructCode, tag *runtime.StructTa
680686
fieldCode.isAddrForMarshaler = true
681687
fieldCode.isNilCheck = false
682688
default:
683-
code, err := c.typeToCodeWithPtr(fieldType, isPtr)
689+
code, err := c.typeToCodeWithPtr(fieldType, isPtr, fieldCode.isAnonymous)
684690
if err != nil {
685691
return nil, err
686692
}

0 commit comments

Comments
 (0)