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) 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" "unsafe"
"github.com/goccy/go-json/internal/encoder" "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) { 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) ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p)) var (
if iface.ptr == nil { 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 = appendNull(ctx, b)
b = appendComma(ctx, b) b = appendComma(ctx, b)
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, up)
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err 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 ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr)) store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset) store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent)) storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

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

View File

@ -33,6 +33,15 @@ type emptyInterface struct {
ptr unsafe.Pointer 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 { func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op) return fmt.Errorf("encoder: opcode %s has not been implemented", op)
} }

View File

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "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) { 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) ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p)) var (
if iface.ptr == nil { 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 = appendNull(ctx, b)
b = appendComma(ctx, b) b = appendComma(ctx, b)
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, up)
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err 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 ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr)) store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset) store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent)) storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -26,6 +26,15 @@ type emptyInterface struct {
ptr unsafe.Pointer 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 { func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder: opcode %s has not been implemented", op) return fmt.Errorf("encoder: opcode %s has not been implemented", op)
} }

View File

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "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) { 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) ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p)) var (
if iface.ptr == nil { 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 = appendNull(ctx, b)
b = appendComma(ctx, b) b = appendComma(ctx, b)
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, up)
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err 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 ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr)) store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset) store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent)) storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -28,6 +28,15 @@ type emptyInterface struct {
ptr unsafe.Pointer 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 { func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op) return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
} }

View File

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "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) { 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) ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p)) var (
if iface.ptr == nil { 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 = appendNull(ctx, b)
b = appendComma(ctx, b) b = appendComma(ctx, b)
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, up)
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err 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 ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr)) store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset) store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent)) storeIndent(ctxptr, end, uintptr(oldBaseIndent))

View File

@ -35,6 +35,15 @@ type emptyInterface struct {
ptr unsafe.Pointer 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 { func errUnimplementedOp(op encoder.OpType) error {
return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op) return fmt.Errorf("encoder (indent): opcode %s has not been implemented", op)
} }

View File

@ -7,6 +7,7 @@ import (
"unsafe" "unsafe"
"github.com/goccy/go-json/internal/encoder" "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) { 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) ctx.SeenPtr = append(ctx.SeenPtr, p)
iface := (*emptyInterface)(ptrToUnsafePtr(p)) var (
if iface.ptr == nil { 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 = appendNull(ctx, b)
b = appendComma(ctx, b) b = appendComma(ctx, b)
code = code.Next code = code.Next
break break
} }
ctx.KeepRefs = append(ctx.KeepRefs, up)
ctx.KeepRefs = append(ctx.KeepRefs, unsafe.Pointer(iface)) ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(typ)))
ifaceCodeSet, err := encoder.CompileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
if err != nil { if err != nil {
return nil, err 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 ctxptr = ctx.Ptr() + ptrOffset // assign new ctxptr
end := ifaceCodeSet.EndCode end := ifaceCodeSet.EndCode
store(ctxptr, c.Idx, uintptr(iface.ptr)) store(ctxptr, c.Idx, uintptr(ifacePtr))
store(ctxptr, end.Idx, oldOffset) store(ctxptr, end.Idx, oldOffset)
store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next))) store(ctxptr, end.ElemIdx, uintptr(unsafe.Pointer(code.Next)))
storeIndent(ctxptr, end, uintptr(oldBaseIndent)) storeIndent(ctxptr, end, uintptr(oldBaseIndent))