add opcode generator

This commit is contained in:
Masaaki Goshima 2021-11-22 13:20:03 +09:00
parent d11fc7fe6c
commit d631a21b59
No known key found for this signature in database
GPG Key ID: 6A53785055537153
5 changed files with 688 additions and 82 deletions

View File

@ -1,7 +1,9 @@
package encoder package encoder
import ( import (
"fmt"
"reflect" "reflect"
"unsafe"
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
@ -9,7 +11,23 @@ import (
type Code interface { type Code interface {
Type() CodeType2 Type() CodeType2
ToOpcode() []*Opcode ToOpcode(*compileContext) Opcodes
}
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]
} }
type CodeType2 int type CodeType2 int
@ -29,6 +47,7 @@ const (
CodeTypeBytes CodeTypeBytes
CodeTypeMarshalJSON CodeTypeMarshalJSON
CodeTypeMarshalText CodeTypeMarshalText
CodeTypeRecursive
) )
type IntCode struct { type IntCode struct {
@ -42,8 +61,19 @@ func (c *IntCode) Type() CodeType2 {
return CodeTypeInt return CodeTypeInt
} }
func (c *IntCode) ToOpcode() []*Opcode { func (c *IntCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} 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}
} }
type UintCode struct { type UintCode struct {
@ -57,8 +87,19 @@ func (c *UintCode) Type() CodeType2 {
return CodeTypeUint return CodeTypeUint
} }
func (c *UintCode) ToOpcode() []*Opcode { func (c *UintCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} 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}
} }
type FloatCode struct { type FloatCode struct {
@ -72,8 +113,26 @@ func (c *FloatCode) Type() CodeType2 {
return CodeTypeFloat return CodeTypeFloat
} }
func (c *FloatCode) ToOpcode() []*Opcode { func (c *FloatCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} 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}
} }
type StringCode struct { type StringCode struct {
@ -86,8 +145,24 @@ func (c *StringCode) Type() CodeType2 {
return CodeTypeString return CodeTypeString
} }
func (c *StringCode) ToOpcode() []*Opcode { func (c *StringCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} 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}
} }
type BoolCode struct { type BoolCode struct {
@ -100,44 +175,16 @@ func (c *BoolCode) Type() CodeType2 {
return CodeTypeBool return CodeTypeBool
} }
func (c *BoolCode) ToOpcode() []*Opcode { func (c *BoolCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpBoolPtr)
default:
code = newOpCode(ctx, OpBool)
} }
ctx.incIndex()
type SliceCode struct { return Opcodes{code}
typ *runtime.Type
}
func (c *SliceCode) Type() CodeType2 {
return CodeTypeSlice
}
func (c *SliceCode) ToOpcode() []*Opcode {
return []*Opcode{}
}
type ArrayCode struct {
typ *runtime.Type
}
func (c *ArrayCode) Type() CodeType2 {
return CodeTypeArray
}
func (c *ArrayCode) ToOpcode() []*Opcode {
return []*Opcode{}
}
type MapCode struct {
typ *runtime.Type
}
func (c *MapCode) Type() CodeType2 {
return CodeTypeMap
}
func (c *MapCode) ToOpcode() []*Opcode {
return []*Opcode{}
} }
type BytesCode struct { type BytesCode struct {
@ -149,8 +196,125 @@ func (c *BytesCode) Type() CodeType2 {
return CodeTypeBytes return CodeTypeBytes
} }
func (c *BytesCode) ToOpcode() []*Opcode { func (c *BytesCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpBytesPtr)
default:
code = newOpCode(ctx, OpBytes)
}
ctx.incIndex()
return Opcodes{code}
}
type SliceCode struct {
typ *runtime.Type
value Code
}
func (c *SliceCode) Type() CodeType2 {
return CodeTypeSlice
}
func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
// header => opcode => elem => end
// ^ |
// |________|
size := c.typ.Elem().Size()
header := newSliceHeaderCode(ctx)
ctx.incIndex()
codes := c.value.ToOpcode(ctx)
elemCode := newSliceElemCode(ctx, header, size)
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
return Opcodes{header}
}
type ArrayCode struct {
typ *runtime.Type
value Code
}
func (c *ArrayCode) Type() CodeType2 {
return CodeTypeArray
}
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()
codes := c.value.ToOpcode(ctx)
elemCode := newArrayElemCode(ctx, header, alen, size)
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
return Opcodes{header}
}
type MapCode struct {
typ *runtime.Type
key Code
value Code
}
func (c *MapCode) Type() CodeType2 {
return CodeTypeMap
}
func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
ctx = ctx.incIndent()
header := newMapHeaderCode(ctx)
ctx.incIndex()
keyCodes := c.key.ToOpcode(ctx)
value := newMapValueCode(ctx, header)
ctx.incIndex()
valueCodes := c.value.ToOpcode(ctx)
key := newMapKeyCode(ctx, header)
ctx.incIndex()
ctx = ctx.decIndent()
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
return Opcodes{header}
} }
type StructCode struct { type StructCode struct {
@ -158,14 +322,79 @@ type StructCode struct {
isPtr bool isPtr bool
fields []*StructFieldCode fields []*StructFieldCode
disableIndirectConversion bool disableIndirectConversion bool
isIndirect bool
isRecursive bool
recursiveCodes Opcodes
} }
func (c *StructCode) Type() CodeType2 { func (c *StructCode) Type() CodeType2 {
return CodeTypeStruct return CodeTypeStruct
} }
func (c *StructCode) ToOpcode() []*Opcode { func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} // header => code => structField => code => end
// ^ |
// |__________|
var recursive *Opcode
compiled := &CompiledCode{}
if c.isRecursive {
recursive = newRecursiveCode(ctx, compiled)
ctx.incIndex()
}
if len(c.recursiveCodes) > 0 {
c.linkRecursiveCode(compiled, c.recursiveCodes)
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.ToOpcode(ctx, isFirstField, isEndField)
for _, code := range fieldCodes {
if c.isIndirect {
code.Flags |= IndirectFlags
}
}
if len(codes) > 0 {
codes.Last().Next = fieldCodes.First()
}
if prevField != nil {
prevField.NextField = fieldCodes.First()
}
prevField = fieldCodes.First()
codes = append(codes, fieldCodes...)
}
if c.isRecursive {
c.recursiveCodes = codes
c.linkRecursiveCode(compiled, c.recursiveCodes)
return Opcodes{recursive}
}
return codes
}
func (c *StructCode) linkRecursiveCode(compiled *CompiledCode, codes Opcodes) {
compiled.Code = copyOpcode(codes.First())
code := compiled.Code
code.End.Next = newEndOp(&compileContext{})
code.Op = code.Op.PtrHeadToHead()
beforeLastCode := code.End
lastCode := beforeLastCode.Next
lastCode.Idx = beforeLastCode.Idx + uintptrSize
lastCode.ElemIdx = lastCode.Idx + uintptrSize
lastCode.Length = lastCode.Idx + 2*uintptrSize
// extend length to alloc slot for elemIdx + length
totalLength := uintptr(codes.First().TotalLength() + 3)
nextTotalLength := uintptr(code.TotalLength() + 3)
code.End.Next.Op = OpRecursiveEnd
compiled.CurLen = totalLength
compiled.NextLen = nextTotalLength
compiled.Linked = true
} }
func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) { func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
@ -173,7 +402,7 @@ func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
for _, field := range c.fields { for _, field := range c.fields {
if field.isAnonymous { if field.isAnonymous {
structCode := field.getAnonymousStruct() structCode := field.getAnonymousStruct()
if structCode != nil { if structCode != nil && !structCode.isRecursive {
structCode.removeFieldsByTags(tags) structCode.removeFieldsByTags(tags)
if len(structCode.fields) > 0 { if len(structCode.fields) > 0 {
fields = append(fields, field) fields = append(fields, field)
@ -189,9 +418,25 @@ func (c *StructCode) removeFieldsByTags(tags runtime.StructTags) {
c.fields = fields c.fields = fields
} }
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()
}
type StructFieldCode struct { type StructFieldCode struct {
typ *runtime.Type typ *runtime.Type
key string key string
tag *runtime.StructTag
value Code value Code
offset uintptr offset uintptr
isAnonymous bool isAnonymous bool
@ -202,15 +447,128 @@ type StructFieldCode struct {
isNextOpPtrType bool isNextOpPtrType bool
} }
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
}
func (c *StructFieldCode) getAnonymousStruct() *StructCode { func (c *StructFieldCode) getAnonymousStruct() *StructCode {
if !c.isAnonymous { if !c.isAnonymous {
return nil return nil
} }
code, ok := c.value.(*StructCode) return c.getStruct()
if ok { }
return code
func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
var key string
if ctx.escapeKey {
rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
key = fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, c.key)))
} else {
key = fmt.Sprintf(`"%s":`, c.key)
}
var flags OpFlags
if c.isAnonymous {
flags |= AnonymousKeyFlags
}
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
}
field := &Opcode{
Idx: opcodeOffset(ctx.ptrIndex),
Flags: flags,
Key: key,
Offset: uint32(c.offset),
Type: c.typ,
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
DisplayKey: c.key,
}
ctx.incIndex()
codes := c.value.ToOpcode(ctx)
if isFirstField {
op := optimizeStructHeader(codes.First(), c.tag)
field.Op = op
field.NumBitSize = codes.First().NumBitSize
field.PtrNum = codes.First().PtrNum
fieldCodes := Opcodes{field}
if op.IsMultipleOpHead() {
field.Next = codes.First()
fieldCodes = append(fieldCodes, codes...)
} else {
ctx.decIndex()
}
if isEndField && !c.isAnonymous {
end := &Opcode{
Op: OpStructEnd,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
fieldCodes.Last().Next = end
fieldCodes = append(fieldCodes, end)
ctx.incIndex()
}
return fieldCodes
}
op := optimizeStructField(codes.First(), c.tag)
field.Op = op
field.NumBitSize = codes.First().NumBitSize
field.PtrNum = codes.First().PtrNum
fieldCodes := Opcodes{field}
if op.IsMultipleOpField() {
field.Next = codes.First()
fieldCodes = append(fieldCodes, codes...)
} else {
// optimize codes
ctx.decIndex()
}
if isEndField && !c.isAnonymous {
if isEnableStructEndOptimizationType(c.value.Type()) {
field.Op = field.Op.FieldToEnd()
} else {
end := &Opcode{
Op: OpStructEnd,
Idx: opcodeOffset(ctx.ptrIndex),
DisplayIdx: ctx.opcodeIndex,
Indent: ctx.indent,
}
fieldCodes.Last().Next = end
fieldCodes = append(fieldCodes, end)
ctx.incIndex()
}
}
return fieldCodes
}
func isEnableStructEndOptimizationType(typ CodeType2) bool {
switch typ {
case CodeTypeInt, CodeTypeUint, CodeTypeFloat, CodeTypeString, CodeTypeBool:
return true
default:
return false
} }
return nil
} }
type InterfaceCode struct { type InterfaceCode struct {
@ -222,8 +580,16 @@ func (c *InterfaceCode) Type() CodeType2 {
return CodeTypeInterface return CodeTypeInterface
} }
func (c *InterfaceCode) ToOpcode() []*Opcode { func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} var code *Opcode
switch {
case c.isPtr:
code = newOpCode(ctx, OpInterfacePtr)
default:
code = newOpCode(ctx, OpInterface)
}
ctx.incIndex()
return Opcodes{code}
} }
type MarshalJSONCode struct { type MarshalJSONCode struct {
@ -234,8 +600,22 @@ func (c *MarshalJSONCode) Type() CodeType2 {
return CodeTypeMarshalJSON return CodeTypeMarshalJSON
} }
func (c *MarshalJSONCode) ToOpcode() []*Opcode { func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} code := newOpCode(ctx, OpMarshalJSON)
typ := c.typ
if isPtrMarshalJSONType(typ) {
code.Flags |= AddrForMarshalerFlags
}
if typ.Implements(marshalJSONContextType) || runtime.PtrTo(typ).Implements(marshalJSONContextType) {
code.Flags |= MarshalerContextFlags
}
if isNilableType(typ) {
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
ctx.incIndex()
return Opcodes{code}
} }
type MarshalTextCode struct { type MarshalTextCode struct {
@ -246,21 +626,35 @@ func (c *MarshalTextCode) Type() CodeType2 {
return CodeTypeMarshalText return CodeTypeMarshalText
} }
func (c *MarshalTextCode) ToOpcode() []*Opcode { func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} code := newOpCode(ctx, OpMarshalText)
typ := c.typ
if !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType) {
code.Flags |= AddrForMarshalerFlags
}
if isNilableType(typ) {
code.Flags |= IsNilableTypeFlags
} else {
code.Flags &= ^IsNilableTypeFlags
}
ctx.incIndex()
return Opcodes{code}
} }
type PtrCode struct { type PtrCode struct {
typ *runtime.Type typ *runtime.Type
value Code value Code
ptrNum uint8
} }
func (c *PtrCode) Type() CodeType2 { func (c *PtrCode) Type() CodeType2 {
return CodeTypePtr return CodeTypePtr
} }
func (c *PtrCode) ToOpcode() []*Opcode { func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes {
return []*Opcode{} codes := c.value.ToOpcode(ctx)
codes.First().PtrNum = c.ptrNum
return codes
} }
func type2code(ctx *compileContext) (Code, error) { func type2code(ctx *compileContext) (Code, error) {
@ -450,6 +844,46 @@ func compileFloat642(ctx *compileContext, isPtr bool) (*FloatCode, error) {
return &FloatCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil return &FloatCode{typ: ctx.typ, bitSize: 64, isPtr: isPtr}, nil
} }
func compileIntString2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil
}
func compileInt8String2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil
}
func compileInt16String2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: 8, isString: true}, nil
}
func compileInt32String2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: 16, isString: true}, nil
}
func compileInt64String2(ctx *compileContext) (*IntCode, error) {
return &IntCode{typ: ctx.typ, bitSize: 64, isString: true}, nil
}
func compileUintString2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil
}
func compileUint8String2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: intSize, isString: true}, nil
}
func compileUint16String2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: 8, isString: true}, nil
}
func compileUint32String2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: 16, isString: true}, nil
}
func compileUint64String2(ctx *compileContext) (*UintCode, error) {
return &UintCode{typ: ctx.typ, bitSize: 64, isString: true}, nil
}
func compileString2(ctx *compileContext, isString bool) (*StringCode, error) { func compileString2(ctx *compileContext, isString bool) (*StringCode, error) {
return &StringCode{typ: ctx.typ, isString: isString}, nil return &StringCode{typ: ctx.typ, isString: isString}, nil
} }
@ -459,15 +893,47 @@ func compileBool2(ctx *compileContext, isString bool) (*BoolCode, error) {
} }
func compileSlice2(ctx *compileContext) (*SliceCode, error) { func compileSlice2(ctx *compileContext) (*SliceCode, error) {
return &SliceCode{typ: ctx.typ}, nil elem := ctx.typ.Elem()
code, err := compileListElem2(ctx.withType(elem))
if err != nil {
return nil, err
}
if code.Type() == CodeTypeStruct {
structCode := code.(*StructCode)
structCode.enableIndirect()
}
return &SliceCode{typ: ctx.typ, value: code}, nil
} }
func compileArray2(ctx *compileContext) (*ArrayCode, error) { func compileArray2(ctx *compileContext) (*ArrayCode, error) {
return &ArrayCode{typ: ctx.typ}, nil typ := ctx.typ
elem := typ.Elem()
code, err := compileListElem2(ctx.withType(elem))
if err != nil {
return nil, err
}
if code.Type() == CodeTypeStruct {
structCode := code.(*StructCode)
structCode.enableIndirect()
}
return &ArrayCode{typ: ctx.typ, value: code}, nil
} }
func compileMap2(ctx *compileContext) (*MapCode, error) { func compileMap2(ctx *compileContext) (*MapCode, error) {
return &MapCode{typ: ctx.typ}, nil typ := ctx.typ
keyCode, err := compileMapKey(ctx.withType(typ.Key()))
if err != nil {
return nil, err
}
valueCode, err := compileMapValue2(ctx.withType(typ.Elem()))
if err != nil {
return nil, err
}
if valueCode.Type() == CodeTypeStruct {
structCode := valueCode.(*StructCode)
structCode.enableIndirect()
}
return &MapCode{typ: ctx.typ, key: keyCode, value: valueCode}, nil
} }
func compileBytes2(ctx *compileContext, isPtr bool) (*BytesCode, error) { func compileBytes2(ctx *compileContext, isPtr bool) (*BytesCode, error) {
@ -491,21 +957,109 @@ func compilePtr2(ctx *compileContext) (*PtrCode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &PtrCode{typ: ctx.typ, value: code}, nil ptr, ok := code.(*PtrCode)
if ok {
return &PtrCode{typ: ctx.typ, value: ptr.value, ptrNum: ptr.ptrNum + 1}, nil
}
return &PtrCode{typ: ctx.typ, value: code, ptrNum: 1}, nil
}
func compileListElem2(ctx *compileContext) (Code, error) {
typ := ctx.typ
switch {
case isPtrMarshalJSONType(typ):
return compileMarshalJSON2(ctx)
case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
return compileMarshalText2(ctx)
case typ.Kind() == reflect.Map:
return compilePtr2(ctx.withType(runtime.PtrTo(typ)))
default:
code, err := type2codeWithPtr(ctx, false)
if err != nil {
return nil, err
}
ptr, ok := code.(*PtrCode)
if ok {
if ptr.value.Type() == CodeTypeMap {
ptr.ptrNum++
}
}
return code, nil
}
}
func compileMapKey(ctx *compileContext) (Code, error) {
typ := ctx.typ
switch {
case implementsMarshalJSON(typ):
return compileMarshalJSON2(ctx)
case implementsMarshalText(typ):
return compileMarshalText2(ctx)
}
switch typ.Kind() {
case reflect.Ptr:
return compilePtr2(ctx)
case reflect.String:
return compileString2(ctx, false)
case reflect.Int:
return compileIntString2(ctx)
case reflect.Int8:
return compileInt8String2(ctx)
case reflect.Int16:
return compileInt16String2(ctx)
case reflect.Int32:
return compileInt32String2(ctx)
case reflect.Int64:
return compileInt64String2(ctx)
case reflect.Uint:
return compileUintString2(ctx)
case reflect.Uint8:
return compileUint8String2(ctx)
case reflect.Uint16:
return compileUint16String2(ctx)
case reflect.Uint32:
return compileUint32String2(ctx)
case reflect.Uint64:
return compileUint64String2(ctx)
case reflect.Uintptr:
return compileUintString2(ctx)
}
return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
}
func compileMapValue2(ctx *compileContext) (Code, error) {
switch ctx.typ.Kind() {
case reflect.Map:
return compilePtr2(ctx.withType(runtime.PtrTo(ctx.typ)))
default:
code, err := type2codeWithPtr(ctx, false)
if err != nil {
return nil, err
}
ptr, ok := code.(*PtrCode)
if ok {
if ptr.value.Type() == CodeTypeMap {
ptr.ptrNum++
}
}
return code, nil
}
} }
func compileStruct2(ctx *compileContext, isPtr bool) (*StructCode, error) { func compileStruct2(ctx *compileContext, isPtr bool) (*StructCode, error) {
//typeptr := uintptr(unsafe.Pointer(typ))
//compiled := &CompiledCode{}
//ctx.structTypeToCompiledCode[typeptr] = compiled
// header => code => structField => code => end
// ^ |
// |__________|
typ := ctx.typ typ := ctx.typ
typeptr := uintptr(unsafe.Pointer(typ))
if code, exists := ctx.structTypeToCode[typeptr]; exists {
derefCode := *code
derefCode.isRecursive = true
return &derefCode, nil
}
indirect := runtime.IfaceIndir(typ)
code := &StructCode{typ: typ, isPtr: isPtr, isIndirect: indirect}
ctx.structTypeToCode[typeptr] = code
fieldNum := typ.NumField() fieldNum := typ.NumField()
//indirect := runtime.IfaceIndir(typ)
tags := typeToStructTags(typ) tags := typeToStructTags(typ)
code := &StructCode{typ: typ, isPtr: isPtr}
fields := []*StructFieldCode{} fields := []*StructFieldCode{}
for i, tag := range tags { for i, tag := range tags {
isOnlyOneFirstField := i == 0 && fieldNum == 1 isOnlyOneFirstField := i == 0 && fieldNum == 1
@ -518,12 +1072,35 @@ func compileStruct2(ctx *compileContext, isPtr bool) (*StructCode, error) {
if structCode != nil { if structCode != nil {
structCode.removeFieldsByTags(tags) structCode.removeFieldsByTags(tags)
} }
if isAssignableIndirect(field, isPtr) {
if indirect {
structCode.isIndirect = true
} else {
structCode.isIndirect = false
}
}
} else {
structCode := field.getStruct()
if structCode != nil {
if indirect {
// if parent is indirect type, set child indirect property to true
structCode.isIndirect = true
} else {
// if parent is not indirect type, set child indirect property to false.
// but if parent's indirect is false and isPtr is true, then indirect must be true.
// Do this only if indirectConversion is enabled at the end of compileStruct.
structCode.isIndirect = false
}
}
} }
fields = append(fields, field) fields = append(fields, field)
} }
fieldMap := getFieldMap(fields) fieldMap := getFieldMap(fields)
duplicatedFieldMap := getDuplicatedFieldMap(fieldMap) duplicatedFieldMap := getDuplicatedFieldMap(fieldMap)
code.fields = filteredDuplicatedFields(fields, duplicatedFieldMap) code.fields = filteredDuplicatedFields(fields, duplicatedFieldMap)
if !code.disableIndirectConversion && !indirect && isPtr {
code.enableIndirect()
}
return code, nil return code, nil
} }
@ -532,7 +1109,7 @@ func getFieldMap(fields []*StructFieldCode) map[string][]*StructFieldCode {
for _, field := range fields { for _, field := range fields {
if field.isAnonymous { if field.isAnonymous {
structCode := field.getAnonymousStruct() structCode := field.getAnonymousStruct()
if structCode != nil { if structCode != nil && !structCode.isRecursive {
for k, v := range getFieldMap(structCode.fields) { for k, v := range getFieldMap(structCode.fields) {
fieldMap[k] = append(fieldMap[k], v...) fieldMap[k] = append(fieldMap[k], v...)
} }
@ -571,7 +1148,7 @@ func filteredDuplicatedFields(fields []*StructFieldCode, duplicatedFieldMap map[
for _, field := range fields { for _, field := range fields {
if field.isAnonymous { if field.isAnonymous {
structCode := field.getAnonymousStruct() structCode := field.getAnonymousStruct()
if structCode != nil { if structCode != nil && !structCode.isRecursive {
structCode.fields = filteredDuplicatedFields(structCode.fields, duplicatedFieldMap) structCode.fields = filteredDuplicatedFields(structCode.fields, duplicatedFieldMap)
if len(structCode.fields) > 0 { if len(structCode.fields) > 0 {
filteredFields = append(filteredFields, field) filteredFields = append(filteredFields, field)
@ -629,6 +1206,7 @@ func (c *StructCode) compileStructField(ctx *compileContext, tag *runtime.Struct
fieldCode := &StructFieldCode{ fieldCode := &StructFieldCode{
typ: fieldType, typ: fieldType,
key: tag.Key, key: tag.Key,
tag: tag,
offset: field.Offset, offset: field.Offset,
isAnonymous: field.Anonymous && !tag.IsTaggedKey, isAnonymous: field.Anonymous && !tag.IsTaggedKey,
isTaggedKey: tag.IsTaggedKey, isTaggedKey: tag.IsTaggedKey,
@ -644,6 +1222,7 @@ func (c *StructCode) compileStructField(ctx *compileContext, tag *runtime.Struct
fieldCode.value = code fieldCode.value = code
fieldCode.isAddrForMarshaler = true fieldCode.isAddrForMarshaler = true
fieldCode.isNilCheck = false fieldCode.isNilCheck = false
c.isIndirect = false
c.disableIndirectConversion = true c.disableIndirectConversion = true
case isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(fieldType, isIndirectSpecialCase): case isMovePointerPositionFromHeadToFirstMarshalTextFieldCase(fieldType, isIndirectSpecialCase):
code, err := compileMarshalText2(ctx.withType(fieldType)) code, err := compileMarshalText2(ctx.withType(fieldType))
@ -653,6 +1232,7 @@ func (c *StructCode) compileStructField(ctx *compileContext, tag *runtime.Struct
fieldCode.value = code fieldCode.value = code
fieldCode.isAddrForMarshaler = true fieldCode.isAddrForMarshaler = true
fieldCode.isNilCheck = false fieldCode.isNilCheck = false
c.isIndirect = false
c.disableIndirectConversion = true c.disableIndirectConversion = true
case isPtr && isPtrMarshalJSONType(fieldType): case isPtr && isPtrMarshalJSONType(fieldType):
// *struct{ field T } // *struct{ field T }
@ -688,3 +1268,17 @@ func (c *StructCode) compileStructField(ctx *compileContext, tag *runtime.Struct
} }
return fieldCode, nil return fieldCode, nil
} }
func isAssignableIndirect(fieldCode *StructFieldCode, isPtr bool) bool {
if isPtr {
return false
}
codeType := fieldCode.value.Type()
if codeType == CodeTypeMarshalJSON {
return false
}
if codeType == CodeTypeMarshalText {
return false
}
return true
}

View File

@ -12,7 +12,6 @@ import (
"github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/errors"
"github.com/goccy/go-json/internal/runtime" "github.com/goccy/go-json/internal/runtime"
"github.com/k0kubun/pp"
) )
type marshalerContext interface { type marshalerContext interface {
@ -102,7 +101,12 @@ func compileHead(ctx *compileContext) (*Opcode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
pp.Println(code) //pp.Println(code)
newCtx := *ctx
codes := code.ToOpcode(&newCtx)
codes.Last().Next = newEndOp(ctx)
//pp.Println(codes)
fmt.Println(codes.First().Dump())
typ := ctx.typ typ := ctx.typ
switch { switch {

View File

@ -1,3 +1,4 @@
//go:build !race
// +build !race // +build !race
package encoder package encoder
@ -23,6 +24,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
noescapeKeyCode, err := compileHead(&compileContext{ noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -30,6 +32,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
escapeKeyCode, err := compileHead(&compileContext{ escapeKeyCode, err := compileHead(&compileContext{
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{},
escapeKey: true, escapeKey: true,
}) })
if err != nil { if err != nil {

View File

@ -1,3 +1,4 @@
//go:build race
// +build race // +build race
package encoder package encoder
@ -29,6 +30,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
noescapeKeyCode, err := compileHead(&compileContext{ noescapeKeyCode, err := compileHead(&compileContext{
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{},
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -36,6 +38,7 @@ func CompileToGetCodeSet(typeptr uintptr) (*OpcodeSet, error) {
escapeKeyCode, err := compileHead(&compileContext{ escapeKeyCode, err := compileHead(&compileContext{
typ: copiedType, typ: copiedType,
structTypeToCompiledCode: map[uintptr]*CompiledCode{}, structTypeToCompiledCode: map[uintptr]*CompiledCode{},
structTypeToCode: map[uintptr]*StructCode{},
escapeKey: true, escapeKey: true,
}) })
if err != nil { if err != nil {

View File

@ -15,6 +15,7 @@ type compileContext struct {
indent uint32 indent uint32
escapeKey bool escapeKey bool
structTypeToCompiledCode map[uintptr]*CompiledCode structTypeToCompiledCode map[uintptr]*CompiledCode
structTypeToCode map[uintptr]*StructCode
parent *compileContext parent *compileContext
} }
@ -27,6 +28,7 @@ func (c *compileContext) context() *compileContext {
indent: c.indent, indent: c.indent,
escapeKey: c.escapeKey, escapeKey: c.escapeKey,
structTypeToCompiledCode: c.structTypeToCompiledCode, structTypeToCompiledCode: c.structTypeToCompiledCode,
structTypeToCode: c.structTypeToCode,
parent: c, parent: c,
} }
} }