Merge pull request #284 from goccy/feature/fix-281

Fix encoding of not empty interface type
This commit is contained in:
Masaaki Goshima 2021-08-30 14:02:16 +09:00 committed by GitHub
commit e152fc2225
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 172 additions and 39 deletions

View File

@ -2108,3 +2108,29 @@ func TestEmbeddedNotFirstField(t *testing.T) {
t.Fatalf("failed to encode embedded structure. expected = %q but got %q", expected, got)
}
}
type implementedMethodIface interface {
M()
}
type implementedIfaceType struct {
A int
B string
}
func (implementedIfaceType) M() {}
func TestImplementedMethodInterfaceType(t *testing.T) {
data := []implementedIfaceType{implementedIfaceType{}}
expected, err := stdjson.Marshal(data)
if err != nil {
t.Fatal(err)
}
got, err := json.Marshal(data)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(expected, got) {
t.Fatalf("failed to encode implemented method interface type. expected:[%q] but got:[%q]", expected, got)
}
}

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,28 @@ 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
)
up := ptrToUnsafePtr(p)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(up)
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(up)
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, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +236,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,28 @@ 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
)
up := ptrToUnsafePtr(p)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(up)
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(up)
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, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +236,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,28 @@ 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
)
up := ptrToUnsafePtr(p)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(up)
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(up)
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, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +236,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,28 @@ 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
)
up := ptrToUnsafePtr(p)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(up)
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(up)
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, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +236,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,28 @@ 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
)
up := ptrToUnsafePtr(p)
if code.Flags&encoder.NonEmptyInterfaceFlags != 0 {
iface := (*nonEmptyInterface)(up)
ifacePtr = iface.ptr
typ = iface.itab.typ
} else {
iface := (*emptyInterface)(up)
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, up)
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
if err != nil {
return nil, err
}
@ -223,7 +236,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))