mirror of https://github.com/goccy/go-json.git
Merge pull request #284 from goccy/feature/fix-281
Fix encoding of not empty interface type
This commit is contained in:
commit
e152fc2225
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue