go-json/internal/encoder/opcode.go

795 lines
17 KiB
Go

package encoder
import (
"fmt"
"strings"
"unsafe"
"github.com/goccy/go-json/internal/runtime"
)
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
NonEmptyInterfaceFlags OpFlags = 1 << 9
)
type Opcode struct {
Op OpType // operation type
Idx uint32 // offset to access ptr
Next *Opcode // next opcode
End *Opcode // array/slice/struct/map end
NextField *Opcode // next struct field
Key string // struct field key
Offset uint32 // offset size from struct header
PtrNum uint8 // pointer number: e.g. double pointer is 2.
NumBitSize uint8
Flags OpFlags
Type *runtime.Type // go type
PrevField *Opcode // prev struct field
Jmp *CompiledCode // for recursive call
ElemIdx uint32 // offset to access array/slice/map elem
Length uint32 // offset to access slice/map length or array length
MapIter uint32 // offset to access map iterator
MapPos uint32 // offset to access position list for sorted map
Indent uint32 // indent number
Size uint32 // array/slice elem size
DisplayIdx uint32 // opcode index
DisplayKey string // key text to display
}
func (c *Opcode) Validate() error {
var prevIdx uint32
for code := c; !code.IsEnd(); {
if prevIdx != 0 {
if code.DisplayIdx != prevIdx+1 {
return fmt.Errorf(
"invalid index. previous display index is %d but next is %d. dump = %s",
prevIdx, code.DisplayIdx, c.Dump(),
)
}
}
prevIdx = code.DisplayIdx
code = code.IterNext()
}
return nil
}
func (c *Opcode) IterNext() *Opcode {
if c == nil {
return nil
}
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
return c.End
default:
return c.Next
}
}
func (c *Opcode) IsEnd() bool {
if c == nil {
return true
}
return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
}
func (c *Opcode) IsStructHeadOp() bool {
if c == nil {
return false
}
return strings.Contains(c.Op.String(), "Head")
}
func (c *Opcode) IsRecursiveOp() bool {
if c == nil {
return false
}
return strings.Contains(c.Op.String(), "Recursive")
}
func (c *Opcode) MaxIdx() uint32 {
max := uint32(0)
for _, value := range []uint32{
c.Idx,
c.ElemIdx,
c.Length,
c.MapIter,
c.MapPos,
c.Size,
} {
if max < value {
max = value
}
}
return max
}
func (c *Opcode) ToHeaderType(isString bool) OpType {
switch c.Op {
case OpInt:
if isString {
return OpStructHeadIntString
}
return OpStructHeadInt
case OpIntPtr:
if isString {
return OpStructHeadIntPtrString
}
return OpStructHeadIntPtr
case OpUint:
if isString {
return OpStructHeadUintString
}
return OpStructHeadUint
case OpUintPtr:
if isString {
return OpStructHeadUintPtrString
}
return OpStructHeadUintPtr
case OpFloat32:
if isString {
return OpStructHeadFloat32String
}
return OpStructHeadFloat32
case OpFloat32Ptr:
if isString {
return OpStructHeadFloat32PtrString
}
return OpStructHeadFloat32Ptr
case OpFloat64:
if isString {
return OpStructHeadFloat64String
}
return OpStructHeadFloat64
case OpFloat64Ptr:
if isString {
return OpStructHeadFloat64PtrString
}
return OpStructHeadFloat64Ptr
case OpString:
if isString {
return OpStructHeadStringString
}
return OpStructHeadString
case OpStringPtr:
if isString {
return OpStructHeadStringPtrString
}
return OpStructHeadStringPtr
case OpNumber:
if isString {
return OpStructHeadNumberString
}
return OpStructHeadNumber
case OpNumberPtr:
if isString {
return OpStructHeadNumberPtrString
}
return OpStructHeadNumberPtr
case OpBool:
if isString {
return OpStructHeadBoolString
}
return OpStructHeadBool
case OpBoolPtr:
if isString {
return OpStructHeadBoolPtrString
}
return OpStructHeadBoolPtr
case OpBytes:
return OpStructHeadBytes
case OpBytesPtr:
return OpStructHeadBytesPtr
case OpMap:
return OpStructHeadMap
case OpMapPtr:
c.Op = OpMap
return OpStructHeadMapPtr
case OpArray:
return OpStructHeadArray
case OpArrayPtr:
c.Op = OpArray
return OpStructHeadArrayPtr
case OpSlice:
return OpStructHeadSlice
case OpSlicePtr:
c.Op = OpSlice
return OpStructHeadSlicePtr
case OpMarshalJSON:
return OpStructHeadMarshalJSON
case OpMarshalJSONPtr:
return OpStructHeadMarshalJSONPtr
case OpMarshalText:
return OpStructHeadMarshalText
case OpMarshalTextPtr:
return OpStructHeadMarshalTextPtr
}
return OpStructHead
}
func (c *Opcode) ToFieldType(isString bool) OpType {
switch c.Op {
case OpInt:
if isString {
return OpStructFieldIntString
}
return OpStructFieldInt
case OpIntPtr:
if isString {
return OpStructFieldIntPtrString
}
return OpStructFieldIntPtr
case OpUint:
if isString {
return OpStructFieldUintString
}
return OpStructFieldUint
case OpUintPtr:
if isString {
return OpStructFieldUintPtrString
}
return OpStructFieldUintPtr
case OpFloat32:
if isString {
return OpStructFieldFloat32String
}
return OpStructFieldFloat32
case OpFloat32Ptr:
if isString {
return OpStructFieldFloat32PtrString
}
return OpStructFieldFloat32Ptr
case OpFloat64:
if isString {
return OpStructFieldFloat64String
}
return OpStructFieldFloat64
case OpFloat64Ptr:
if isString {
return OpStructFieldFloat64PtrString
}
return OpStructFieldFloat64Ptr
case OpString:
if isString {
return OpStructFieldStringString
}
return OpStructFieldString
case OpStringPtr:
if isString {
return OpStructFieldStringPtrString
}
return OpStructFieldStringPtr
case OpNumber:
if isString {
return OpStructFieldNumberString
}
return OpStructFieldNumber
case OpNumberPtr:
if isString {
return OpStructFieldNumberPtrString
}
return OpStructFieldNumberPtr
case OpBool:
if isString {
return OpStructFieldBoolString
}
return OpStructFieldBool
case OpBoolPtr:
if isString {
return OpStructFieldBoolPtrString
}
return OpStructFieldBoolPtr
case OpBytes:
return OpStructFieldBytes
case OpBytesPtr:
return OpStructFieldBytesPtr
case OpMap:
return OpStructFieldMap
case OpMapPtr:
c.Op = OpMap
return OpStructFieldMapPtr
case OpArray:
return OpStructFieldArray
case OpArrayPtr:
c.Op = OpArray
return OpStructFieldArrayPtr
case OpSlice:
return OpStructFieldSlice
case OpSlicePtr:
c.Op = OpSlice
return OpStructFieldSlicePtr
case OpMarshalJSON:
return OpStructFieldMarshalJSON
case OpMarshalJSONPtr:
return OpStructFieldMarshalJSONPtr
case OpMarshalText:
return OpStructFieldMarshalText
case OpMarshalTextPtr:
return OpStructFieldMarshalTextPtr
}
return OpStructField
}
func newOpCode(ctx *compileContext, op OpType) *Opcode {
return newOpCodeWithNext(ctx, op, newEndOp(ctx))
}
func opcodeOffset(idx int) uint32 {
return uint32(idx) * uintptrSize
}
func copyOpcode(code *Opcode) *Opcode {
codeMap := map[uintptr]*Opcode{}
return code.copy(codeMap)
}
func setTotalLengthToInterfaceOp(code *Opcode) {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
if c.Op == OpInterface {
c.Length = uint32(code.TotalLength())
}
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
}
func ToEndCode(code *Opcode) *Opcode {
c := code
for c.Op != OpEnd && c.Op != OpInterfaceEnd {
switch c.Op.CodeType() {
case CodeArrayElem, CodeSliceElem, CodeMapKey:
c = c.End
default:
c = c.Next
}
}
return c
}
func copyToInterfaceOpcode(code *Opcode) *Opcode {
copied := copyOpcode(code)
c := copied
c = ToEndCode(c)
c.Idx += uintptrSize
c.ElemIdx = c.Idx + uintptrSize
c.Length = c.Idx + 2*uintptrSize
c.Op = OpInterfaceEnd
return copied
}
func newOpCodeWithNext(ctx *compileContext, op OpType, next *Opcode) *Opcode {
return &Opcode{
Op: op,
Idx: opcodeOffset(ctx.ptrIndex),
Next: next,
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
}
func newEndOp(ctx *compileContext) *Opcode {
return newOpCodeWithNext(ctx, OpEnd, nil)
}
func (c *Opcode) copy(codeMap map[uintptr]*Opcode) *Opcode {
if c == nil {
return nil
}
addr := uintptr(unsafe.Pointer(c))
if code, exists := codeMap[addr]; exists {
return code
}
copied := &Opcode{
Op: c.Op,
Key: c.Key,
PtrNum: c.PtrNum,
NumBitSize: c.NumBitSize,
Flags: c.Flags,
Idx: c.Idx,
Offset: c.Offset,
Type: c.Type,
DisplayIdx: c.DisplayIdx,
DisplayKey: c.DisplayKey,
ElemIdx: c.ElemIdx,
Length: c.Length,
MapIter: c.MapIter,
MapPos: c.MapPos,
Size: c.Size,
Indent: c.Indent,
}
codeMap[addr] = copied
copied.End = c.End.copy(codeMap)
copied.PrevField = c.PrevField.copy(codeMap)
copied.NextField = c.NextField.copy(codeMap)
copied.Next = c.Next.copy(codeMap)
copied.Jmp = c.Jmp
return copied
}
func (c *Opcode) BeforeLastCode() *Opcode {
code := c
for {
nextCode := code.IterNext()
if nextCode.IsEnd() {
return code
}
code = nextCode
}
}
func (c *Opcode) TotalLength() int {
var idx int
code := c
for !code.IsEnd() {
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
if code.Op == OpRecursiveEnd {
break
}
code = code.IterNext()
}
maxIdx := int(code.MaxIdx() / uintptrSize)
if idx < maxIdx {
idx = maxIdx
}
return idx + 1
}
func (c *Opcode) decOpcodeIndex() {
for code := c; !code.IsEnd(); {
code.DisplayIdx--
if code.Idx > 0 {
code.Idx -= uintptrSize
}
if code.ElemIdx > 0 {
code.ElemIdx -= uintptrSize
}
if code.MapIter > 0 {
code.MapIter -= uintptrSize
}
if code.Length > 0 && code.Op.CodeType() != CodeArrayHead && code.Op.CodeType() != CodeArrayElem {
code.Length -= uintptrSize
}
code = code.IterNext()
}
}
func (c *Opcode) decIndent() {
for code := c; !code.IsEnd(); {
code.Indent--
code = code.IterNext()
}
}
func (c *Opcode) dumpHead(code *Opcode) string {
var length uint32
if code.Op.CodeType() == CodeArrayHead {
length = code.Length
} else {
length = code.Length / uintptrSize
}
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
length,
)
}
func (c *Opcode) dumpMapHead(code *Opcode) string {
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
code.Length/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) dumpMapEnd(code *Opcode) string {
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][mapPos:%d][length:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapPos/uintptrSize,
code.Length/uintptrSize,
)
}
func (c *Opcode) dumpElem(code *Opcode) string {
var length uint32
if code.Op.CodeType() == CodeArrayElem {
length = code.Length
} else {
length = code.Length / uintptrSize
}
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
length,
code.Size,
)
}
func (c *Opcode) dumpField(code *Opcode) string {
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][key:%s][offset:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.DisplayKey,
code.Offset,
)
}
func (c *Opcode) dumpKey(code *Opcode) string {
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][elemIdx:%d][length:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.ElemIdx/uintptrSize,
code.Length/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) dumpValue(code *Opcode) string {
return fmt.Sprintf(
`[%02d]%s%s ([idx:%d][mapIter:%d])`,
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
code.MapIter/uintptrSize,
)
}
func (c *Opcode) Dump() string {
codes := []string{}
for code := c; !code.IsEnd(); {
switch code.Op.CodeType() {
case CodeSliceHead:
codes = append(codes, c.dumpHead(code))
code = code.Next
case CodeMapHead:
codes = append(codes, c.dumpMapHead(code))
code = code.Next
case CodeArrayElem, CodeSliceElem:
codes = append(codes, c.dumpElem(code))
code = code.End
case CodeMapKey:
codes = append(codes, c.dumpKey(code))
code = code.End
case CodeMapValue:
codes = append(codes, c.dumpValue(code))
code = code.Next
case CodeMapEnd:
codes = append(codes, c.dumpMapEnd(code))
code = code.Next
case CodeStructField:
codes = append(codes, c.dumpField(code))
code = code.Next
case CodeStructEnd:
codes = append(codes, c.dumpField(code))
code = code.Next
default:
codes = append(codes, fmt.Sprintf(
"[%02d]%s%s ([idx:%d])",
code.DisplayIdx,
strings.Repeat("-", int(code.Indent)),
code.Op,
code.Idx/uintptrSize,
))
code = code.Next
}
}
return strings.Join(codes, "\n")
}
func prevField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
if _, exists := removedFields[code]; exists {
return prevField(code.PrevField, removedFields)
}
return code
}
func nextField(code *Opcode, removedFields map[*Opcode]struct{}) *Opcode {
if _, exists := removedFields[code]; exists {
return nextField(code.NextField, removedFields)
}
return code
}
func linkPrevToNextField(cur *Opcode, removedFields map[*Opcode]struct{}) {
prev := prevField(cur.PrevField, removedFields)
prev.NextField = nextField(cur.NextField, removedFields)
code := prev
for {
nextCode := code.IterNext()
if nextCode == cur {
code.Next = cur.NextField
break
} else if nextCode.Op == OpEnd {
break
}
code = nextCode
}
}
func newSliceHeaderCode(ctx *compileContext) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpSlice,
Idx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Length: length,
Indent: ctx.indent,
}
}
func newSliceElemCode(ctx *compileContext, head *Opcode, size uintptr) *Opcode {
return &Opcode{
Op: OpSliceElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
Indent: ctx.indent,
Size: uint32(size),
}
}
func newArrayHeaderCode(ctx *compileContext, alen int) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpArray,
Idx: idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Indent: ctx.indent,
Length: uint32(alen),
}
}
func newArrayElemCode(ctx *compileContext, head *Opcode, length int, size uintptr) *Opcode {
return &Opcode{
Op: OpArrayElem,
Idx: head.Idx,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: uint32(length),
Indent: ctx.indent,
Size: uint32(size),
}
}
func newMapHeaderCode(ctx *compileContext) *Opcode {
idx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
elemIdx := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
length := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
mapIter := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMap,
Idx: idx,
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
ElemIdx: elemIdx,
Length: length,
MapIter: mapIter,
Indent: ctx.indent,
}
}
func newMapKeyCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapKey,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
Indent: ctx.indent,
}
}
func newMapValueCode(ctx *compileContext, head *Opcode) *Opcode {
return &Opcode{
Op: OpMapValue,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
ElemIdx: head.ElemIdx,
Length: head.Length,
MapIter: head.MapIter,
Indent: ctx.indent,
}
}
func newMapEndCode(ctx *compileContext, head *Opcode) *Opcode {
mapPos := opcodeOffset(ctx.ptrIndex)
ctx.incPtrIndex()
idx := opcodeOffset(ctx.ptrIndex)
return &Opcode{
Op: OpMapEnd,
Idx: idx,
Next: newEndOp(ctx),
DisplayIdx: ctx.opcodeIndex,
Length: head.Length,
MapPos: mapPos,
Indent: ctx.indent,
}
}
func newInterfaceCode(ctx *compileContext) *Opcode {
var flag OpFlags
if ctx.typ.NumMethod() > 0 {
flag |= NonEmptyInterfaceFlags
}
return &Opcode{
Op: OpInterface,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
Flags: flag,
}
}
func newRecursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
return &Opcode{
Op: OpRecursive,
Idx: opcodeOffset(ctx.ptrIndex),
Next: newEndOp(ctx),
Type: ctx.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
Jmp: jmp,
}
}