go-json/encode_compile.go

1425 lines
35 KiB
Go
Raw Normal View History

2020-04-29 18:31:50 +03:00
package json
import (
2021-01-31 16:45:59 +03:00
"encoding"
2020-04-29 18:31:50 +03:00
"fmt"
"math"
2020-04-29 18:31:50 +03:00
"reflect"
2020-12-29 19:29:29 +03:00
"strings"
2020-04-29 18:31:50 +03:00
"unsafe"
)
2021-01-31 16:45:59 +03:00
type compiledCode struct {
code *opcode
linked bool // whether recursive code already have linked
curLen uintptr
nextLen uintptr
}
type opcodeSet struct {
code *opcode
codeLength int
}
var (
2021-02-04 12:00:08 +03:00
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
jsonNumberType = reflect.TypeOf(Number(""))
2021-01-31 16:45:59 +03:00
)
func encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
opcodeMap := loadOpcodeMap()
if codeSet, exists := opcodeMap[typeptr]; exists {
return codeSet, nil
}
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
code, err := encodeCompileHead(&encodeCompileContext{
typ: copiedType,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.totalLength()
codeSet := &opcodeSet{
code: code,
codeLength: codeLength,
}
storeOpcodeSet(typeptr, codeSet, opcodeMap)
return codeSet, nil
}
func encodeCompileHead(ctx *encodeCompileContext) (*opcode, error) {
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-18 07:36:36 +03:00
switch {
case encodeImplementsMarshalJSON(typ):
2021-01-31 16:45:59 +03:00
return encodeCompileMarshalJSON(ctx)
case encodeImplementsMarshalText(typ):
2021-01-31 16:45:59 +03:00
return encodeCompileMarshalText(ctx)
2020-05-04 12:39:17 +03:00
}
isPtr := false
2020-12-07 04:49:00 +03:00
orgType := typ
2020-05-04 12:39:17 +03:00
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
isPtr = true
2020-05-04 12:39:17 +03:00
}
switch {
case encodeImplementsMarshalJSON(typ):
return encodeCompileMarshalJSON(ctx)
case encodeImplementsMarshalText(typ):
return encodeCompileMarshalText(ctx)
}
if typ.Kind() == reflect.Map {
if isPtr {
return encodeCompilePtr(ctx.withType(rtype_ptrTo(typ)))
}
return encodeCompileMap(ctx.withType(typ))
} else if typ.Kind() == reflect.Struct {
2021-01-31 16:45:59 +03:00
code, err := encodeCompileStruct(ctx.withType(typ), isPtr)
2020-12-29 19:29:29 +03:00
if err != nil {
return nil, err
}
2021-01-31 16:45:59 +03:00
encodeOptimizeStructEnd(code)
encodeLinkRecursiveCode(code)
2020-12-29 19:29:29 +03:00
return code, nil
2020-12-07 04:49:00 +03:00
} else if isPtr && typ.Implements(marshalTextType) {
typ = orgType
}
code, err := encodeCompile(ctx.withType(typ), isPtr)
2020-12-29 19:29:29 +03:00
if err != nil {
return nil, err
}
2021-01-31 16:45:59 +03:00
encodeOptimizeStructEnd(code)
encodeLinkRecursiveCode(code)
2020-12-29 19:29:29 +03:00
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeLinkRecursiveCode(c *opcode) {
2021-01-15 10:25:00 +03:00
for code := c; code.op != opEnd && code.op != opStructFieldRecursiveEnd; {
switch code.op {
case opStructFieldRecursive, opStructFieldRecursivePtr:
2021-01-15 10:25:00 +03:00
if code.jmp.linked {
code = code.next
continue
}
code.jmp.code = copyOpcode(code.jmp.code)
c := code.jmp.code
c.end.next = newEndOp(&encodeCompileContext{})
c.op = c.op.ptrHeadToHead()
beforeLastCode := c.end
lastCode := beforeLastCode.next
lastCode.idx = beforeLastCode.idx + uintptrSize
lastCode.elemIdx = lastCode.idx + uintptrSize
// extend length to alloc slot for elemIdx
totalLength := uintptr(code.totalLength() + 1)
nextTotalLength := uintptr(c.totalLength() + 1)
c.end.next.op = opStructFieldRecursiveEnd
code.jmp.curLen = totalLength
code.jmp.nextLen = nextTotalLength
code.jmp.linked = true
2021-01-31 16:45:59 +03:00
encodeLinkRecursiveCode(code.jmp.code)
2021-01-15 10:25:00 +03:00
code = code.next
continue
}
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
}
2021-01-31 16:45:59 +03:00
func encodeOptimizeStructEnd(c *opcode) {
2020-12-29 19:29:29 +03:00
for code := c; code.op != opEnd; {
if code.op == opStructFieldRecursive || code.op == opStructFieldRecursivePtr {
2020-12-29 19:29:29 +03:00
// ignore if exists recursive operation
return
}
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
for code := c; code.op != opEnd; {
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
case codeStructEnd:
switch code.op {
case opStructEnd:
prev := code.prevField
prevOp := prev.op.String()
if strings.Contains(prevOp, "Head") ||
strings.Contains(prevOp, "Slice") ||
strings.Contains(prevOp, "Array") ||
strings.Contains(prevOp, "Map") ||
strings.Contains(prevOp, "MarshalJSON") ||
strings.Contains(prevOp, "MarshalText") {
2020-12-29 19:29:29 +03:00
// not exists field
code = code.next
break
}
if prev.op != prev.op.fieldToEnd() {
prev.op = prev.op.fieldToEnd()
prev.next = code.next
}
code = code.next
default:
code = code.next
}
default:
code = code.next
}
}
2020-05-04 12:39:17 +03:00
}
func encodeImplementsMarshalJSON(typ *rtype) bool {
if !typ.Implements(marshalJSONType) {
return false
2021-01-09 07:55:34 +03:00
}
if typ.Kind() != reflect.Ptr {
return true
2021-01-09 07:55:34 +03:00
}
// type kind is reflect.Ptr
if !typ.Elem().Implements(marshalJSONType) {
return true
2021-01-09 07:55:34 +03:00
}
// needs to dereference
return false
2021-01-09 07:55:34 +03:00
}
func encodeImplementsMarshalText(typ *rtype) bool {
if !typ.Implements(marshalTextType) {
return false
}
if typ.Kind() != reflect.Ptr {
return true
}
// type kind is reflect.Ptr
if !typ.Elem().Implements(marshalTextType) {
return true
}
// needs to dereference
return false
}
func encodeCompile(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-18 07:36:36 +03:00
switch {
case encodeImplementsMarshalJSON(typ):
2021-01-31 16:45:59 +03:00
return encodeCompileMarshalJSON(ctx)
case encodeImplementsMarshalText(typ):
2021-01-31 16:45:59 +03:00
return encodeCompileMarshalText(ctx)
2020-05-04 12:39:17 +03:00
}
2020-04-29 18:31:50 +03:00
switch typ.Kind() {
case reflect.Ptr:
2021-01-31 16:45:59 +03:00
return encodeCompilePtr(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Slice:
elem := typ.Elem()
if elem.Kind() == reflect.Uint8 {
p := rtype_ptrTo(elem)
if !p.Implements(marshalJSONType) && !p.Implements(marshalTextType) {
return encodeCompileBytes(ctx)
}
2020-08-19 04:34:11 +03:00
}
2021-01-31 16:45:59 +03:00
return encodeCompileSlice(ctx)
case reflect.Array:
2021-01-31 16:45:59 +03:00
return encodeCompileArray(ctx)
case reflect.Map:
return encodeCompileMap(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Struct:
return encodeCompileStruct(ctx, isPtr)
2020-08-12 10:54:15 +03:00
case reflect.Interface:
2021-01-31 16:45:59 +03:00
return encodeCompileInterface(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int:
2021-01-31 16:45:59 +03:00
return encodeCompileInt(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int8:
2021-01-31 16:45:59 +03:00
return encodeCompileInt8(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int16:
2021-01-31 16:45:59 +03:00
return encodeCompileInt16(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int32:
2021-01-31 16:45:59 +03:00
return encodeCompileInt32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int64:
2021-01-31 16:45:59 +03:00
return encodeCompileInt64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint:
2021-01-31 16:45:59 +03:00
return encodeCompileUint(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint8:
2021-01-31 16:45:59 +03:00
return encodeCompileUint8(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint16:
2021-01-31 16:45:59 +03:00
return encodeCompileUint16(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint32:
2021-01-31 16:45:59 +03:00
return encodeCompileUint32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint64:
2021-01-31 16:45:59 +03:00
return encodeCompileUint64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uintptr:
2021-01-31 16:45:59 +03:00
return encodeCompileUint(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Float32:
2021-01-31 16:45:59 +03:00
return encodeCompileFloat32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Float64:
2021-01-31 16:45:59 +03:00
return encodeCompileFloat64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.String:
2021-01-31 16:45:59 +03:00
return encodeCompileString(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Bool:
2021-01-31 16:45:59 +03:00
return encodeCompileBool(ctx)
2020-04-29 18:31:50 +03:00
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
2020-04-29 18:31:50 +03:00
}
func encodeConvertPtrOp(code *opcode) opType {
ptrHeadOp := code.op.headToPtrHead()
if code.op != ptrHeadOp {
return ptrHeadOp
}
switch code.op {
case opInt:
return opIntPtr
case opUint:
return opUintPtr
case opFloat32:
return opFloat32Ptr
case opFloat64:
return opFloat64Ptr
case opString:
return opStringPtr
case opBool:
return opBoolPtr
case opBytes:
return opBytesPtr
case opArray:
return opArrayPtr
case opSlice:
return opSlicePtr
case opMap:
return opMapPtr
case opMarshalJSON:
return opMarshalJSONPtr
case opMarshalText:
return opMarshalTextPtr
case opInterface:
return opInterfacePtr
case opStructFieldRecursive:
return opStructFieldRecursivePtr
}
return code.op
}
2021-01-31 16:45:59 +03:00
func encodeCompileKey(ctx *encodeCompileContext) (*opcode, error) {
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-20 11:47:38 +03:00
switch {
case encodeImplementsMarshalJSON(typ):
return encodeCompileMarshalJSON(ctx)
case encodeImplementsMarshalText(typ):
return encodeCompileMarshalText(ctx)
2020-08-20 11:47:38 +03:00
}
switch typ.Kind() {
case reflect.Ptr:
2021-01-31 16:45:59 +03:00
return encodeCompilePtr(ctx)
2020-08-20 11:47:38 +03:00
case reflect.Interface:
2021-01-31 16:45:59 +03:00
return encodeCompileInterface(ctx)
2020-08-20 11:47:38 +03:00
case reflect.String:
2021-01-31 16:45:59 +03:00
return encodeCompileString(ctx)
case reflect.Int:
2021-01-31 16:45:59 +03:00
return encodeCompileIntString(ctx)
case reflect.Int8:
2021-01-31 16:45:59 +03:00
return encodeCompileInt8String(ctx)
case reflect.Int16:
2021-01-31 16:45:59 +03:00
return encodeCompileInt16String(ctx)
case reflect.Int32:
2021-01-31 16:45:59 +03:00
return encodeCompileInt32String(ctx)
case reflect.Int64:
2021-01-31 16:45:59 +03:00
return encodeCompileInt64String(ctx)
case reflect.Uint:
2021-01-31 16:45:59 +03:00
return encodeCompileUintString(ctx)
case reflect.Uint8:
2021-01-31 16:45:59 +03:00
return encodeCompileUint8String(ctx)
case reflect.Uint16:
2021-01-31 16:45:59 +03:00
return encodeCompileUint16String(ctx)
case reflect.Uint32:
2021-01-31 16:45:59 +03:00
return encodeCompileUint32String(ctx)
case reflect.Uint64:
2021-01-31 16:45:59 +03:00
return encodeCompileUint64String(ctx)
2020-12-15 06:29:19 +03:00
case reflect.Uintptr:
2021-01-31 16:45:59 +03:00
return encodeCompileUintString(ctx)
2020-08-20 11:47:38 +03:00
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
2021-01-31 16:45:59 +03:00
func encodeCompilePtr(ctx *encodeCompileContext) (*opcode, error) {
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()), true)
2020-08-30 11:32:26 +03:00
if err != nil {
return nil, err
}
code.op = encodeConvertPtrOp(code)
code.ptrNum++
return code, nil
2020-04-30 05:56:56 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opMarshalJSON)
typ := ctx.typ
if !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType) {
code.addrForMarshaler = true
}
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opMarshalText)
typ := ctx.typ
if !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType) {
code.addrForMarshaler = true
}
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
const intSize = 32 << (^uint(0) >> 63)
2021-01-31 16:45:59 +03:00
func encodeCompileInt(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt)
switch intSize {
case 32:
code.mask = math.MaxUint32
code.rshiftNum = 31
default:
code.mask = math.MaxUint64
code.rshiftNum = 63
}
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
code.mask = math.MaxUint8
code.rshiftNum = 7
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
code.mask = math.MaxUint16
code.rshiftNum = 15
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
code.mask = math.MaxUint32
code.rshiftNum = 31
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
code.mask = math.MaxUint64
code.rshiftNum = 63
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint)
switch intSize {
case 32:
code.mask = math.MaxUint32
code.rshiftNum = 31
default:
code.mask = math.MaxUint64
code.rshiftNum = 63
}
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
code.mask = math.MaxUint8
code.rshiftNum = 7
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
code.mask = math.MaxUint16
code.rshiftNum = 15
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
code.mask = math.MaxUint32
code.rshiftNum = 31
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
code.mask = math.MaxUint64
code.rshiftNum = 63
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileIntString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
switch intSize {
case 32:
code.mask = math.MaxUint32
code.rshiftNum = 31
default:
code.mask = math.MaxUint64
code.rshiftNum = 63
}
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt8String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
code.mask = math.MaxUint8
code.rshiftNum = 7
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt16String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
code.mask = math.MaxUint16
code.rshiftNum = 15
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt32String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
code.mask = math.MaxUint32
code.rshiftNum = 31
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileInt64String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
code.mask = math.MaxUint64
code.rshiftNum = 63
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileUintString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
switch intSize {
case 32:
code.mask = math.MaxUint32
code.rshiftNum = 31
default:
code.mask = math.MaxUint64
code.rshiftNum = 63
}
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint8String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
code.mask = math.MaxUint8
code.rshiftNum = 7
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint16String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
code.mask = math.MaxUint16
code.rshiftNum = 15
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint32String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
code.mask = math.MaxUint32
code.rshiftNum = 31
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileUint64String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
code.mask = math.MaxUint64
code.rshiftNum = 63
ctx.incIndex()
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileFloat32(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opFloat32)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileFloat64(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opFloat64)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileString(ctx *encodeCompileContext) (*opcode, error) {
var op opType
if ctx.typ == type2rtype(jsonNumberType) {
op = opNumber
} else {
op = opString
}
code := newOpCode(ctx, op)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileBool(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opBool)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileBytes(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opBytes)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-08-19 04:34:11 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompileInterface(ctx *encodeCompileContext) (*opcode, error) {
2020-08-31 15:59:22 +03:00
code := newInterfaceCode(ctx)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
2021-01-31 16:45:59 +03:00
func encodeCompileSlice(ctx *encodeCompileContext) (*opcode, error) {
2020-08-29 09:11:31 +03:00
elem := ctx.typ.Elem()
2020-04-29 18:31:50 +03:00
size := elem.Size()
2020-05-02 17:35:41 +03:00
2020-08-30 11:32:26 +03:00
header := newSliceHeaderCode(ctx)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
code, err := encodeCompileSliceElem(ctx.withType(elem).incIndent())
2020-04-29 18:31:50 +03:00
if err != nil {
return nil, err
}
2020-04-29 19:44:48 +03:00
// header => opcode => elem => end
// ^ |
// |________|
2020-04-29 18:31:50 +03:00
2020-09-01 16:26:26 +03:00
elemCode := newSliceElemCode(ctx, header, size)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
end := newOpCode(ctx, opSliceEnd)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
header.elem = elemCode
header.end = end
header.next = code
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
elemCode.next = code
elemCode.end = end
return (*opcode)(unsafe.Pointer(header)), nil
}
func encodeCompileSliceElem(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType):
return encodeCompileMarshalJSON(ctx)
case !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType):
return encodeCompileMarshalText(ctx)
default:
return encodeCompile(ctx, false)
}
}
2021-01-31 16:45:59 +03:00
func encodeCompileArray(ctx *encodeCompileContext) (*opcode, error) {
2020-08-29 09:11:31 +03:00
typ := ctx.typ
elem := typ.Elem()
alen := typ.Len()
size := elem.Size()
2020-05-02 17:35:41 +03:00
2020-08-30 11:32:26 +03:00
header := newArrayHeaderCode(ctx, alen)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
code, err := encodeCompile(ctx.withType(elem).incIndent(), false)
if err != nil {
return nil, err
}
// header => opcode => elem => end
// ^ |
// |________|
2020-09-01 16:26:26 +03:00
elemCode := newArrayElemCode(ctx, header, alen, size)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
end := newOpCode(ctx, opArrayEnd)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-05-02 17:35:41 +03:00
2020-04-29 19:44:48 +03:00
header.elem = elemCode
header.end = end
header.next = code
2020-04-29 18:31:50 +03:00
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
elemCode.next = code
elemCode.end = end
return (*opcode)(unsafe.Pointer(header)), nil
}
2020-04-30 07:52:24 +03:00
//go:linkname mapiterinit reflect.mapiterinit
//go:noescape
func mapiterinit(mapType *rtype, m unsafe.Pointer) unsafe.Pointer
//go:linkname mapiterkey reflect.mapiterkey
//go:noescape
func mapiterkey(it unsafe.Pointer) unsafe.Pointer
//go:linkname mapiternext reflect.mapiternext
//go:noescape
func mapiternext(it unsafe.Pointer)
//go:linkname maplen reflect.maplen
//go:noescape
func maplen(m unsafe.Pointer) int
func encodeCompileMap(ctx *encodeCompileContext) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
2020-08-29 09:35:03 +03:00
ctx = ctx.incIndent()
header := newMapHeaderCode(ctx)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:11:31 +03:00
typ := ctx.typ
keyType := ctx.typ.Key()
2021-01-31 16:45:59 +03:00
keyCode, err := encodeCompileKey(ctx.withType(keyType))
if err != nil {
return nil, err
}
2020-08-30 11:32:26 +03:00
2020-09-01 16:26:26 +03:00
value := newMapValueCode(ctx, header)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2021-03-15 07:29:08 +03:00
valueCode, err := encodeCompileMapValue(ctx.withType(typ.Elem()))
if err != nil {
return nil, err
}
2020-05-02 17:35:41 +03:00
2020-09-01 16:26:26 +03:00
key := newMapKeyCode(ctx, header)
ctx.incIndex()
2020-05-02 17:35:41 +03:00
2020-08-29 09:35:03 +03:00
ctx = ctx.decIndent()
2020-05-02 17:35:41 +03:00
2020-08-31 15:59:22 +03:00
header.mapKey = key
header.mapValue = value
2020-09-16 08:51:37 +03:00
end := newMapEndCode(ctx, header)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-05-02 17:35:41 +03:00
header.next = keyCode
keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value))
value.next = valueCode
valueCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(key))
key.next = keyCode
header.end = end
key.end = end
2020-09-16 08:51:37 +03:00
value.end = end
2020-04-29 18:31:50 +03:00
return (*opcode)(unsafe.Pointer(header)), nil
}
2021-03-15 07:29:08 +03:00
func encodeCompileMapValue(ctx *encodeCompileContext) (*opcode, error) {
switch ctx.typ.Kind() {
case reflect.Map:
return encodeCompilePtr(ctx.withType(rtype_ptrTo(ctx.typ)))
default:
return encodeCompile(ctx, false)
}
}
func encodeTypeToHeaderType(code *opcode) opType {
2020-12-23 07:13:34 +03:00
switch code.op {
2020-05-02 17:35:41 +03:00
case opInt:
return opStructFieldHeadInt
case opIntPtr:
return opStructFieldHeadIntPtr
2020-05-02 17:35:41 +03:00
case opUint:
return opStructFieldHeadUint
case opUintPtr:
return opStructFieldHeadUintPtr
2020-05-02 17:35:41 +03:00
case opFloat32:
return opStructFieldHeadFloat32
case opFloat32Ptr:
return opStructFieldHeadFloat32Ptr
2020-05-02 17:35:41 +03:00
case opFloat64:
return opStructFieldHeadFloat64
case opFloat64Ptr:
return opStructFieldHeadFloat64Ptr
2020-05-02 17:35:41 +03:00
case opString:
return opStructFieldHeadString
case opStringPtr:
return opStructFieldHeadStringPtr
case opNumber:
return opStructFieldHeadNumber
case opNumberPtr:
return opStructFieldHeadNumberPtr
2020-05-02 17:35:41 +03:00
case opBool:
return opStructFieldHeadBool
case opBoolPtr:
return opStructFieldHeadBoolPtr
case opMap:
return opStructFieldHeadMap
case opMapPtr:
code.op = opMap
return opStructFieldHeadMapPtr
case opArray:
return opStructFieldHeadArray
case opArrayPtr:
code.op = opArray
return opStructFieldHeadArrayPtr
case opSlice:
return opStructFieldHeadSlice
case opSlicePtr:
code.op = opSlice
return opStructFieldHeadSlicePtr
case opMarshalJSON:
return opStructFieldHeadMarshalJSON
case opMarshalJSONPtr:
return opStructFieldHeadMarshalJSONPtr
case opMarshalText:
return opStructFieldHeadMarshalText
case opMarshalTextPtr:
return opStructFieldHeadMarshalTextPtr
2020-05-02 17:35:41 +03:00
}
return opStructFieldHead
}
func encodeTypeToFieldType(code *opcode) opType {
2020-09-17 15:50:27 +03:00
switch code.op {
2020-05-02 17:35:41 +03:00
case opInt:
return opStructFieldInt
case opIntPtr:
return opStructFieldIntPtr
2020-05-02 17:35:41 +03:00
case opUint:
return opStructFieldUint
case opUintPtr:
return opStructFieldUintPtr
2020-05-02 17:35:41 +03:00
case opFloat32:
return opStructFieldFloat32
case opFloat32Ptr:
return opStructFieldFloat32Ptr
2020-05-02 17:35:41 +03:00
case opFloat64:
return opStructFieldFloat64
case opFloat64Ptr:
return opStructFieldFloat64Ptr
2020-05-02 17:35:41 +03:00
case opString:
return opStructFieldString
case opStringPtr:
return opStructFieldStringPtr
case opNumber:
return opStructFieldNumber
case opNumberPtr:
return opStructFieldNumberPtr
2020-05-02 17:35:41 +03:00
case opBool:
return opStructFieldBool
case opBoolPtr:
return opStructFieldBoolPtr
case opMap:
return opStructFieldMap
case opMapPtr:
code.op = opMap
return opStructFieldMapPtr
case opArray:
return opStructFieldArray
case opArrayPtr:
code.op = opArray
return opStructFieldArrayPtr
case opSlice:
return opStructFieldSlice
case opSlicePtr:
code.op = opSlice
return opStructFieldSlicePtr
case opMarshalJSON:
return opStructFieldMarshalJSON
case opMarshalJSONPtr:
return opStructFieldMarshalJSONPtr
case opMarshalText:
return opStructFieldMarshalText
case opMarshalTextPtr:
return opStructFieldMarshalTextPtr
2020-05-02 17:35:41 +03:00
}
return opStructField
}
func encodeOptimizeStructHeader(code *opcode, tag *structTag) opType {
headType := encodeTypeToHeaderType(code)
2020-08-19 13:56:02 +03:00
switch {
case tag.isOmitEmpty:
2020-08-15 11:41:38 +03:00
headType = headType.headToOmitEmptyHead()
2020-08-19 13:56:02 +03:00
case tag.isString:
headType = headType.headToStringTagHead()
2020-08-15 11:41:38 +03:00
}
return headType
}
func encodeOptimizeStructField(code *opcode, tag *structTag) opType {
fieldType := encodeTypeToFieldType(code)
2020-08-19 13:56:02 +03:00
switch {
case tag.isOmitEmpty:
2020-08-15 11:41:38 +03:00
fieldType = fieldType.fieldToOmitEmptyField()
2020-08-19 13:56:02 +03:00
case tag.isString:
fieldType = fieldType.fieldToStringTagField()
2020-08-15 11:41:38 +03:00
}
return fieldType
}
2021-01-31 16:45:59 +03:00
func encodeRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
2020-08-31 15:59:22 +03:00
code := newRecursiveCode(ctx, jmp)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-31 15:59:22 +03:00
return code
2020-08-14 16:44:09 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeCompiledCode(ctx *encodeCompileContext) *opcode {
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-12 12:42:29 +03:00
typeptr := uintptr(unsafe.Pointer(typ))
2020-12-24 21:53:48 +03:00
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
2021-01-31 16:45:59 +03:00
return encodeRecursiveCode(ctx, compiledCode)
2020-08-14 16:44:09 +03:00
}
return nil
}
2021-01-31 16:45:59 +03:00
func encodeStructHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
2020-08-14 16:44:09 +03:00
fieldCode.indent--
op := encodeOptimizeStructHeader(valueCode, tag)
2020-08-14 16:44:09 +03:00
fieldCode.op = op
fieldCode.mask = valueCode.mask
fieldCode.rshiftNum = valueCode.rshiftNum
2020-12-23 07:13:34 +03:00
fieldCode.ptrNum = valueCode.ptrNum
2020-08-14 16:44:09 +03:00
switch op {
case opStructFieldHead,
opStructFieldHeadSlice,
opStructFieldHeadArray,
opStructFieldHeadMap,
opStructFieldHeadStruct,
2020-08-14 16:44:09 +03:00
opStructFieldHeadOmitEmpty,
opStructFieldHeadOmitEmptySlice,
opStructFieldHeadStringTagSlice,
opStructFieldHeadOmitEmptyArray,
opStructFieldHeadStringTagArray,
opStructFieldHeadOmitEmptyMap,
opStructFieldHeadStringTagMap,
opStructFieldHeadOmitEmptyStruct,
2020-12-25 11:03:56 +03:00
opStructFieldHeadStringTag:
2020-08-14 16:44:09 +03:00
return valueCode.beforeLastCode()
case opStructFieldHeadSlicePtr,
opStructFieldHeadOmitEmptySlicePtr,
opStructFieldHeadStringTagSlicePtr,
opStructFieldHeadArrayPtr,
opStructFieldHeadOmitEmptyArrayPtr,
opStructFieldHeadStringTagArrayPtr,
opStructFieldHeadMapPtr,
opStructFieldHeadOmitEmptyMapPtr,
opStructFieldHeadStringTagMapPtr:
return valueCode.beforeLastCode()
case opStructFieldHeadMarshalJSONPtr,
opStructFieldHeadOmitEmptyMarshalJSONPtr,
opStructFieldHeadStringTagMarshalJSONPtr,
opStructFieldHeadMarshalTextPtr,
opStructFieldHeadOmitEmptyMarshalTextPtr,
opStructFieldHeadStringTagMarshalTextPtr:
ctx.decOpcodeIndex()
return (*opcode)(unsafe.Pointer(fieldCode))
2020-08-14 16:44:09 +03:00
}
2020-08-30 11:32:26 +03:00
ctx.decOpcodeIndex()
2020-08-14 16:44:09 +03:00
return (*opcode)(unsafe.Pointer(fieldCode))
}
2021-01-31 16:45:59 +03:00
func encodeStructField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
2020-08-14 16:44:09 +03:00
code := (*opcode)(unsafe.Pointer(fieldCode))
op := encodeOptimizeStructField(valueCode, tag)
2020-08-14 16:44:09 +03:00
fieldCode.op = op
2020-12-23 07:13:34 +03:00
fieldCode.ptrNum = valueCode.ptrNum
fieldCode.mask = valueCode.mask
fieldCode.rshiftNum = valueCode.rshiftNum
fieldCode.jmp = valueCode.jmp
2020-08-14 16:44:09 +03:00
switch op {
case opStructField,
opStructFieldSlice,
opStructFieldArray,
opStructFieldMap,
opStructFieldStruct,
2020-08-14 16:44:09 +03:00
opStructFieldOmitEmpty,
opStructFieldOmitEmptySlice,
opStructFieldStringTagSlice,
opStructFieldOmitEmptyArray,
opStructFieldStringTagArray,
opStructFieldOmitEmptyMap,
opStructFieldStringTagMap,
opStructFieldOmitEmptyStruct,
2020-12-25 11:03:56 +03:00
opStructFieldStringTag:
2020-08-14 16:44:09 +03:00
return valueCode.beforeLastCode()
case opStructFieldSlicePtr,
opStructFieldOmitEmptySlicePtr,
opStructFieldStringTagSlicePtr,
opStructFieldArrayPtr,
opStructFieldOmitEmptyArrayPtr,
opStructFieldStringTagArrayPtr,
opStructFieldMapPtr,
opStructFieldOmitEmptyMapPtr,
opStructFieldStringTagMapPtr:
return valueCode.beforeLastCode()
2020-08-14 16:44:09 +03:00
}
2020-09-01 16:26:26 +03:00
ctx.decIndex()
2020-08-14 16:44:09 +03:00
return code
}
2020-08-22 06:58:34 +03:00
2021-01-31 16:45:59 +03:00
func encodeIsNotExistsField(head *opcode) bool {
2020-08-22 06:58:34 +03:00
if head == nil {
return false
}
if head.op != opStructFieldHead {
return false
}
if !head.anonymousHead {
2020-08-22 06:58:34 +03:00
return false
}
if head.next == nil {
return false
}
if head.nextField == nil {
return false
}
if head.nextField.op != opStructAnonymousEnd {
return false
}
if head.next.op == opStructAnonymousEnd {
return true
}
if head.next.op.codeType() != codeStructField {
return false
}
2021-01-31 16:45:59 +03:00
return encodeIsNotExistsField(head.next)
2020-08-22 06:58:34 +03:00
}
2021-01-31 16:45:59 +03:00
func encodeOptimizeAnonymousFields(head *opcode) {
2020-08-22 06:58:34 +03:00
code := head
2020-08-31 15:59:22 +03:00
var prev *opcode
2020-11-16 15:28:33 +03:00
removedFields := map[*opcode]struct{}{}
2020-08-22 06:58:34 +03:00
for {
2020-12-25 11:03:56 +03:00
if code.op == opStructEnd {
2020-08-22 06:58:34 +03:00
break
}
2020-12-25 11:03:56 +03:00
if code.op == opStructField {
2020-08-22 06:58:34 +03:00
codeType := code.next.op.codeType()
if codeType == codeStructField {
2021-01-31 16:45:59 +03:00
if encodeIsNotExistsField(code.next) {
2020-08-22 06:58:34 +03:00
code.next = code.nextField
2020-08-30 21:14:37 +03:00
diff := code.next.displayIdx - code.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
code.next.decOpcodeIndex()
}
2021-01-31 16:45:59 +03:00
encodeLinkPrevToNextField(code, removedFields)
2020-08-22 06:58:34 +03:00
code = prev
}
}
}
prev = code
2020-08-31 15:59:22 +03:00
code = code.nextField
2020-08-22 06:58:34 +03:00
}
}
type structFieldPair struct {
2020-08-31 15:59:22 +03:00
prevField *opcode
curField *opcode
2020-08-22 09:40:18 +03:00
isTaggedKey bool
linked bool
}
2021-02-01 16:31:39 +03:00
func encodeAnonymousStructFieldPairMap(tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
2020-08-22 09:40:18 +03:00
anonymousFields := map[string][]structFieldPair{}
f := valueCode
2020-08-31 15:59:22 +03:00
var prevAnonymousField *opcode
2020-11-16 15:28:33 +03:00
removedFields := map[*opcode]struct{}{}
2020-08-22 09:40:18 +03:00
for {
existsKey := tags.existsKey(f.displayKey)
isHeadOp := strings.Contains(f.op.String(), "Head")
if existsKey && strings.Contains(f.op.String(), "Recursive") {
2020-11-16 15:28:33 +03:00
// through
} else if isHeadOp && !f.anonymousHead {
2020-08-22 09:40:18 +03:00
if existsKey {
// TODO: need to remove this head
f.op = opStructFieldHead
f.anonymousKey = true
f.anonymousHead = true
2020-11-16 15:28:33 +03:00
} else if named == "" {
f.anonymousHead = true
2020-08-22 09:40:18 +03:00
}
2020-11-16 15:28:33 +03:00
} else if named == "" && f.op == opStructEnd {
2020-08-22 09:40:18 +03:00
f.op = opStructAnonymousEnd
} else if existsKey {
2020-08-30 21:14:37 +03:00
diff := f.nextField.displayIdx - f.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
f.nextField.decOpcodeIndex()
}
2021-01-31 16:45:59 +03:00
encodeLinkPrevToNextField(f, removedFields)
2020-08-22 09:40:18 +03:00
}
if f.displayKey == "" {
if f.nextField == nil {
break
}
prevAnonymousField = f
2020-08-31 15:59:22 +03:00
f = f.nextField
2020-08-22 09:40:18 +03:00
continue
}
2020-11-16 15:28:33 +03:00
key := fmt.Sprintf("%s.%s", named, f.displayKey)
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
2020-08-22 09:40:18 +03:00
prevField: prevAnonymousField,
curField: f,
isTaggedKey: f.isTaggedKey,
})
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
2021-02-09 03:25:33 +03:00
for k, v := range encodeAnonymousFieldPairRecursively(named, f.next) {
2020-08-22 09:40:18 +03:00
anonymousFields[k] = append(anonymousFields[k], v...)
}
}
if f.nextField == nil {
break
}
prevAnonymousField = f
2020-08-31 15:59:22 +03:00
f = f.nextField
2020-08-22 09:40:18 +03:00
}
return anonymousFields
}
2021-02-09 03:25:33 +03:00
func encodeAnonymousFieldPairRecursively(named string, valueCode *opcode) map[string][]structFieldPair {
2021-02-09 03:20:48 +03:00
anonymousFields := map[string][]structFieldPair{}
f := valueCode
var prevAnonymousField *opcode
for {
if f.displayKey != "" && f.anonymousHead {
2021-02-09 03:20:48 +03:00
key := fmt.Sprintf("%s.%s", named, f.displayKey)
anonymousFields[key] = append(anonymousFields[key], structFieldPair{
prevField: prevAnonymousField,
curField: f,
isTaggedKey: f.isTaggedKey,
})
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
2021-02-09 03:25:33 +03:00
for k, v := range encodeAnonymousFieldPairRecursively(named, f.next) {
2021-02-09 03:20:48 +03:00
anonymousFields[k] = append(anonymousFields[k], v...)
}
}
}
if f.nextField == nil {
break
}
prevAnonymousField = f
f = f.nextField
}
return anonymousFields
}
2021-01-31 16:45:59 +03:00
func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
2020-11-16 15:28:33 +03:00
removedFields := map[*opcode]struct{}{}
2020-08-22 09:40:18 +03:00
for _, fieldPairs := range anonymousFields {
if len(fieldPairs) == 1 {
continue
}
// conflict anonymous fields
taggedPairs := []structFieldPair{}
for _, fieldPair := range fieldPairs {
if fieldPair.isTaggedKey {
taggedPairs = append(taggedPairs, fieldPair)
} else {
if !fieldPair.linked {
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.op = opStructFieldHead
fieldPair.curField.anonymousHead = true
fieldPair.curField.anonymousKey = true
2020-08-22 09:40:18 +03:00
} else {
2020-08-30 21:14:37 +03:00
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
2020-11-16 15:28:33 +03:00
removedFields[fieldPair.curField] = struct{}{}
2021-01-31 16:45:59 +03:00
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
2020-08-22 09:40:18 +03:00
}
fieldPair.linked = true
}
}
}
if len(taggedPairs) > 1 {
for _, fieldPair := range taggedPairs {
if !fieldPair.linked {
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.op = opStructFieldHead
fieldPair.curField.anonymousHead = true
fieldPair.curField.anonymousKey = true
2020-08-22 09:40:18 +03:00
} else {
2020-08-30 21:14:37 +03:00
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
2020-11-16 15:28:33 +03:00
removedFields[fieldPair.curField] = struct{}{}
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
2021-01-31 16:45:59 +03:00
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
2020-08-22 09:40:18 +03:00
}
fieldPair.linked = true
}
}
} else {
for _, fieldPair := range taggedPairs {
fieldPair.curField.isTaggedKey = false
}
}
}
2020-08-22 06:58:34 +03:00
}
2021-03-11 18:43:48 +03:00
func encodeIsNilableType(typ *rtype) bool {
switch typ.Kind() {
case reflect.Ptr:
return true
case reflect.Interface:
return true
case reflect.Slice:
return true
case reflect.Map:
return true
default:
return false
}
}
2021-01-31 16:45:59 +03:00
func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
if code := encodeCompiledCode(ctx); code != nil {
2020-08-14 16:44:09 +03:00
return code, nil
}
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-14 16:44:09 +03:00
typeptr := uintptr(unsafe.Pointer(typ))
2020-08-12 12:42:29 +03:00
compiled := &compiledCode{}
2020-12-24 21:53:48 +03:00
ctx.structTypeToCompiledCode[typeptr] = compiled
2020-04-30 05:56:56 +03:00
// header => code => structField => code => end
// ^ |
// |__________|
2020-04-29 18:31:50 +03:00
fieldNum := typ.NumField()
indirect := ifaceIndir(typ)
2020-04-29 18:31:50 +03:00
fieldIdx := 0
disableIndirectConversion := false
2020-04-29 19:44:48 +03:00
var (
2020-08-31 15:59:22 +03:00
head *opcode
2020-04-29 19:44:48 +03:00
code *opcode
2020-08-31 15:59:22 +03:00
prevField *opcode
2020-04-29 19:44:48 +03:00
)
2020-08-29 09:35:03 +03:00
ctx = ctx.incIndent()
2020-08-22 06:58:34 +03:00
tags := structTags{}
2020-08-22 09:40:18 +03:00
anonymousFields := map[string][]structFieldPair{}
2020-04-29 18:31:50 +03:00
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
2020-08-20 06:38:50 +03:00
if isIgnoredStructField(field) {
2020-04-29 18:31:50 +03:00
continue
}
2020-08-22 06:58:34 +03:00
tags = append(tags, structTagFromField(field))
}
for i, tag := range tags {
field := tag.field
2020-04-29 18:31:50 +03:00
fieldType := type2rtype(field.Type)
2020-08-30 11:32:26 +03:00
fieldOpcodeIndex := ctx.opcodeIndex
2020-09-01 16:26:26 +03:00
fieldPtrIndex := ctx.ptrIndex
ctx.incIndex()
2021-03-11 18:43:48 +03:00
nilcheck := true
addrForMarshaler := false
2021-03-11 18:43:48 +03:00
isIndirectSpecialCase := isPtr && i == 0 && fieldNum == 1
isNilableType := encodeIsNilableType(fieldType)
var valueCode *opcode
switch {
case isIndirectSpecialCase && !isNilableType && encodeIsPtrMarshalJSONType(fieldType):
// *struct{ field T } => struct { field *T }
// func (*T) MarshalJSON() ([]byte, error)
// move pointer position from head to first field
code, err := encodeCompileMarshalJSON(ctx.withType(rtype_ptrTo(fieldType)))
if err != nil {
return nil, err
}
valueCode = code
nilcheck = false
indirect = false
disableIndirectConversion = true
2021-03-11 18:43:48 +03:00
case isIndirectSpecialCase && !isNilableType && encodeIsPtrMarshalTextType(fieldType):
// *struct{ field T } => struct { field *T }
// func (*T) MarshalText() ([]byte, error)
// move pointer position from head to first field
code, err := encodeCompileMarshalText(ctx.withType(rtype_ptrTo(fieldType)))
if err != nil {
return nil, err
}
valueCode = code
nilcheck = false
indirect = false
disableIndirectConversion = true
2021-03-11 18:43:48 +03:00
case isPtr && encodeIsPtrMarshalJSONType(fieldType):
// *struct{ field T }
// func (*T) MarshalJSON() ([]byte, error)
code, err := encodeCompileMarshalJSON(ctx.withType(fieldType))
if err != nil {
return nil, err
}
addrForMarshaler = true
nilcheck = false
valueCode = code
2021-03-11 18:43:48 +03:00
case isPtr && encodeIsPtrMarshalTextType(fieldType):
// *struct{ field T }
// func (*T) MarshalText() ([]byte, error)
code, err := encodeCompileMarshalText(ctx.withType(fieldType))
if err != nil {
return nil, err
}
addrForMarshaler = true
nilcheck = false
valueCode = code
2021-03-11 18:43:48 +03:00
default:
code, err := encodeCompile(ctx.withType(fieldType), isPtr)
if err != nil {
return nil, err
}
valueCode = code
2020-04-29 18:31:50 +03:00
}
2020-08-30 11:32:26 +03:00
2020-08-15 11:41:38 +03:00
if field.Anonymous {
2020-11-16 15:28:33 +03:00
tagKey := ""
if tag.isTaggedKey {
tagKey = tag.key
}
2021-02-01 16:31:39 +03:00
for k, v := range encodeAnonymousStructFieldPairMap(tags, tagKey, valueCode) {
2020-08-22 09:40:18 +03:00
anonymousFields[k] = append(anonymousFields[k], v...)
2020-08-15 11:41:38 +03:00
}
valueCode.decIndent()
// fix issue144
if !(isPtr && strings.Contains(valueCode.op.String(), "Marshal")) {
valueCode.indirect = indirect
}
} else {
valueCode.indirect = indirect
2020-08-15 11:41:38 +03:00
}
2020-08-19 13:56:02 +03:00
key := fmt.Sprintf(`"%s":`, tag.key)
2020-12-24 21:53:48 +03:00
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
2020-08-31 15:59:22 +03:00
fieldCode := &opcode{
typ: valueCode.typ,
displayIdx: fieldOpcodeIndex,
idx: opcodeOffset(fieldPtrIndex),
next: valueCode,
indent: ctx.indent,
anonymousKey: field.Anonymous,
key: []byte(key),
escapedKey: []byte(escapedKey),
isTaggedKey: tag.isTaggedKey,
displayKey: tag.key,
offset: field.Offset,
indirect: indirect,
nilcheck: nilcheck,
addrForMarshaler: addrForMarshaler,
2020-04-30 05:56:56 +03:00
}
2020-04-29 18:31:50 +03:00
if fieldIdx == 0 {
2020-09-01 16:26:26 +03:00
fieldCode.headIdx = fieldCode.idx
2021-01-31 16:45:59 +03:00
code = encodeStructHeader(ctx, fieldCode, valueCode, tag)
2020-04-29 19:44:48 +03:00
head = fieldCode
2020-04-29 18:31:50 +03:00
prevField = fieldCode
} else {
2020-09-01 16:26:26 +03:00
fieldCode.headIdx = head.headIdx
code.next = fieldCode
2021-01-31 16:45:59 +03:00
code = encodeStructField(ctx, fieldCode, valueCode, tag)
2020-09-01 16:26:26 +03:00
prevField.nextField = fieldCode
2020-11-16 15:28:33 +03:00
fieldCode.prevField = prevField
2020-04-29 18:31:50 +03:00
prevField = fieldCode
}
fieldIdx++
}
2020-08-29 09:35:03 +03:00
ctx = ctx.decIndent()
2020-05-01 07:12:01 +03:00
2020-08-31 15:59:22 +03:00
structEndCode := &opcode{
op: opStructEnd,
typ: nil,
indent: ctx.indent,
next: newEndOp(ctx),
}
2020-05-01 07:12:01 +03:00
// no struct field
if head == nil {
2020-08-31 15:59:22 +03:00
head = &opcode{
op: opStructFieldHead,
typ: typ,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
headIdx: opcodeOffset(ctx.ptrIndex),
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
nextField: structEndCode,
2020-05-01 07:12:01 +03:00
}
2020-11-16 15:28:33 +03:00
structEndCode.prevField = head
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-31 15:59:22 +03:00
code = head
2020-05-01 07:12:01 +03:00
}
2020-08-30 11:32:26 +03:00
2020-08-30 21:14:37 +03:00
structEndCode.displayIdx = ctx.opcodeIndex
2020-09-01 17:23:07 +03:00
structEndCode.idx = opcodeOffset(ctx.ptrIndex)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
if prevField != nil && prevField.nextField == nil {
prevField.nextField = structEndCode
2020-11-16 15:28:33 +03:00
structEndCode.prevField = prevField
2020-08-30 11:32:26 +03:00
}
2020-04-29 19:44:48 +03:00
head.end = structEndCode
2020-04-29 18:31:50 +03:00
code.next = structEndCode
2021-01-31 16:45:59 +03:00
encodeOptimizeConflictAnonymousFields(anonymousFields)
encodeOptimizeAnonymousFields(head)
2020-08-12 12:42:29 +03:00
ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret
2020-08-15 15:13:08 +03:00
2020-12-24 21:53:48 +03:00
delete(ctx.structTypeToCompiledCode, typeptr)
2020-08-15 15:13:08 +03:00
if !disableIndirectConversion && !head.indirect && isPtr {
head.indirect = true
}
2020-08-12 12:42:29 +03:00
return ret, nil
2020-04-29 18:31:50 +03:00
}
2021-03-11 18:43:48 +03:00
func encodeIsPtrMarshalJSONType(typ *rtype) bool {
return !typ.Implements(marshalJSONType) && rtype_ptrTo(typ).Implements(marshalJSONType)
}
func encodeIsPtrMarshalTextType(typ *rtype) bool {
return !typ.Implements(marshalTextType) && rtype_ptrTo(typ).Implements(marshalTextType)
}