Fix interface operation

This commit is contained in:
Masaaki Goshima 2021-01-24 23:27:23 +09:00
parent d59d465042
commit 75f34df1c6
4 changed files with 81 additions and 166 deletions

View File

@ -131,7 +131,7 @@ func (c *opcode) totalLength() int {
var idx int var idx int
for code := c; code.op != opEnd; { for code := c; code.op != opEnd; {
idx = int(code.idx / uintptrSize) idx = int(code.idx / uintptrSize)
if code.op == opInterfaceEnd || code.op == opStructFieldRecursiveEnd { if code.op == opStructFieldRecursiveEnd {
break break
} }
switch code.op.codeType() { switch code.op.codeType() {

View File

@ -207,65 +207,46 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) (
} }
} }
ctx.seenPtr = append(ctx.seenPtr, ptr) ctx.seenPtr = append(ctx.seenPtr, ptr)
v := e.ptrToInterface(code, ptr) iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) if iface == nil || iface.ptr == nil {
rv := reflect.ValueOf(v)
if rv.IsNil() {
b = encodeNull(b) b = encodeNull(b)
b = encodeComma(b) b = encodeComma(b)
code = code.next code = code.next
break break
} }
vv := rv.Interface() ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
header := (*interfaceHeader)(unsafe.Pointer(&vv)) ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if header.typ.Kind() == reflect.Ptr {
if rv.Elem().IsNil() {
b = encodeNull(b)
b = encodeComma(b)
code = code.next
break
}
}
c, err := e.compileHead(&encodeCompileContext{
typ: header.typ,
root: code.root,
indent: code.indent,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
beforeLastCode := c.beforeLastCode()
lastCode := beforeLastCode.next totalLength := uintptr(codeSet.codeLength)
lastCode.idx = beforeLastCode.idx + uintptrSize nextTotalLength := uintptr(ifaceCodeSet.codeLength)
totalLength := uintptr(code.totalLength())
nextTotalLength := uintptr(c.totalLength())
curlen := uintptr(len(ctx.ptrs)) curlen := uintptr(len(ctx.ptrs))
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
newLen := offsetNum + totalLength + nextTotalLength newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen { if curlen < newLen {
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
} }
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr oldPtrs := ctx.ptrs
store(ctxptr, 0, uintptr(header.ptr)) newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
store(ctxptr, lastCode.idx, oldOffset) newPtrs[0] = uintptr(iface.ptr)
// link lastCode ( opInterfaceEnd ) => code.next ctx.ptrs = newPtrs
lastCode.op = opInterfaceEnd
lastCode.next = code.next
code = c bb, err := e.runEscaped(ctx, b, ifaceCodeSet)
recursiveLevel++ if err != nil {
case opInterfaceEnd: return nil, err
recursiveLevel-- }
// restore ctxptr
offset := load(ctxptr, code.idx) ctx.ptrs = oldPtrs
ctxptr = ctx.ptr() + offset ctxptr = ctx.ptr()
ptrOffset = offset ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
b = bb
code = code.next code = code.next
case opMarshalJSON: case opMarshalJSON:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)

View File

@ -12,8 +12,6 @@ import (
) )
func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) { func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
recursiveLevel := 0
var seenPtr map[uintptr]struct{}
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.ptr() ctxptr := ctx.ptr()
code := codeSet.code code := codeSet.code
@ -164,83 +162,52 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet
code = code.next code = code.next
break break
} }
if seenPtr == nil { for _, seen := range ctx.seenPtr {
seenPtr = map[uintptr]struct{}{} if ptr == seen {
}
if _, exists := seenPtr[ptr]; exists {
return nil, errUnsupportedValue(code, ptr) return nil, errUnsupportedValue(code, ptr)
} }
seenPtr[ptr] = struct{}{} }
v := e.ptrToInterface(code, ptr) ctx.seenPtr = append(ctx.seenPtr, ptr)
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
rv := reflect.ValueOf(v) if iface == nil || iface.ptr == nil {
if rv.IsNil() {
b = encodeNull(b) b = encodeNull(b)
b = encodeIndentComma(b) b = encodeIndentComma(b)
code = code.next code = code.next
break break
} }
vv := rv.Interface() ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
header := (*interfaceHeader)(unsafe.Pointer(&vv)) ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
typ := header.typ
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
var c *opcode
if typ.Kind() == reflect.Map {
code, err := e.compileMap(&encodeCompileContext{
typ: typ,
root: code.root,
indent: code.indent,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
}, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c = code
} else { totalLength := uintptr(codeSet.codeLength)
code, err := e.compile(&encodeCompileContext{ nextTotalLength := uintptr(ifaceCodeSet.codeLength)
typ: typ,
root: code.root,
indent: code.indent,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
c = code
}
beforeLastCode := c.beforeLastCode()
lastCode := beforeLastCode.next
lastCode.idx = beforeLastCode.idx + uintptrSize
totalLength := uintptr(code.totalLength())
nextTotalLength := uintptr(c.totalLength())
curlen := uintptr(len(ctx.ptrs)) curlen := uintptr(len(ctx.ptrs))
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
newLen := offsetNum + totalLength + nextTotalLength newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen { if curlen < newLen {
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
} }
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr oldPtrs := ctx.ptrs
store(ctxptr, 0, uintptr(header.ptr)) newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
store(ctxptr, lastCode.idx, oldOffset) newPtrs[0] = uintptr(iface.ptr)
// link lastCode ( opInterfaceEnd ) => code.next ctx.ptrs = newPtrs
lastCode.op = opInterfaceEnd
lastCode.next = code.next
code = c bb, err := e.runEscaped(ctx, b, ifaceCodeSet)
recursiveLevel++ if err != nil {
case opInterfaceEnd: return nil, err
recursiveLevel-- }
// restore ctxptr
offset := load(ctxptr, code.idx) ctx.ptrs = oldPtrs
ctxptr = ctx.ptr() + offset ctxptr = ctx.ptr()
ptrOffset = offset ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
b = bb
code = code.next code = code.next
case opMarshalJSON: case opMarshalJSON:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)

View File

@ -12,8 +12,6 @@ import (
) )
func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) { func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
recursiveLevel := 0
var seenPtr map[uintptr]struct{}
ptrOffset := uintptr(0) ptrOffset := uintptr(0)
ctxptr := ctx.ptr() ctxptr := ctx.ptr()
code := codeSet.code code := codeSet.code
@ -164,83 +162,52 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcode
code = code.next code = code.next
break break
} }
if seenPtr == nil { for _, seen := range ctx.seenPtr {
seenPtr = map[uintptr]struct{}{} if ptr == seen {
}
if _, exists := seenPtr[ptr]; exists {
return nil, errUnsupportedValue(code, ptr) return nil, errUnsupportedValue(code, ptr)
} }
seenPtr[ptr] = struct{}{} }
v := e.ptrToInterface(code, ptr) ctx.seenPtr = append(ctx.seenPtr, ptr)
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v)) iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
rv := reflect.ValueOf(v) if iface == nil || iface.ptr == nil {
if rv.IsNil() {
b = encodeNull(b) b = encodeNull(b)
b = encodeIndentComma(b) b = encodeIndentComma(b)
code = code.next code = code.next
break break
} }
vv := rv.Interface() ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
header := (*interfaceHeader)(unsafe.Pointer(&vv)) ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
typ := header.typ
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
var c *opcode
if typ.Kind() == reflect.Map {
code, err := e.compileMap(&encodeCompileContext{
typ: typ,
root: code.root,
indent: code.indent,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
}, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
c = code
} else { totalLength := uintptr(codeSet.codeLength)
code, err := e.compile(&encodeCompileContext{ nextTotalLength := uintptr(ifaceCodeSet.codeLength)
typ: typ,
root: code.root,
indent: code.indent,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
c = code
}
beforeLastCode := c.beforeLastCode()
lastCode := beforeLastCode.next
lastCode.idx = beforeLastCode.idx + uintptrSize
totalLength := uintptr(code.totalLength())
nextTotalLength := uintptr(c.totalLength())
curlen := uintptr(len(ctx.ptrs)) curlen := uintptr(len(ctx.ptrs))
offsetNum := ptrOffset / uintptrSize offsetNum := ptrOffset / uintptrSize
oldOffset := ptrOffset
ptrOffset += totalLength * uintptrSize
newLen := offsetNum + totalLength + nextTotalLength newLen := offsetNum + totalLength + nextTotalLength
if curlen < newLen { if curlen < newLen {
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...) ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
} }
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr oldPtrs := ctx.ptrs
store(ctxptr, 0, uintptr(header.ptr)) newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
store(ctxptr, lastCode.idx, oldOffset) newPtrs[0] = uintptr(iface.ptr)
// link lastCode ( opInterfaceEnd ) => code.next ctx.ptrs = newPtrs
lastCode.op = opInterfaceEnd
lastCode.next = code.next
code = c bb, err := e.runEscaped(ctx, b, ifaceCodeSet)
recursiveLevel++ if err != nil {
case opInterfaceEnd: return nil, err
recursiveLevel-- }
// restore ctxptr
offset := load(ctxptr, code.idx) ctx.ptrs = oldPtrs
ctxptr = ctx.ptr() + offset ctxptr = ctx.ptr()
ptrOffset = offset ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
b = bb
code = code.next code = code.next
case opMarshalJSON: case opMarshalJSON:
ptr := load(ctxptr, code.idx) ptr := load(ctxptr, code.idx)