go-json/internal/encoder/code.go

867 lines
18 KiB
Go
Raw Permalink Normal View History

2021-11-19 12:10:42 +03:00
package encoder
import (
2021-11-22 07:20:03 +03:00
"fmt"
"unsafe"
2021-11-19 12:10:42 +03:00
"github.com/goccy/go-json/internal/runtime"
)
type Code interface {
2021-11-27 15:56:31 +03:00
Kind() CodeKind
2021-11-22 07:20:03 +03:00
ToOpcode(*compileContext) Opcodes
}
2021-11-23 07:53:08 +03:00
type AnonymousCode interface {
ToAnonymousOpcode(*compileContext) Opcodes
}
2021-11-22 07:20:03 +03:00
type Opcodes []*Opcode
func (o Opcodes) First() *Opcode {
if len(o) == 0 {
return nil
}
return o[0]
}
func (o Opcodes) Last() *Opcode {
if len(o) == 0 {
return nil
}
return o[len(o)-1]
2021-11-19 12:10:42 +03:00
}
2021-11-27 13:34:06 +03:00
func (o Opcodes) Add(codes ...*Opcode) Opcodes {
return append(o, codes...)
}
2021-11-27 15:56:31 +03:00
type CodeKind int
2021-11-19 12:10:42 +03:00
const (
2021-11-27 15:56:31 +03:00
CodeKindInterface CodeKind = iota
CodeKindPtr
CodeKindInt
CodeKindUint
CodeKindFloat
CodeKindString
CodeKindBool
CodeKindStruct
CodeKindMap
CodeKindSlice
CodeKindArray
CodeKindBytes
CodeKindMarshalJSON
CodeKindMarshalText
CodeKindRecursive
2021-11-19 12:10:42 +03:00
)
type IntCode struct {
typ *runtime.Type
bitSize uint8
isString bool
2021-11-19 14:22:02 +03:00
isPtr bool
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *IntCode) Kind() CodeKind {
return CodeKindInt
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *IntCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpIntPtr)
case c.isString:
code = newOpCode(ctx, OpIntString)
default:
code = newOpCode(ctx, OpInt)
}
code.NumBitSize = c.bitSize
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type UintCode struct {
typ *runtime.Type
bitSize uint8
isString bool
2021-11-19 14:22:02 +03:00
isPtr bool
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *UintCode) Kind() CodeKind {
return CodeKindUint
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *UintCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpUintPtr)
case c.isString:
code = newOpCode(ctx, OpUintString)
default:
code = newOpCode(ctx, OpUint)
}
code.NumBitSize = c.bitSize
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type FloatCode struct {
typ *runtime.Type
bitSize uint8
isString bool
2021-11-19 14:22:02 +03:00
isPtr bool
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *FloatCode) Kind() CodeKind {
return CodeKindFloat
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *FloatCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
switch c.bitSize {
case 32:
code = newOpCode(ctx, OpFloat32Ptr)
default:
code = newOpCode(ctx, OpFloat64Ptr)
}
default:
switch c.bitSize {
case 32:
code = newOpCode(ctx, OpFloat32)
default:
code = newOpCode(ctx, OpFloat64)
}
}
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type StringCode struct {
typ *runtime.Type
isString bool
2021-11-19 14:22:02 +03:00
isPtr bool
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *StringCode) Kind() CodeKind {
return CodeKindString
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *StringCode) ToOpcode(ctx *compileContext) Opcodes {
isJsonNumberType := c.typ == runtime.Type2RType(jsonNumberType)
var code *Opcode
if c.isPtr {
if isJsonNumberType {
code = newOpCode(ctx, OpNumberPtr)
} else {
code = newOpCode(ctx, OpStringPtr)
}
} else {
if isJsonNumberType {
code = newOpCode(ctx, OpNumber)
} else {
code = newOpCode(ctx, OpString)
}
}
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type BoolCode struct {
typ *runtime.Type
isString bool
2021-11-19 14:22:02 +03:00
isPtr bool
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *BoolCode) Kind() CodeKind {
return CodeKindBool
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *BoolCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpBoolPtr)
default:
code = newOpCode(ctx, OpBool)
}
ctx.incIndex()
return Opcodes{code}
}
type BytesCode struct {
typ *runtime.Type
isPtr bool
}
2021-11-27 15:56:31 +03:00
func (c *BytesCode) Kind() CodeKind {
return CodeKindBytes
2021-11-22 07:20:03 +03:00
}
func (c *BytesCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpBytesPtr)
default:
code = newOpCode(ctx, OpBytes)
}
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type SliceCode struct {
2021-11-22 07:20:03 +03:00
typ *runtime.Type
value Code
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *SliceCode) Kind() CodeKind {
return CodeKindSlice
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
// header => opcode => elem => end
// ^ |
// |________|
size := c.typ.Elem().Size()
header := newSliceHeaderCode(ctx)
ctx.incIndex()
2021-11-25 07:10:01 +03:00
codes := c.value.ToOpcode(ctx.incIndent())
2021-11-25 15:27:29 +03:00
codes.First().Flags |= IndirectFlags
2021-11-25 07:10:01 +03:00
elemCode := newSliceElemCode(ctx.withType(c.typ.Elem()), header, size)
2021-11-22 07:20:03 +03:00
ctx.incIndex()
end := newOpCode(ctx, OpSliceEnd)
ctx.incIndex()
header.End = end
header.Next = codes.First()
codes.Last().Next = elemCode
elemCode.Next = codes.First()
elemCode.End = end
2021-11-27 13:34:06 +03:00
return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
2021-11-19 12:10:42 +03:00
}
type ArrayCode struct {
2021-11-22 07:20:03 +03:00
typ *runtime.Type
value Code
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *ArrayCode) Kind() CodeKind {
return CodeKindArray
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *ArrayCode) ToOpcode(ctx *compileContext) Opcodes {
// header => opcode => elem => end
// ^ |
// |________|
elem := c.typ.Elem()
alen := c.typ.Len()
size := elem.Size()
header := newArrayHeaderCode(ctx, alen)
ctx.incIndex()
2021-11-24 07:32:58 +03:00
codes := c.value.ToOpcode(ctx.incIndent())
2021-11-25 15:27:29 +03:00
codes.First().Flags |= IndirectFlags
2021-11-22 07:20:03 +03:00
2021-11-24 07:32:58 +03:00
elemCode := newArrayElemCode(ctx.withType(elem), header, alen, size)
2021-11-22 07:20:03 +03:00
ctx.incIndex()
end := newOpCode(ctx, OpArrayEnd)
ctx.incIndex()
header.End = end
header.Next = codes.First()
codes.Last().Next = elemCode
elemCode.Next = codes.First()
elemCode.End = end
2021-11-24 07:32:58 +03:00
2021-11-27 13:34:06 +03:00
return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
2021-11-19 12:10:42 +03:00
}
type MapCode struct {
2021-11-22 07:20:03 +03:00
typ *runtime.Type
key Code
value Code
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *MapCode) Kind() CodeKind {
return CodeKindMap
2021-11-19 14:22:02 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
header := newMapHeaderCode(ctx)
ctx.incIndex()
2021-11-19 12:10:42 +03:00
2021-11-22 07:20:03 +03:00
keyCodes := c.key.ToOpcode(ctx)
2021-11-19 12:10:42 +03:00
2021-11-22 07:20:03 +03:00
value := newMapValueCode(ctx, header)
ctx.incIndex()
2021-11-25 07:10:01 +03:00
valueCodes := c.value.ToOpcode(ctx.incIndent())
2021-11-25 15:27:29 +03:00
valueCodes.First().Flags |= IndirectFlags
2021-11-22 07:20:03 +03:00
key := newMapKeyCode(ctx, header)
ctx.incIndex()
2021-11-19 12:10:42 +03:00
2021-11-22 07:20:03 +03:00
end := newMapEndCode(ctx, header)
ctx.incIndex()
header.Next = keyCodes.First()
keyCodes.Last().Next = value
value.Next = valueCodes.First()
valueCodes.Last().Next = key
key.Next = keyCodes.First()
header.End = end
key.End = end
value.End = end
2021-11-27 13:34:06 +03:00
return Opcodes{header}.Add(keyCodes...).Add(value).Add(valueCodes...).Add(key).Add(end)
2021-11-19 12:10:42 +03:00
}
type StructCode struct {
typ *runtime.Type
isPtr bool
fields []*StructFieldCode
disableIndirectConversion bool
2021-11-22 07:20:03 +03:00
isIndirect bool
isRecursive bool
recursiveCodes Opcodes
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *StructCode) Kind() CodeKind {
return CodeKindStruct
2021-11-19 12:10:42 +03:00
}
2021-11-26 19:13:11 +03:00
func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *Opcode {
if field.isAnonymous {
return c.lastAnonymousFieldCode(firstField)
}
lastField := firstField
for lastField.NextField != nil {
lastField = lastField.NextField
}
return lastField
}
func (c *StructCode) lastAnonymousFieldCode(firstField *Opcode) *Opcode {
// firstField is special StructHead operation for anonymous structure.
// So, StructHead's next operation is truely struct head operation.
lastField := firstField.Next
for lastField.NextField != nil {
lastField = lastField.NextField
}
return lastField
}
2021-11-22 07:20:03 +03:00
func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
// header => code => structField => code => end
// ^ |
// |__________|
if c.isRecursive {
2021-11-25 07:10:01 +03:00
recursive := newRecursiveCode(ctx, &CompiledCode{})
recursive.Type = c.typ
2021-11-22 07:20:03 +03:00
ctx.incIndex()
2021-11-25 07:10:01 +03:00
*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
2021-11-22 07:20:03 +03:00
return Opcodes{recursive}
}
codes := Opcodes{}
var prevField *Opcode
2021-11-23 07:53:08 +03:00
ctx = ctx.incIndent()
2021-11-22 07:20:03 +03:00
for idx, field := range c.fields {
isFirstField := idx == 0
isEndField := idx == len(c.fields)-1
fieldCodes := field.ToOpcode(ctx, isFirstField, isEndField)
for _, code := range fieldCodes {
if c.isIndirect {
code.Flags |= IndirectFlags
}
}
2021-11-26 19:13:11 +03:00
firstField := fieldCodes.First()
2021-11-22 07:20:03 +03:00
if len(codes) > 0 {
2021-11-26 19:13:11 +03:00
codes.Last().Next = firstField
firstField.Idx = codes.First().Idx
2021-11-23 07:53:08 +03:00
}
if prevField != nil {
2021-11-26 19:13:11 +03:00
prevField.NextField = firstField
2021-11-23 07:53:08 +03:00
}
if isEndField {
2021-11-26 19:13:11 +03:00
endField := fieldCodes.Last()
2021-11-23 07:53:08 +03:00
if len(codes) > 0 {
2021-11-26 19:13:11 +03:00
codes.First().End = endField
2021-11-24 07:32:58 +03:00
} else if field.isAnonymous {
2021-11-26 19:13:11 +03:00
firstField.End = endField
lastField := c.lastAnonymousFieldCode(firstField)
lastField.NextField = endField
2021-11-23 07:53:08 +03:00
} else {
2021-11-26 19:13:11 +03:00
firstField.End = endField
2021-11-23 07:53:08 +03:00
}
2021-11-27 13:34:06 +03:00
codes = codes.Add(fieldCodes...)
2021-11-26 07:28:30 +03:00
break
2021-11-23 07:53:08 +03:00
}
2021-11-26 19:13:11 +03:00
prevField = c.lastFieldCode(field, firstField)
2021-11-27 13:34:06 +03:00
codes = codes.Add(fieldCodes...)
2021-11-23 07:53:08 +03:00
}
2021-11-25 07:10:01 +03:00
if len(codes) == 0 {
2021-11-26 07:28:30 +03:00
head := &Opcode{
Op: OpStructHead,
2021-11-25 07:10:01 +03:00
Idx: opcodeOffset(ctx.ptrIndex),
2021-11-26 07:28:30 +03:00
Type: c.typ,
2021-11-25 07:10:01 +03:00
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
2021-11-26 07:28:30 +03:00
ctx.incOpcodeIndex()
end := &Opcode{
Op: OpStructEnd,
2021-11-25 07:10:01 +03:00
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
2021-11-26 07:28:30 +03:00
head.NextField = end
head.Next = end
head.End = end
2021-11-27 13:34:06 +03:00
codes = codes.Add(head, end)
2021-11-25 07:10:01 +03:00
ctx.incIndex()
2021-11-23 07:53:08 +03:00
}
2021-11-25 07:10:01 +03:00
ctx = ctx.decIndent()
ctx.structTypeToCodes[uintptr(unsafe.Pointer(c.typ))] = codes
2021-11-23 07:53:08 +03:00
return codes
}
func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
// header => code => structField => code => end
// ^ |
// |__________|
if c.isRecursive {
2021-11-25 07:10:01 +03:00
recursive := newRecursiveCode(ctx, &CompiledCode{})
recursive.Type = c.typ
2021-11-23 07:53:08 +03:00
ctx.incIndex()
2021-11-25 07:10:01 +03:00
*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
2021-11-23 07:53:08 +03:00
return Opcodes{recursive}
}
codes := Opcodes{}
var prevField *Opcode
for idx, field := range c.fields {
isFirstField := idx == 0
isEndField := idx == len(c.fields)-1
fieldCodes := field.ToAnonymousOpcode(ctx, isFirstField, isEndField)
for _, code := range fieldCodes {
if c.isIndirect {
code.Flags |= IndirectFlags
}
}
2021-11-26 19:13:11 +03:00
firstField := fieldCodes.First()
2021-11-23 07:53:08 +03:00
if len(codes) > 0 {
2021-11-26 19:13:11 +03:00
codes.Last().Next = firstField
firstField.Idx = codes.First().Idx
2021-11-22 07:20:03 +03:00
}
if prevField != nil {
2021-11-26 19:13:11 +03:00
prevField.NextField = firstField
2021-11-22 07:20:03 +03:00
}
2021-11-23 07:53:08 +03:00
if isEndField {
2021-11-26 19:13:11 +03:00
lastField := fieldCodes.Last()
2021-11-23 07:53:08 +03:00
if len(codes) > 0 {
2021-11-26 19:13:11 +03:00
codes.First().End = lastField
2021-11-23 07:53:08 +03:00
} else {
2021-11-26 19:13:11 +03:00
firstField.End = lastField
2021-11-23 07:53:08 +03:00
}
}
2021-11-26 19:13:11 +03:00
prevField = firstField
2021-11-27 13:34:06 +03:00
codes = codes.Add(fieldCodes...)
2021-11-22 07:20:03 +03:00
}
return codes
}
2021-11-20 11:00:52 +03:00
func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
fields := make([]*StructFieldCode, 0, len(c.fields))
for _, field := range c.fields {
if field.isAnonymous {
structCode := field.getAnonymousStruct()
2021-11-22 07:20:03 +03:00
if structCode != nil && !structCode.isRecursive {
2021-11-20 11:00:52 +03:00
structCode.removeFieldsByTags(tags)
if len(structCode.fields) > 0 {
fields = append(fields, field)
}
continue
}
}
if tags.ExistsKey(field.key) {
continue
}
fields = append(fields, field)
}
c.fields = fields
}
2021-11-22 07:20:03 +03:00
func (c *StructCode) enableIndirect() {
if c.isIndirect {
return
}
c.isIndirect = true
if len(c.fields) == 0 {
return
}
structCode := c.fields[0].getStruct()
if structCode == nil {
return
}
structCode.enableIndirect()
}
2021-11-19 12:10:42 +03:00
type StructFieldCode struct {
typ *runtime.Type
key string
2021-11-22 07:20:03 +03:00
tag *runtime.StructTag
2021-11-19 12:10:42 +03:00
value Code
offset uintptr
isAnonymous bool
isTaggedKey bool
isNilableType bool
isNilCheck bool
isAddrForMarshaler bool
isNextOpPtrType bool
}
2021-11-22 07:20:03 +03:00
func (c *StructFieldCode) getStruct() *StructCode {
value := c.value
ptr, ok := value.(*PtrCode)
if ok {
value = ptr.value
}
structCode, ok := value.(*StructCode)
if ok {
return structCode
}
return nil
}
2021-11-20 11:00:52 +03:00
func (c *StructFieldCode) getAnonymousStruct() *StructCode {
if !c.isAnonymous {
return nil
}
2021-11-22 07:20:03 +03:00
return c.getStruct()
}
2021-11-27 15:56:31 +03:00
func optimizeStructHeader(code *Opcode, tag *runtime.StructTag) OpType {
headType := code.ToHeaderType(tag.IsString)
if tag.IsOmitEmpty {
headType = headType.HeadToOmitEmptyHead()
}
return headType
}
func optimizeStructField(code *Opcode, tag *runtime.StructTag) OpType {
fieldType := code.ToFieldType(tag.IsString)
if tag.IsOmitEmpty {
fieldType = fieldType.FieldToOmitEmptyField()
}
return fieldType
}
2021-11-26 19:13:11 +03:00
func (c *StructFieldCode) headerOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
value := valueCodes.First()
op := optimizeStructHeader(value, c.tag)
field.Op = op
field.NumBitSize = value.NumBitSize
field.PtrNum = value.PtrNum
fieldCodes := Opcodes{field}
if op.IsMultipleOpHead() {
field.Next = value
2021-11-27 13:34:06 +03:00
fieldCodes = fieldCodes.Add(valueCodes...)
2021-11-23 07:53:08 +03:00
} else {
2021-11-26 19:13:11 +03:00
ctx.decIndex()
2021-11-22 07:20:03 +03:00
}
2021-11-26 19:13:11 +03:00
return fieldCodes
}
func (c *StructFieldCode) fieldOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
value := valueCodes.First()
op := optimizeStructField(value, c.tag)
2021-11-22 07:20:03 +03:00
field.Op = op
2021-11-26 19:13:11 +03:00
field.NumBitSize = value.NumBitSize
field.PtrNum = value.PtrNum
2021-11-22 07:20:03 +03:00
fieldCodes := Opcodes{field}
if op.IsMultipleOpField() {
2021-11-26 19:13:11 +03:00
field.Next = value
2021-11-27 13:34:06 +03:00
fieldCodes = fieldCodes.Add(valueCodes...)
2021-11-22 07:20:03 +03:00
} else {
ctx.decIndex()
}
return fieldCodes
}
2021-11-26 19:13:11 +03:00
func (c *StructFieldCode) addStructEndCode(ctx *compileContext, codes Opcodes) Opcodes {
end := &Opcode{
Op: OpStructEnd,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
codes.Last().Next = end
codes.First().NextField = end
2021-11-27 13:34:06 +03:00
codes = codes.Add(end)
ctx.incOpcodeIndex()
2021-11-26 19:13:11 +03:00
return codes
}
func (c *StructFieldCode) structKey(ctx *compileContext) string {
if ctx.escapeKey {
rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
return fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, c.key)))
}
return fmt.Sprintf(`"%s":`, c.key)
}
2021-11-24 07:32:58 +03:00
func (c *StructFieldCode) flags() OpFlags {
2021-11-23 07:53:08 +03:00
var flags OpFlags
if c.isTaggedKey {
flags |= IsTaggedKeyFlags
}
if c.isNilableType {
flags |= IsNilableTypeFlags
}
if c.isNilCheck {
flags |= NilCheckFlags
}
if c.isAddrForMarshaler {
flags |= AddrForMarshalerFlags
}
if c.isNextOpPtrType {
flags |= IsNextOpPtrTypeFlags
}
2021-11-26 19:13:11 +03:00
if c.isAnonymous {
flags |= AnonymousKeyFlags
}
2021-11-24 07:32:58 +03:00
return flags
}
2021-11-26 19:13:11 +03:00
func (c *StructFieldCode) toValueOpcodes(ctx *compileContext) Opcodes {
2021-11-26 09:07:50 +03:00
if c.isAnonymous {
2021-11-26 19:13:11 +03:00
anonymCode, ok := c.value.(AnonymousCode)
if ok {
return anonymCode.ToAnonymousOpcode(ctx.withType(c.typ))
}
2021-11-26 09:07:50 +03:00
}
2021-11-26 19:13:11 +03:00
return c.value.ToOpcode(ctx.withType(c.typ))
}
func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
2021-11-23 07:53:08 +03:00
field := &Opcode{
Idx: opcodeOffset(ctx.ptrIndex),
2021-11-26 19:13:11 +03:00
Flags: c.flags(),
Key: c.structKey(ctx),
2021-11-23 07:53:08 +03:00
Offset: uint32(c.offset),
Type: c.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
DisplayKey: c.key,
}
ctx.incIndex()
2021-11-26 19:13:11 +03:00
valueCodes := c.toValueOpcodes(ctx)
if isFirstField {
codes := c.headerOpcodes(ctx, field, valueCodes)
if isEndField {
codes = c.addStructEndCode(ctx, codes)
2021-11-26 07:28:30 +03:00
}
2021-11-26 19:13:11 +03:00
return codes
2021-11-23 07:53:08 +03:00
}
2021-11-26 19:13:11 +03:00
codes := c.fieldOpcodes(ctx, field, valueCodes)
if isEndField {
2021-11-27 15:56:31 +03:00
if isEnableStructEndOptimizationType(c.value.Kind()) {
2021-11-26 19:13:11 +03:00
field.Op = field.Op.FieldToEnd()
2021-11-23 07:53:08 +03:00
} else {
2021-11-26 19:13:11 +03:00
codes = c.addStructEndCode(ctx, codes)
2021-11-23 07:53:08 +03:00
}
}
2021-11-26 19:13:11 +03:00
return codes
}
2021-11-23 07:53:08 +03:00
2021-11-26 19:13:11 +03:00
func (c *StructFieldCode) ToAnonymousOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
field := &Opcode{
Idx: opcodeOffset(ctx.ptrIndex),
Flags: c.flags() | AnonymousHeadFlags,
Key: c.structKey(ctx),
Offset: uint32(c.offset),
Type: c.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
DisplayKey: c.key,
2021-11-23 07:53:08 +03:00
}
2021-11-26 19:13:11 +03:00
ctx.incIndex()
valueCodes := c.toValueOpcodes(ctx)
if isFirstField {
return c.headerOpcodes(ctx, field, valueCodes)
}
return c.fieldOpcodes(ctx, field, valueCodes)
2021-11-23 07:53:08 +03:00
}
2021-11-27 15:56:31 +03:00
func isEnableStructEndOptimizationType(typ CodeKind) bool {
2021-11-22 07:20:03 +03:00
switch typ {
2021-11-27 15:56:31 +03:00
case CodeKindInt,
CodeKindUint,
CodeKindFloat,
CodeKindString,
CodeKindBool,
CodeKindBytes:
2021-11-22 07:20:03 +03:00
return true
default:
return false
2021-11-20 11:00:52 +03:00
}
2021-11-19 14:22:02 +03:00
}
2021-11-19 12:10:42 +03:00
type InterfaceCode struct {
2021-11-19 14:22:02 +03:00
typ *runtime.Type
isPtr bool
}
2021-11-27 15:56:31 +03:00
func (c *InterfaceCode) Kind() CodeKind {
return CodeKindInterface
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
var code *Opcode
switch {
case c.isPtr:
2021-11-25 15:46:51 +03:00
code = newOpCode(ctx.withType(c.typ), OpInterfacePtr)
2021-11-22 07:20:03 +03:00
default:
2021-11-25 15:46:51 +03:00
code = newOpCode(ctx.withType(c.typ), OpInterface)
2021-11-22 07:20:03 +03:00
}
2021-11-27 16:33:45 +03:00
if c.typ.NumMethod() > 0 {
code.Flags |= NonEmptyInterfaceFlags
}
2021-11-22 07:20:03 +03:00
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
2021-11-19 14:22:02 +03:00
type MarshalJSONCode struct {
2021-11-27 15:56:31 +03:00
typ *runtime.Type
isAddrForMarshaler bool
isNilableType bool
isMarshalerContext bool
2021-11-19 14:22:02 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *MarshalJSONCode) Kind() CodeKind {
return CodeKindMarshalJSON
2021-11-19 14:22:02 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
2021-11-25 07:10:01 +03:00
code := newOpCode(ctx.withType(c.typ), OpMarshalJSON)
2021-11-27 15:56:31 +03:00
if c.isAddrForMarshaler {
2021-11-22 07:20:03 +03:00
code.Flags |= AddrForMarshalerFlags
}
2021-11-27 15:56:31 +03:00
if c.isMarshalerContext {
2021-11-22 07:20:03 +03:00
code.Flags |= MarshalerContextFlags
}
2021-11-27 15:56:31 +03:00
if c.isNilableType {
2021-11-22 07:20:03 +03:00
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
ctx.incIndex()
return Opcodes{code}
2021-11-19 14:22:02 +03:00
}
type MarshalTextCode struct {
2021-11-27 15:56:31 +03:00
typ *runtime.Type
isAddrForMarshaler bool
isNilableType bool
2021-11-19 14:22:02 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *MarshalTextCode) Kind() CodeKind {
return CodeKindMarshalText
2021-11-19 14:22:02 +03:00
}
2021-11-19 12:10:42 +03:00
2021-11-22 07:20:03 +03:00
func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
2021-11-25 07:10:01 +03:00
code := newOpCode(ctx.withType(c.typ), OpMarshalText)
2021-11-27 15:56:31 +03:00
if c.isAddrForMarshaler {
2021-11-22 07:20:03 +03:00
code.Flags |= AddrForMarshalerFlags
}
2021-11-27 15:56:31 +03:00
if c.isNilableType {
2021-11-22 07:20:03 +03:00
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
ctx.incIndex()
return Opcodes{code}
2021-11-19 12:10:42 +03:00
}
type PtrCode struct {
2021-11-22 07:20:03 +03:00
typ *runtime.Type
value Code
ptrNum uint8
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func (c *PtrCode) Kind() CodeKind {
return CodeKindPtr
2021-11-19 12:10:42 +03:00
}
2021-11-22 07:20:03 +03:00
func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes {
2021-11-25 07:10:01 +03:00
codes := c.value.ToOpcode(ctx.withType(c.typ.Elem()))
2021-11-23 07:53:08 +03:00
codes.First().Op = convertPtrOp(codes.First())
codes.First().PtrNum = c.ptrNum
return codes
}
func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
2021-11-26 07:28:30 +03:00
var codes Opcodes
anonymCode, ok := c.value.(AnonymousCode)
if ok {
codes = anonymCode.ToAnonymousOpcode(ctx.withType(c.typ.Elem()))
} else {
codes = c.value.ToOpcode(ctx.withType(c.typ.Elem()))
}
2021-11-23 07:53:08 +03:00
codes.First().Op = convertPtrOp(codes.First())
2021-11-22 07:20:03 +03:00
codes.First().PtrNum = c.ptrNum
return codes
2021-11-19 12:10:42 +03:00
}
2021-11-27 15:56:31 +03:00
func convertPtrOp(code *Opcode) OpType {
ptrHeadOp := code.Op.HeadToPtrHead()
if code.Op != ptrHeadOp {
if code.PtrNum > 0 {
// ptr field and ptr head
code.PtrNum--
2021-11-19 14:22:02 +03:00
}
2021-11-27 15:56:31 +03:00
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 OpNumber:
return OpNumberPtr
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 OpRecursive:
return OpRecursivePtr
}
return code.Op
2021-11-22 07:20:03 +03:00
}