diff --git a/encode.go b/encode.go index f41114e..d3590d5 100644 --- a/encode.go +++ b/encode.go @@ -22,6 +22,7 @@ type Encoder struct { indent int structTypeToCompiledCode map[uintptr]*compiledCode structTypeToCompiledIndentCode map[uintptr]*compiledCode + seenPtr map[uintptr]struct{} } type compiledCode struct { @@ -67,6 +68,7 @@ func init() { buf: make([]byte, 0, bufSize), structTypeToCompiledCode: map[uintptr]*compiledCode{}, structTypeToCompiledIndentCode: map[uintptr]*compiledCode{}, + seenPtr: map[uintptr]struct{}{}, } }, } diff --git a/encode_vm.go b/encode_vm.go index 71e42ff..ab81e07 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -80,6 +80,13 @@ func (e *Encoder) run(code *opcode) error { typ: ifaceCode.typ, ptr: unsafe.Pointer(ptr), })) + if _, exists := e.seenPtr[ptr]; exists { + return &UnsupportedValueError{ + Value: reflect.ValueOf(v), + Str: fmt.Sprintf("encountered a cycle via %s", code.typ), + } + } + e.seenPtr[ptr] = struct{}{} rv := reflect.ValueOf(v) if rv.IsNil() { e.encodeNull() @@ -498,6 +505,17 @@ func (e *Encoder) run(code *opcode) error { code = c.next case opStructFieldRecursive: recursive := code.toRecursiveCode() + if _, exists := e.seenPtr[recursive.ptr]; exists { + 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), + } + } + e.seenPtr[recursive.ptr] = struct{}{} if err := e.run(newRecursiveCode(recursive)); err != nil { return err }