1024 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1024 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package encoder
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"unsafe"
 | 
						|
 | 
						|
	"github.com/goccy/go-json/internal/runtime"
 | 
						|
)
 | 
						|
 | 
						|
type Code interface {
 | 
						|
	Kind() CodeKind
 | 
						|
	ToOpcode(*compileContext) Opcodes
 | 
						|
	Filter(*FieldQuery) Code
 | 
						|
}
 | 
						|
 | 
						|
type AnonymousCode interface {
 | 
						|
	ToAnonymousOpcode(*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]
 | 
						|
}
 | 
						|
 | 
						|
func (o Opcodes) Add(codes ...*Opcode) Opcodes {
 | 
						|
	return append(o, codes...)
 | 
						|
}
 | 
						|
 | 
						|
type CodeKind int
 | 
						|
 | 
						|
const (
 | 
						|
	CodeKindInterface CodeKind = iota
 | 
						|
	CodeKindPtr
 | 
						|
	CodeKindInt
 | 
						|
	CodeKindUint
 | 
						|
	CodeKindFloat
 | 
						|
	CodeKindString
 | 
						|
	CodeKindBool
 | 
						|
	CodeKindStruct
 | 
						|
	CodeKindMap
 | 
						|
	CodeKindSlice
 | 
						|
	CodeKindArray
 | 
						|
	CodeKindBytes
 | 
						|
	CodeKindMarshalJSON
 | 
						|
	CodeKindMarshalText
 | 
						|
	CodeKindRecursive
 | 
						|
)
 | 
						|
 | 
						|
type IntCode struct {
 | 
						|
	typ      *runtime.Type
 | 
						|
	bitSize  uint8
 | 
						|
	isString bool
 | 
						|
	isPtr    bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *IntCode) Kind() CodeKind {
 | 
						|
	return CodeKindInt
 | 
						|
}
 | 
						|
 | 
						|
func (c *IntCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		code = newOpCode(ctx, c.typ, OpIntPtr)
 | 
						|
	case c.isString:
 | 
						|
		code = newOpCode(ctx, c.typ, OpIntString)
 | 
						|
	default:
 | 
						|
		code = newOpCode(ctx, c.typ, OpInt)
 | 
						|
	}
 | 
						|
	code.NumBitSize = c.bitSize
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *IntCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type UintCode struct {
 | 
						|
	typ      *runtime.Type
 | 
						|
	bitSize  uint8
 | 
						|
	isString bool
 | 
						|
	isPtr    bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *UintCode) Kind() CodeKind {
 | 
						|
	return CodeKindUint
 | 
						|
}
 | 
						|
 | 
						|
func (c *UintCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		code = newOpCode(ctx, c.typ, OpUintPtr)
 | 
						|
	case c.isString:
 | 
						|
		code = newOpCode(ctx, c.typ, OpUintString)
 | 
						|
	default:
 | 
						|
		code = newOpCode(ctx, c.typ, OpUint)
 | 
						|
	}
 | 
						|
	code.NumBitSize = c.bitSize
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *UintCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type FloatCode struct {
 | 
						|
	typ     *runtime.Type
 | 
						|
	bitSize uint8
 | 
						|
	isPtr   bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *FloatCode) Kind() CodeKind {
 | 
						|
	return CodeKindFloat
 | 
						|
}
 | 
						|
 | 
						|
func (c *FloatCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		switch c.bitSize {
 | 
						|
		case 32:
 | 
						|
			code = newOpCode(ctx, c.typ, OpFloat32Ptr)
 | 
						|
		default:
 | 
						|
			code = newOpCode(ctx, c.typ, OpFloat64Ptr)
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		switch c.bitSize {
 | 
						|
		case 32:
 | 
						|
			code = newOpCode(ctx, c.typ, OpFloat32)
 | 
						|
		default:
 | 
						|
			code = newOpCode(ctx, c.typ, OpFloat64)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *FloatCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type StringCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	isPtr bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *StringCode) Kind() CodeKind {
 | 
						|
	return CodeKindString
 | 
						|
}
 | 
						|
 | 
						|
func (c *StringCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	isJSONNumberType := c.typ == runtime.Type2RType(jsonNumberType)
 | 
						|
	var code *Opcode
 | 
						|
	if c.isPtr {
 | 
						|
		if isJSONNumberType {
 | 
						|
			code = newOpCode(ctx, c.typ, OpNumberPtr)
 | 
						|
		} else {
 | 
						|
			code = newOpCode(ctx, c.typ, OpStringPtr)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if isJSONNumberType {
 | 
						|
			code = newOpCode(ctx, c.typ, OpNumber)
 | 
						|
		} else {
 | 
						|
			code = newOpCode(ctx, c.typ, OpString)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *StringCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type BoolCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	isPtr bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *BoolCode) Kind() CodeKind {
 | 
						|
	return CodeKindBool
 | 
						|
}
 | 
						|
 | 
						|
func (c *BoolCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		code = newOpCode(ctx, c.typ, OpBoolPtr)
 | 
						|
	default:
 | 
						|
		code = newOpCode(ctx, c.typ, OpBool)
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *BoolCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type BytesCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	isPtr bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *BytesCode) Kind() CodeKind {
 | 
						|
	return CodeKindBytes
 | 
						|
}
 | 
						|
 | 
						|
func (c *BytesCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		code = newOpCode(ctx, c.typ, OpBytesPtr)
 | 
						|
	default:
 | 
						|
		code = newOpCode(ctx, c.typ, OpBytes)
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *BytesCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type SliceCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	value Code
 | 
						|
}
 | 
						|
 | 
						|
func (c *SliceCode) Kind() CodeKind {
 | 
						|
	return CodeKindSlice
 | 
						|
}
 | 
						|
 | 
						|
func (c *SliceCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	// header => opcode => elem => end
 | 
						|
	//             ^        |
 | 
						|
	//             |________|
 | 
						|
	size := c.typ.Elem().Size()
 | 
						|
	header := newSliceHeaderCode(ctx, c.typ)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	ctx.incIndent()
 | 
						|
	codes := c.value.ToOpcode(ctx)
 | 
						|
	ctx.decIndent()
 | 
						|
 | 
						|
	codes.First().Flags |= IndirectFlags
 | 
						|
	elemCode := newSliceElemCode(ctx, c.typ.Elem(), header, size)
 | 
						|
	ctx.incIndex()
 | 
						|
	end := newOpCode(ctx, c.typ, OpSliceEnd)
 | 
						|
	ctx.incIndex()
 | 
						|
	header.End = end
 | 
						|
	header.Next = codes.First()
 | 
						|
	codes.Last().Next = elemCode
 | 
						|
	elemCode.Next = codes.First()
 | 
						|
	elemCode.End = end
 | 
						|
	return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
 | 
						|
}
 | 
						|
 | 
						|
func (c *SliceCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type ArrayCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	value Code
 | 
						|
}
 | 
						|
 | 
						|
func (c *ArrayCode) Kind() CodeKind {
 | 
						|
	return CodeKindArray
 | 
						|
}
 | 
						|
 | 
						|
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, c.typ, alen)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	ctx.incIndent()
 | 
						|
	codes := c.value.ToOpcode(ctx)
 | 
						|
	ctx.decIndent()
 | 
						|
 | 
						|
	codes.First().Flags |= IndirectFlags
 | 
						|
 | 
						|
	elemCode := newArrayElemCode(ctx, elem, header, alen, size)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	end := newOpCode(ctx, c.typ, OpArrayEnd)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	header.End = end
 | 
						|
	header.Next = codes.First()
 | 
						|
	codes.Last().Next = elemCode
 | 
						|
	elemCode.Next = codes.First()
 | 
						|
	elemCode.End = end
 | 
						|
 | 
						|
	return Opcodes{header}.Add(codes...).Add(elemCode).Add(end)
 | 
						|
}
 | 
						|
 | 
						|
func (c *ArrayCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type MapCode struct {
 | 
						|
	typ   *runtime.Type
 | 
						|
	key   Code
 | 
						|
	value Code
 | 
						|
}
 | 
						|
 | 
						|
func (c *MapCode) Kind() CodeKind {
 | 
						|
	return CodeKindMap
 | 
						|
}
 | 
						|
 | 
						|
func (c *MapCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	// header => code => value => code => key => code => value => code => end
 | 
						|
	//                                     ^                       |
 | 
						|
	//                                     |_______________________|
 | 
						|
	header := newMapHeaderCode(ctx, c.typ)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	keyCodes := c.key.ToOpcode(ctx)
 | 
						|
 | 
						|
	value := newMapValueCode(ctx, c.typ.Elem(), header)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	ctx.incIndent()
 | 
						|
	valueCodes := c.value.ToOpcode(ctx)
 | 
						|
	ctx.decIndent()
 | 
						|
 | 
						|
	valueCodes.First().Flags |= IndirectFlags
 | 
						|
 | 
						|
	key := newMapKeyCode(ctx, c.typ.Key(), header)
 | 
						|
	ctx.incIndex()
 | 
						|
 | 
						|
	end := newMapEndCode(ctx, c.typ, 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}.Add(keyCodes...).Add(value).Add(valueCodes...).Add(key).Add(end)
 | 
						|
}
 | 
						|
 | 
						|
func (c *MapCode) Filter(_ *FieldQuery) Code {
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
type StructCode struct {
 | 
						|
	typ                       *runtime.Type
 | 
						|
	fields                    []*StructFieldCode
 | 
						|
	isPtr                     bool
 | 
						|
	disableIndirectConversion bool
 | 
						|
	isIndirect                bool
 | 
						|
	isRecursive               bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructCode) Kind() CodeKind {
 | 
						|
	return CodeKindStruct
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructCode) lastFieldCode(field *StructFieldCode, firstField *Opcode) *Opcode {
 | 
						|
	if isEmbeddedStruct(field) {
 | 
						|
		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 truly struct head operation.
 | 
						|
	for firstField.Op == OpStructHead || firstField.Op == OpStructField {
 | 
						|
		firstField = firstField.Next
 | 
						|
	}
 | 
						|
	lastField := firstField
 | 
						|
	for lastField.NextField != nil {
 | 
						|
		lastField = lastField.NextField
 | 
						|
	}
 | 
						|
	return lastField
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	// header => code => structField => code => end
 | 
						|
	//                        ^          |
 | 
						|
	//                        |__________|
 | 
						|
	if c.isRecursive {
 | 
						|
		recursive := newRecursiveCode(ctx, c.typ, &CompiledCode{})
 | 
						|
		recursive.Type = c.typ
 | 
						|
		ctx.incIndex()
 | 
						|
		*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
 | 
						|
		return Opcodes{recursive}
 | 
						|
	}
 | 
						|
	codes := Opcodes{}
 | 
						|
	var prevField *Opcode
 | 
						|
	ctx.incIndent()
 | 
						|
	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
 | 
						|
			}
 | 
						|
		}
 | 
						|
		firstField := fieldCodes.First()
 | 
						|
		if len(codes) > 0 {
 | 
						|
			codes.Last().Next = firstField
 | 
						|
			firstField.Idx = codes.First().Idx
 | 
						|
		}
 | 
						|
		if prevField != nil {
 | 
						|
			prevField.NextField = firstField
 | 
						|
		}
 | 
						|
		if isEndField {
 | 
						|
			endField := fieldCodes.Last()
 | 
						|
			if len(codes) > 0 {
 | 
						|
				codes.First().End = endField
 | 
						|
			} else {
 | 
						|
				firstField.End = endField
 | 
						|
			}
 | 
						|
			codes = codes.Add(fieldCodes...)
 | 
						|
			break
 | 
						|
		}
 | 
						|
		prevField = c.lastFieldCode(field, firstField)
 | 
						|
		codes = codes.Add(fieldCodes...)
 | 
						|
	}
 | 
						|
	if len(codes) == 0 {
 | 
						|
		head := &Opcode{
 | 
						|
			Op:         OpStructHead,
 | 
						|
			Idx:        opcodeOffset(ctx.ptrIndex),
 | 
						|
			Type:       c.typ,
 | 
						|
			DisplayIdx: ctx.opcodeIndex,
 | 
						|
			Indent:     ctx.indent,
 | 
						|
		}
 | 
						|
		ctx.incOpcodeIndex()
 | 
						|
		end := &Opcode{
 | 
						|
			Op:         OpStructEnd,
 | 
						|
			Idx:        opcodeOffset(ctx.ptrIndex),
 | 
						|
			DisplayIdx: ctx.opcodeIndex,
 | 
						|
			Indent:     ctx.indent,
 | 
						|
		}
 | 
						|
		head.NextField = end
 | 
						|
		head.Next = end
 | 
						|
		head.End = end
 | 
						|
		codes = codes.Add(head, end)
 | 
						|
		ctx.incIndex()
 | 
						|
	}
 | 
						|
	ctx.decIndent()
 | 
						|
	ctx.structTypeToCodes[uintptr(unsafe.Pointer(c.typ))] = codes
 | 
						|
	return codes
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
 | 
						|
	// header => code => structField => code => end
 | 
						|
	//                        ^          |
 | 
						|
	//                        |__________|
 | 
						|
	if c.isRecursive {
 | 
						|
		recursive := newRecursiveCode(ctx, c.typ, &CompiledCode{})
 | 
						|
		recursive.Type = c.typ
 | 
						|
		ctx.incIndex()
 | 
						|
		*ctx.recursiveCodes = append(*ctx.recursiveCodes, recursive)
 | 
						|
		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
 | 
						|
			}
 | 
						|
		}
 | 
						|
		firstField := fieldCodes.First()
 | 
						|
		if len(codes) > 0 {
 | 
						|
			codes.Last().Next = firstField
 | 
						|
			firstField.Idx = codes.First().Idx
 | 
						|
		}
 | 
						|
		if prevField != nil {
 | 
						|
			prevField.NextField = firstField
 | 
						|
		}
 | 
						|
		if isEndField {
 | 
						|
			lastField := fieldCodes.Last()
 | 
						|
			if len(codes) > 0 {
 | 
						|
				codes.First().End = lastField
 | 
						|
			} else {
 | 
						|
				firstField.End = lastField
 | 
						|
			}
 | 
						|
		}
 | 
						|
		prevField = firstField
 | 
						|
		codes = codes.Add(fieldCodes...)
 | 
						|
	}
 | 
						|
	return codes
 | 
						|
}
 | 
						|
 | 
						|
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()
 | 
						|
			if structCode != nil && !structCode.isRecursive {
 | 
						|
				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
 | 
						|
}
 | 
						|
 | 
						|
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()
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructCode) Filter(query *FieldQuery) Code {
 | 
						|
	fieldMap := map[string]*FieldQuery{}
 | 
						|
	for _, field := range query.Fields {
 | 
						|
		fieldMap[field.Name] = field
 | 
						|
	}
 | 
						|
	fields := make([]*StructFieldCode, 0, len(c.fields))
 | 
						|
	for _, field := range c.fields {
 | 
						|
		query, exists := fieldMap[field.key]
 | 
						|
		if !exists {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		fieldCode := &StructFieldCode{
 | 
						|
			typ:                field.typ,
 | 
						|
			key:                field.key,
 | 
						|
			tag:                field.tag,
 | 
						|
			value:              field.value,
 | 
						|
			offset:             field.offset,
 | 
						|
			isAnonymous:        field.isAnonymous,
 | 
						|
			isTaggedKey:        field.isTaggedKey,
 | 
						|
			isNilableType:      field.isNilableType,
 | 
						|
			isNilCheck:         field.isNilCheck,
 | 
						|
			isAddrForMarshaler: field.isAddrForMarshaler,
 | 
						|
			isNextOpPtrType:    field.isNextOpPtrType,
 | 
						|
		}
 | 
						|
		if len(query.Fields) > 0 {
 | 
						|
			fieldCode.value = fieldCode.value.Filter(query)
 | 
						|
		}
 | 
						|
		fields = append(fields, fieldCode)
 | 
						|
	}
 | 
						|
	return &StructCode{
 | 
						|
		typ:                       c.typ,
 | 
						|
		fields:                    fields,
 | 
						|
		isPtr:                     c.isPtr,
 | 
						|
		disableIndirectConversion: c.disableIndirectConversion,
 | 
						|
		isIndirect:                c.isIndirect,
 | 
						|
		isRecursive:               c.isRecursive,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type StructFieldCode struct {
 | 
						|
	typ                *runtime.Type
 | 
						|
	key                string
 | 
						|
	tag                *runtime.StructTag
 | 
						|
	value              Code
 | 
						|
	offset             uintptr
 | 
						|
	isAnonymous        bool
 | 
						|
	isTaggedKey        bool
 | 
						|
	isNilableType      bool
 | 
						|
	isNilCheck         bool
 | 
						|
	isAddrForMarshaler bool
 | 
						|
	isNextOpPtrType    bool
 | 
						|
	isMarshalerContext 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 {
 | 
						|
	if !c.isAnonymous {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return c.getStruct()
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructFieldCode) headerOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
 | 
						|
	value := valueCodes.First()
 | 
						|
	op := optimizeStructHeader(value, c.tag)
 | 
						|
	field.Op = op
 | 
						|
	if value.Flags&MarshalerContextFlags != 0 {
 | 
						|
		field.Flags |= MarshalerContextFlags
 | 
						|
	}
 | 
						|
	field.NumBitSize = value.NumBitSize
 | 
						|
	field.PtrNum = value.PtrNum
 | 
						|
	field.FieldQuery = value.FieldQuery
 | 
						|
	fieldCodes := Opcodes{field}
 | 
						|
	if op.IsMultipleOpHead() {
 | 
						|
		field.Next = value
 | 
						|
		fieldCodes = fieldCodes.Add(valueCodes...)
 | 
						|
	} else {
 | 
						|
		ctx.decIndex()
 | 
						|
	}
 | 
						|
	return fieldCodes
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructFieldCode) fieldOpcodes(ctx *compileContext, field *Opcode, valueCodes Opcodes) Opcodes {
 | 
						|
	value := valueCodes.First()
 | 
						|
	op := optimizeStructField(value, c.tag)
 | 
						|
	field.Op = op
 | 
						|
	if value.Flags&MarshalerContextFlags != 0 {
 | 
						|
		field.Flags |= MarshalerContextFlags
 | 
						|
	}
 | 
						|
	field.NumBitSize = value.NumBitSize
 | 
						|
	field.PtrNum = value.PtrNum
 | 
						|
	field.FieldQuery = value.FieldQuery
 | 
						|
 | 
						|
	fieldCodes := Opcodes{field}
 | 
						|
	if op.IsMultipleOpField() {
 | 
						|
		field.Next = value
 | 
						|
		fieldCodes = fieldCodes.Add(valueCodes...)
 | 
						|
	} else {
 | 
						|
		ctx.decIndex()
 | 
						|
	}
 | 
						|
	return fieldCodes
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
	code := codes.First()
 | 
						|
	for code.Op == OpStructField || code.Op == OpStructHead {
 | 
						|
		code = code.Next
 | 
						|
	}
 | 
						|
	for code.NextField != nil {
 | 
						|
		code = code.NextField
 | 
						|
	}
 | 
						|
	code.NextField = end
 | 
						|
 | 
						|
	codes = codes.Add(end)
 | 
						|
	ctx.incOpcodeIndex()
 | 
						|
	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)
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructFieldCode) flags() OpFlags {
 | 
						|
	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
 | 
						|
	}
 | 
						|
	if c.isAnonymous {
 | 
						|
		flags |= AnonymousKeyFlags
 | 
						|
	}
 | 
						|
	if c.isMarshalerContext {
 | 
						|
		flags |= MarshalerContextFlags
 | 
						|
	}
 | 
						|
	return flags
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructFieldCode) toValueOpcodes(ctx *compileContext) Opcodes {
 | 
						|
	if c.isAnonymous {
 | 
						|
		anonymCode, ok := c.value.(AnonymousCode)
 | 
						|
		if ok {
 | 
						|
			return anonymCode.ToAnonymousOpcode(ctx)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return c.value.ToOpcode(ctx)
 | 
						|
}
 | 
						|
 | 
						|
func (c *StructFieldCode) ToOpcode(ctx *compileContext, isFirstField, isEndField bool) Opcodes {
 | 
						|
	field := &Opcode{
 | 
						|
		Idx:        opcodeOffset(ctx.ptrIndex),
 | 
						|
		Flags:      c.flags(),
 | 
						|
		Key:        c.structKey(ctx),
 | 
						|
		Offset:     uint32(c.offset),
 | 
						|
		Type:       c.typ,
 | 
						|
		DisplayIdx: ctx.opcodeIndex,
 | 
						|
		Indent:     ctx.indent,
 | 
						|
		DisplayKey: c.key,
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	valueCodes := c.toValueOpcodes(ctx)
 | 
						|
	if isFirstField {
 | 
						|
		codes := c.headerOpcodes(ctx, field, valueCodes)
 | 
						|
		if isEndField {
 | 
						|
			codes = c.addStructEndCode(ctx, codes)
 | 
						|
		}
 | 
						|
		return codes
 | 
						|
	}
 | 
						|
	codes := c.fieldOpcodes(ctx, field, valueCodes)
 | 
						|
	if isEndField {
 | 
						|
		if isEnableStructEndOptimization(c.value) {
 | 
						|
			field.Op = field.Op.FieldToEnd()
 | 
						|
		} else {
 | 
						|
			codes = c.addStructEndCode(ctx, codes)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return codes
 | 
						|
}
 | 
						|
 | 
						|
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,
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	valueCodes := c.toValueOpcodes(ctx)
 | 
						|
	if isFirstField {
 | 
						|
		return c.headerOpcodes(ctx, field, valueCodes)
 | 
						|
	}
 | 
						|
	return c.fieldOpcodes(ctx, field, valueCodes)
 | 
						|
}
 | 
						|
 | 
						|
func isEnableStructEndOptimization(value Code) bool {
 | 
						|
	switch value.Kind() {
 | 
						|
	case CodeKindInt,
 | 
						|
		CodeKindUint,
 | 
						|
		CodeKindFloat,
 | 
						|
		CodeKindString,
 | 
						|
		CodeKindBool,
 | 
						|
		CodeKindBytes:
 | 
						|
		return true
 | 
						|
	case CodeKindPtr:
 | 
						|
		return isEnableStructEndOptimization(value.(*PtrCode).value)
 | 
						|
	default:
 | 
						|
		return false
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type InterfaceCode struct {
 | 
						|
	typ        *runtime.Type
 | 
						|
	fieldQuery *FieldQuery
 | 
						|
	isPtr      bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *InterfaceCode) Kind() CodeKind {
 | 
						|
	return CodeKindInterface
 | 
						|
}
 | 
						|
 | 
						|
func (c *InterfaceCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var code *Opcode
 | 
						|
	switch {
 | 
						|
	case c.isPtr:
 | 
						|
		code = newOpCode(ctx, c.typ, OpInterfacePtr)
 | 
						|
	default:
 | 
						|
		code = newOpCode(ctx, c.typ, OpInterface)
 | 
						|
	}
 | 
						|
	code.FieldQuery = c.fieldQuery
 | 
						|
	if c.typ.NumMethod() > 0 {
 | 
						|
		code.Flags |= NonEmptyInterfaceFlags
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *InterfaceCode) Filter(query *FieldQuery) Code {
 | 
						|
	return &InterfaceCode{
 | 
						|
		typ:        c.typ,
 | 
						|
		fieldQuery: query,
 | 
						|
		isPtr:      c.isPtr,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type MarshalJSONCode struct {
 | 
						|
	typ                *runtime.Type
 | 
						|
	fieldQuery         *FieldQuery
 | 
						|
	isAddrForMarshaler bool
 | 
						|
	isNilableType      bool
 | 
						|
	isMarshalerContext bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalJSONCode) Kind() CodeKind {
 | 
						|
	return CodeKindMarshalJSON
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalJSONCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	code := newOpCode(ctx, c.typ, OpMarshalJSON)
 | 
						|
	code.FieldQuery = c.fieldQuery
 | 
						|
	if c.isAddrForMarshaler {
 | 
						|
		code.Flags |= AddrForMarshalerFlags
 | 
						|
	}
 | 
						|
	if c.isMarshalerContext {
 | 
						|
		code.Flags |= MarshalerContextFlags
 | 
						|
	}
 | 
						|
	if c.isNilableType {
 | 
						|
		code.Flags |= IsNilableTypeFlags
 | 
						|
	} else {
 | 
						|
		code.Flags &= ^IsNilableTypeFlags
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalJSONCode) Filter(query *FieldQuery) Code {
 | 
						|
	return &MarshalJSONCode{
 | 
						|
		typ:                c.typ,
 | 
						|
		fieldQuery:         query,
 | 
						|
		isAddrForMarshaler: c.isAddrForMarshaler,
 | 
						|
		isNilableType:      c.isNilableType,
 | 
						|
		isMarshalerContext: c.isMarshalerContext,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type MarshalTextCode struct {
 | 
						|
	typ                *runtime.Type
 | 
						|
	fieldQuery         *FieldQuery
 | 
						|
	isAddrForMarshaler bool
 | 
						|
	isNilableType      bool
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalTextCode) Kind() CodeKind {
 | 
						|
	return CodeKindMarshalText
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalTextCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	code := newOpCode(ctx, c.typ, OpMarshalText)
 | 
						|
	code.FieldQuery = c.fieldQuery
 | 
						|
	if c.isAddrForMarshaler {
 | 
						|
		code.Flags |= AddrForMarshalerFlags
 | 
						|
	}
 | 
						|
	if c.isNilableType {
 | 
						|
		code.Flags |= IsNilableTypeFlags
 | 
						|
	} else {
 | 
						|
		code.Flags &= ^IsNilableTypeFlags
 | 
						|
	}
 | 
						|
	ctx.incIndex()
 | 
						|
	return Opcodes{code}
 | 
						|
}
 | 
						|
 | 
						|
func (c *MarshalTextCode) Filter(query *FieldQuery) Code {
 | 
						|
	return &MarshalTextCode{
 | 
						|
		typ:                c.typ,
 | 
						|
		fieldQuery:         query,
 | 
						|
		isAddrForMarshaler: c.isAddrForMarshaler,
 | 
						|
		isNilableType:      c.isNilableType,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
type PtrCode struct {
 | 
						|
	typ    *runtime.Type
 | 
						|
	value  Code
 | 
						|
	ptrNum uint8
 | 
						|
}
 | 
						|
 | 
						|
func (c *PtrCode) Kind() CodeKind {
 | 
						|
	return CodeKindPtr
 | 
						|
}
 | 
						|
 | 
						|
func (c *PtrCode) ToOpcode(ctx *compileContext) Opcodes {
 | 
						|
	codes := c.value.ToOpcode(ctx)
 | 
						|
	codes.First().Op = convertPtrOp(codes.First())
 | 
						|
	codes.First().PtrNum = c.ptrNum
 | 
						|
	return codes
 | 
						|
}
 | 
						|
 | 
						|
func (c *PtrCode) ToAnonymousOpcode(ctx *compileContext) Opcodes {
 | 
						|
	var codes Opcodes
 | 
						|
	anonymCode, ok := c.value.(AnonymousCode)
 | 
						|
	if ok {
 | 
						|
		codes = anonymCode.ToAnonymousOpcode(ctx)
 | 
						|
	} else {
 | 
						|
		codes = c.value.ToOpcode(ctx)
 | 
						|
	}
 | 
						|
	codes.First().Op = convertPtrOp(codes.First())
 | 
						|
	codes.First().PtrNum = c.ptrNum
 | 
						|
	return codes
 | 
						|
}
 | 
						|
 | 
						|
func (c *PtrCode) Filter(query *FieldQuery) Code {
 | 
						|
	return &PtrCode{
 | 
						|
		typ:    c.typ,
 | 
						|
		value:  c.value.Filter(query),
 | 
						|
		ptrNum: c.ptrNum,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func convertPtrOp(code *Opcode) OpType {
 | 
						|
	ptrHeadOp := code.Op.HeadToPtrHead()
 | 
						|
	if code.Op != ptrHeadOp {
 | 
						|
		if code.PtrNum > 0 {
 | 
						|
			// ptr field and ptr head
 | 
						|
			code.PtrNum--
 | 
						|
		}
 | 
						|
		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
 | 
						|
}
 | 
						|
 | 
						|
func isEmbeddedStruct(field *StructFieldCode) bool {
 | 
						|
	if !field.isAnonymous {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	t := field.typ
 | 
						|
	if t.Kind() == reflect.Ptr {
 | 
						|
		t = t.Elem()
 | 
						|
	}
 | 
						|
	return t.Kind() == reflect.Struct
 | 
						|
}
 |