Fix encoding of not empty interface type

This commit is contained in:
Masaaki Goshima 2021-08-30 13:03:14 +09:00
parent 5c527ab463
commit 92d8dcd13b
10 changed files with 141 additions and 39 deletions

View File

@ -7,6 +7,7 @@ import (
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
)
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
@ -185,16 +186,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
}
}
ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p))
if iface.ptr == nil {
var (
typ *runtime.Type
ifacePtr unsafe.Pointer
)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.typ
}
if ifacePtr == nil {
b = appendNull(ctx, b)
b = appendComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(p))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +235,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -13,15 +13,16 @@ const uintptrSize = 4 << (^uintptr(0) >> 63)
type OpFlags uint16
const (
AnonymousHeadFlags OpFlags = 1 << 0
AnonymousKeyFlags OpFlags = 1 << 1
IndirectFlags OpFlags = 1 << 2
IsTaggedKeyFlags OpFlags = 1 << 3
NilCheckFlags OpFlags = 1 << 4
AddrForMarshalerFlags OpFlags = 1 << 5
IsNextOpPtrTypeFlags OpFlags = 1 << 6
IsNilableTypeFlags OpFlags = 1 << 7
MarshalerContextFlags OpFlags = 1 << 8
AnonymousHeadFlags OpFlags = 1 << 0
AnonymousKeyFlags OpFlags = 1 << 1
IndirectFlags OpFlags = 1 << 2
IsTaggedKeyFlags OpFlags = 1 << 3
NilCheckFlags OpFlags = 1 << 4
AddrForMarshalerFlags OpFlags = 1 << 5
IsNextOpPtrTypeFlags OpFlags = 1 << 6
IsNilableTypeFlags OpFlags = 1 << 7
MarshalerContextFlags OpFlags = 1 << 8
NonEmptyInterfaceFlags OpFlags = 1 << 9
)
type Opcode struct {
@ -743,6 +744,10 @@ func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
}
func newInterfaceCode(ctx *compileContext) *Opcode {
var flag OpFlags
if ctx.typ.NumMethod() > 0 {
flag |= NonEmptyInterfaceFlags
}
return &Opcode{
Op: OpInterface,
Idx: opcodeOffset(ctx.ptrIndex),
@ -750,6 +755,7 @@ func newInterfaceCode(ctx *compileContext) *Opcode {
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
Flags: flag,
}
}

View File

@ -33,6 +33,15 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}

View File

@ -7,6 +7,7 @@ import (
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
)
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
@ -185,16 +186,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
}
}
ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p))
if iface.ptr == nil {
var (
typ *runtime.Type
ifacePtr unsafe.Pointer
)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.typ
}
if ifacePtr == nil {
b = appendNull(ctx, b)
b = appendComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(p))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +235,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -26,6 +26,15 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op)
}

View File

@ -7,6 +7,7 @@ import (
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
)
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
@ -185,16 +186,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
}
}
ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p))
if iface.ptr == nil {
var (
typ *runtime.Type
ifacePtr unsafe.Pointer
)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.typ
}
if ifacePtr == nil {
b = appendNull(ctx, b)
b = appendComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(p))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +235,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -28,6 +28,15 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
}

View File

@ -7,6 +7,7 @@ import (
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
)
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
@ -185,16 +186,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
}
}
ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p))
if iface.ptr == nil {
var (
typ *runtime.Type
ifacePtr unsafe.Pointer
)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.typ
}
if ifacePtr == nil {
b = appendNull(ctx, b)
b = appendComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(p))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +235,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -35,6 +35,15 @@ type emptyInterface struct {
ptr unsafe.Pointer
}
type nonEmptyInterface struct {
itab *struct {
ityp *runtime.Type // static interface type
typ *runtime.Type // dynamic concrete type
// unused fields...
}
ptr unsafe.Pointer
}
func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
}

View File

@ -7,6 +7,7 @@ import (
"unsafe"
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/runtime"
)
func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]byte, error) {
@ -185,16 +186,27 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
}
}
ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p))
if iface.ptr == nil {
var (
typ *runtime.Type
ifacePtr unsafe.Pointer
)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(ptrToUnsafePtr(p))
ifacePtr = iface.ptr
typ = iface.typ
}
if ifacePtr == nil {
b = appendNull(ctx, b)
b = appendComma(ctx, b)
code = code.Next
break
}
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(p))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +235,7 @@ func Run(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet) ([]b
ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr))
store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent))