mirror of https://github.com/goccy/go-json.git
Merge pull request #37 from goccy/feature/fix-cycle-pointer
Fix cycle pointer value
This commit is contained in:
commit
a257c9b964
|
@ -478,7 +478,8 @@ func (c *interfaceCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||||
|
|
||||||
type recursiveCode struct {
|
type recursiveCode struct {
|
||||||
*opcodeHeader
|
*opcodeHeader
|
||||||
jmp *compiledCode
|
jmp *compiledCode
|
||||||
|
seenPtr uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||||
|
@ -489,7 +490,7 @@ func (c *recursiveCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||||
if code, exists := codeMap[addr]; exists {
|
if code, exists := codeMap[addr]; exists {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
recur := &recursiveCode{}
|
recur := &recursiveCode{seenPtr: c.seenPtr}
|
||||||
code := (*opcode)(unsafe.Pointer(recur))
|
code := (*opcode)(unsafe.Pointer(recur))
|
||||||
codeMap[addr] = code
|
codeMap[addr] = code
|
||||||
|
|
||||||
|
|
|
@ -1158,3 +1158,56 @@ func TestEncodePointerString(t *testing.T) {
|
||||||
t.Fatalf("*N = %d; want 42", *back.N)
|
t.Fatalf("*N = %d; want 42", *back.N)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SamePointerNoCycle struct {
|
||||||
|
Ptr1, Ptr2 *SamePointerNoCycle
|
||||||
|
}
|
||||||
|
|
||||||
|
var samePointerNoCycle = &SamePointerNoCycle{}
|
||||||
|
|
||||||
|
type PointerCycle struct {
|
||||||
|
Ptr *PointerCycle
|
||||||
|
}
|
||||||
|
|
||||||
|
var pointerCycle = &PointerCycle{}
|
||||||
|
|
||||||
|
type PointerCycleIndirect struct {
|
||||||
|
Ptrs []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pointerCycleIndirect = &PointerCycleIndirect{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ptr := &SamePointerNoCycle{}
|
||||||
|
samePointerNoCycle.Ptr1 = ptr
|
||||||
|
samePointerNoCycle.Ptr2 = ptr
|
||||||
|
|
||||||
|
pointerCycle.Ptr = pointerCycle
|
||||||
|
pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSamePointerNoCycle(t *testing.T) {
|
||||||
|
if _, err := json.Marshal(samePointerNoCycle); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var unsupportedValues = []interface{}{
|
||||||
|
math.NaN(),
|
||||||
|
math.Inf(-1),
|
||||||
|
math.Inf(1),
|
||||||
|
pointerCycle,
|
||||||
|
pointerCycleIndirect,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedValues(t *testing.T) {
|
||||||
|
for _, v := range unsupportedValues {
|
||||||
|
if _, err := json.Marshal(v); err != nil {
|
||||||
|
if _, ok := err.(*json.UnsupportedValueError); !ok {
|
||||||
|
t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("for %v, expected error", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
19
encode_vm.go
19
encode_vm.go
|
@ -12,6 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Encoder) run(code *opcode) error {
|
func (e *Encoder) run(code *opcode) error {
|
||||||
|
seenPtr := map[uintptr]struct{}{}
|
||||||
for {
|
for {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opPtr:
|
case opPtr:
|
||||||
|
@ -80,6 +81,13 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
typ: ifaceCode.typ,
|
typ: ifaceCode.typ,
|
||||||
ptr: unsafe.Pointer(ptr),
|
ptr: unsafe.Pointer(ptr),
|
||||||
}))
|
}))
|
||||||
|
if _, exists := seenPtr[ptr]; exists {
|
||||||
|
return &UnsupportedValueError{
|
||||||
|
Value: reflect.ValueOf(v),
|
||||||
|
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seenPtr[ptr] = struct{}{}
|
||||||
rv := reflect.ValueOf(v)
|
rv := reflect.ValueOf(v)
|
||||||
if rv.IsNil() {
|
if rv.IsNil() {
|
||||||
e.encodeNull()
|
e.encodeNull()
|
||||||
|
@ -498,6 +506,17 @@ func (e *Encoder) run(code *opcode) error {
|
||||||
code = c.next
|
code = c.next
|
||||||
case opStructFieldRecursive:
|
case opStructFieldRecursive:
|
||||||
recursive := code.toRecursiveCode()
|
recursive := code.toRecursiveCode()
|
||||||
|
if recursive.seenPtr != 0 && recursive.seenPtr == recursive.ptr {
|
||||||
|
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||||
|
typ: code.typ,
|
||||||
|
ptr: unsafe.Pointer(recursive.ptr),
|
||||||
|
}))
|
||||||
|
return &UnsupportedValueError{
|
||||||
|
Value: reflect.ValueOf(v),
|
||||||
|
Str: fmt.Sprintf("encountered a cycle via %s", code.typ),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recursive.seenPtr = recursive.ptr
|
||||||
if err := e.run(newRecursiveCode(recursive)); err != nil {
|
if err := e.run(newRecursiveCode(recursive)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue