mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	
		
			
				
	
	
		
			1570 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			1570 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
| package encoder
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 	"sync/atomic"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"github.com/goccy/go-json/internal/errors"
 | |
| 	"github.com/goccy/go-json/internal/runtime"
 | |
| )
 | |
| 
 | |
| type marshalerContext interface {
 | |
| 	MarshalJSON(context.Context) ([]byte, error)
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	marshalJSONType        = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
 | |
| 	marshalJSONContextType = reflect.TypeOf((*marshalerContext)(nil)).Elem()
 | |
| 	marshalTextType        = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
 | |
| 	jsonNumberType         = reflect.TypeOf(json.Number(""))
 | |
| 	cachedOpcodeSets       []*OpcodeSet
 | |
| 	cachedOpcodeMap        unsafe.Pointer // map[uintptr]*OpcodeSet
 | |
| 	typeAddr               *runtime.TypeAddr
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	typeAddr = runtime.AnalyzeTypeAddr()
 | |
| 	if typeAddr == nil {
 | |
| 		typeAddr = &runtime.TypeAddr{}
 | |
| 	}
 | |
| 	cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift)
 | |
| }
 | |
| 
 | |
| func loadOpcodeMap() map[uintptr]*OpcodeSet {
 | |
| 	p := atomic.LoadPointer(&cachedOpcodeMap)
 | |
| 	return *(*map[uintptr]*OpcodeSet)(unsafe.Pointer(&p))
 | |
| }
 | |
| 
 | |
| func storeOpcodeSet(typ uintptr, set *OpcodeSet, m map[uintptr]*OpcodeSet) {
 | |
| 	newOpcodeMap := make(map[uintptr]*OpcodeSet, len(m)+1)
 | |
| 	newOpcodeMap[typ] = set
 | |
| 
 | |
| 	for k, v := range m {
 | |
| 		newOpcodeMap[k] = v
 | |
| 	}
 | |
| 
 | |
| 	atomic.StorePointer(&cachedOpcodeMap, *(*unsafe.Pointer)(unsafe.Pointer(&newOpcodeMap)))
 | |
| }
 | |
| 
 | |
| func compileToGetCodeSetSlowPath(typeptr uintptr) (*OpcodeSet, error) {
 | |
| 	opcodeMap := loadOpcodeMap()
 | |
| 	if codeSet, exists := opcodeMap[typeptr]; exists {
 | |
| 		return codeSet, nil
 | |
| 	}
 | |
| 
 | |
| 	// noescape trick for header.typ ( reflect.*rtype )
 | |
| 	copiedType := *(**runtime.Type)(unsafe.Pointer(&typeptr))
 | |
| 
 | |
| 	noescapeKeyCode, err := compileHead(&compileContext{
 | |
| 		typ:                      copiedType,
 | |
| 		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	escapeKeyCode, err := compileHead(&compileContext{
 | |
| 		typ:                      copiedType,
 | |
| 		structTypeToCompiledCode: map[uintptr]*CompiledCode{},
 | |
| 		escapeKey:                true,
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	noescapeKeyCode = copyOpcode(noescapeKeyCode)
 | |
| 	escapeKeyCode = copyOpcode(escapeKeyCode)
 | |
| 	setTotalLengthToInterfaceOp(noescapeKeyCode)
 | |
| 	setTotalLengthToInterfaceOp(escapeKeyCode)
 | |
| 	interfaceNoescapeKeyCode := copyToInterfaceOpcode(noescapeKeyCode)
 | |
| 	interfaceEscapeKeyCode := copyToInterfaceOpcode(escapeKeyCode)
 | |
| 	codeLength := noescapeKeyCode.TotalLength()
 | |
| 	codeSet := &OpcodeSet{
 | |
| 		Type:                     copiedType,
 | |
| 		NoescapeKeyCode:          noescapeKeyCode,
 | |
| 		EscapeKeyCode:            escapeKeyCode,
 | |
| 		InterfaceNoescapeKeyCode: interfaceNoescapeKeyCode,
 | |
| 		InterfaceEscapeKeyCode:   interfaceEscapeKeyCode,
 | |
| 		CodeLength:               codeLength,
 | |
| 		EndCode:                  ToEndCode(interfaceNoescapeKeyCode),
 | |
| 	}
 | |
| 	storeOpcodeSet(typeptr, codeSet, opcodeMap)
 | |
| 	return codeSet, nil
 | |
| }
 | |
| 
 | |
| func compileHead(ctx *compileContext) (*Opcode, error) {
 | |
| 	typ := ctx.typ
 | |
| 	switch {
 | |
| 	case implementsMarshalJSON(typ):
 | |
| 		return compileMarshalJSON(ctx)
 | |
| 	case implementsMarshalText(typ):
 | |
| 		return compileMarshalText(ctx)
 | |
| 	}
 | |
| 
 | |
| 	isPtr := false
 | |
| 	orgType := typ
 | |
| 	if typ.Kind() == reflect.Ptr {
 | |
| 		typ = typ.Elem()
 | |
| 		isPtr = true
 | |
| 	}
 | |
| 	switch {
 | |
| 	case implementsMarshalJSON(typ):
 | |
| 		return compileMarshalJSON(ctx)
 | |
| 	case implementsMarshalText(typ):
 | |
| 		return compileMarshalText(ctx)
 | |
| 	}
 | |
| 	switch typ.Kind() {
 | |
| 	case reflect.Slice:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		elem := typ.Elem()
 | |
| 		if elem.Kind() == reflect.Uint8 {
 | |
| 			p := runtime.PtrTo(elem)
 | |
| 			if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
 | |
| 				if isPtr {
 | |
| 					return compileBytesPtr(ctx)
 | |
| 				}
 | |
| 				return compileBytes(ctx)
 | |
| 			}
 | |
| 		}
 | |
| 		code, err := compileSlice(ctx)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		optimizeStructEnd(code)
 | |
| 		linkRecursiveCode(code)
 | |
| 		return code, nil
 | |
| 	case reflect.Map:
 | |
| 		if isPtr {
 | |
| 			return compilePtr(ctx.withType(runtime.PtrTo(typ)))
 | |
| 		}
 | |
| 		code, err := compileMap(ctx.withType(typ))
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		optimizeStructEnd(code)
 | |
| 		linkRecursiveCode(code)
 | |
| 		return code, nil
 | |
| 	case reflect.Struct:
 | |
| 		code, err := compileStruct(ctx.withType(typ), isPtr)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		optimizeStructEnd(code)
 | |
| 		linkRecursiveCode(code)
 | |
| 		return code, nil
 | |
| 	case reflect.Int:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileIntPtr(ctx)
 | |
| 		}
 | |
| 		return compileInt(ctx)
 | |
| 	case reflect.Int8:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileInt8Ptr(ctx)
 | |
| 		}
 | |
| 		return compileInt8(ctx)
 | |
| 	case reflect.Int16:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileInt16Ptr(ctx)
 | |
| 		}
 | |
| 		return compileInt16(ctx)
 | |
| 	case reflect.Int32:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileInt32Ptr(ctx)
 | |
| 		}
 | |
| 		return compileInt32(ctx)
 | |
| 	case reflect.Int64:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileInt64Ptr(ctx)
 | |
| 		}
 | |
| 		return compileInt64(ctx)
 | |
| 	case reflect.Uint, reflect.Uintptr:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileUintPtr(ctx)
 | |
| 		}
 | |
| 		return compileUint(ctx)
 | |
| 	case reflect.Uint8:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileUint8Ptr(ctx)
 | |
| 		}
 | |
| 		return compileUint8(ctx)
 | |
| 	case reflect.Uint16:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileUint16Ptr(ctx)
 | |
| 		}
 | |
| 		return compileUint16(ctx)
 | |
| 	case reflect.Uint32:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileUint32Ptr(ctx)
 | |
| 		}
 | |
| 		return compileUint32(ctx)
 | |
| 	case reflect.Uint64:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileUint64Ptr(ctx)
 | |
| 		}
 | |
| 		return compileUint64(ctx)
 | |
| 	case reflect.Float32:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileFloat32Ptr(ctx)
 | |
| 		}
 | |
| 		return compileFloat32(ctx)
 | |
| 	case reflect.Float64:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileFloat64Ptr(ctx)
 | |
| 		}
 | |
| 		return compileFloat64(ctx)
 | |
| 	case reflect.String:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileStringPtr(ctx)
 | |
| 		}
 | |
| 		return compileString(ctx)
 | |
| 	case reflect.Bool:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileBoolPtr(ctx)
 | |
| 		}
 | |
| 		return compileBool(ctx)
 | |
| 	case reflect.Interface:
 | |
| 		ctx := ctx.withType(typ)
 | |
| 		if isPtr {
 | |
| 			return compileInterfacePtr(ctx)
 | |
| 		}
 | |
| 		return compileInterface(ctx)
 | |
| 	default:
 | |
| 		if isPtr && typ.Implements(marshalTextType) {
 | |
| 			typ = orgType
 | |
| 		}
 | |
| 		code, err := compile(ctx.withType(typ), isPtr)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		optimizeStructEnd(code)
 | |
| 		linkRecursiveCode(code)
 | |
| 		return code, nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func linkRecursiveCode(c *Opcode) {
 | |
| 	for code := c; code.Op != OpEnd && code.Op != OpRecursiveEnd; {
 | |
| 		switch code.Op {
 | |
| 		case OpRecursive, OpRecursivePtr:
 | |
| 			if code.Jmp.Linked {
 | |
| 				code = code.Next
 | |
| 				continue
 | |
| 			}
 | |
| 			code.Jmp.Code = copyOpcode(code.Jmp.Code)
 | |
| 
 | |
| 			c := code.Jmp.Code
 | |
| 			c.End.Next = newEndOp(&compileContext{})
 | |
| 			c.Op = c.Op.PtrHeadToHead()
 | |
| 
 | |
| 			beforeLastCode := c.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(code.TotalLength() + 3)
 | |
| 			nextTotalLength := uintptr(c.TotalLength() + 3)
 | |
| 
 | |
| 			c.End.Next.Op = OpRecursiveEnd
 | |
| 
 | |
| 			code.Jmp.CurLen = totalLength
 | |
| 			code.Jmp.NextLen = nextTotalLength
 | |
| 			code.Jmp.Linked = true
 | |
| 
 | |
| 			linkRecursiveCode(code.Jmp.Code)
 | |
| 
 | |
| 			code = code.Next
 | |
| 			continue
 | |
| 		}
 | |
| 		switch code.Op.CodeType() {
 | |
| 		case CodeArrayElem, CodeSliceElem, CodeMapKey:
 | |
| 			code = code.End
 | |
| 		default:
 | |
| 			code = code.Next
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func optimizeStructEnd(c *Opcode) {
 | |
| 	for code := c; code.Op != OpEnd; {
 | |
| 		if code.Op == OpRecursive || code.Op == OpRecursivePtr {
 | |
| 			// 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") {
 | |
| 					// 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
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func implementsMarshalJSON(typ *runtime.Type) bool {
 | |
| 	if !implementsMarshalJSONType(typ) {
 | |
| 		return false
 | |
| 	}
 | |
| 	if typ.Kind() != reflect.Ptr {
 | |
| 		return true
 | |
| 	}
 | |
| 	// type kind is reflect.Ptr
 | |
| 	if !implementsMarshalJSONType(typ.Elem()) {
 | |
| 		return true
 | |
| 	}
 | |
| 	// needs to dereference
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func implementsMarshalText(typ *runtime.Type) 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 compile(ctx *compileContext, isPtr bool) (*Opcode, error) {
 | |
| 	typ := ctx.typ
 | |
| 	switch {
 | |
| 	case implementsMarshalJSON(typ):
 | |
| 		return compileMarshalJSON(ctx)
 | |
| 	case implementsMarshalText(typ):
 | |
| 		return compileMarshalText(ctx)
 | |
| 	}
 | |
| 	switch typ.Kind() {
 | |
| 	case reflect.Ptr:
 | |
| 		return compilePtr(ctx)
 | |
| 	case reflect.Slice:
 | |
| 		elem := typ.Elem()
 | |
| 		if elem.Kind() == reflect.Uint8 {
 | |
| 			p := runtime.PtrTo(elem)
 | |
| 			if !implementsMarshalJSONType(p) && !p.Implements(marshalTextType) {
 | |
| 				return compileBytes(ctx)
 | |
| 			}
 | |
| 		}
 | |
| 		return compileSlice(ctx)
 | |
| 	case reflect.Array:
 | |
| 		return compileArray(ctx)
 | |
| 	case reflect.Map:
 | |
| 		return compileMap(ctx)
 | |
| 	case reflect.Struct:
 | |
| 		return compileStruct(ctx, isPtr)
 | |
| 	case reflect.Interface:
 | |
| 		return compileInterface(ctx)
 | |
| 	case reflect.Int:
 | |
| 		return compileInt(ctx)
 | |
| 	case reflect.Int8:
 | |
| 		return compileInt8(ctx)
 | |
| 	case reflect.Int16:
 | |
| 		return compileInt16(ctx)
 | |
| 	case reflect.Int32:
 | |
| 		return compileInt32(ctx)
 | |
| 	case reflect.Int64:
 | |
| 		return compileInt64(ctx)
 | |
| 	case reflect.Uint:
 | |
| 		return compileUint(ctx)
 | |
| 	case reflect.Uint8:
 | |
| 		return compileUint8(ctx)
 | |
| 	case reflect.Uint16:
 | |
| 		return compileUint16(ctx)
 | |
| 	case reflect.Uint32:
 | |
| 		return compileUint32(ctx)
 | |
| 	case reflect.Uint64:
 | |
| 		return compileUint64(ctx)
 | |
| 	case reflect.Uintptr:
 | |
| 		return compileUint(ctx)
 | |
| 	case reflect.Float32:
 | |
| 		return compileFloat32(ctx)
 | |
| 	case reflect.Float64:
 | |
| 		return compileFloat64(ctx)
 | |
| 	case reflect.String:
 | |
| 		return compileString(ctx)
 | |
| 	case reflect.Bool:
 | |
| 		return compileBool(ctx)
 | |
| 	}
 | |
| 	return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
 | |
| }
 | |
| 
 | |
| 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 compileKey(ctx *compileContext) (*Opcode, error) {
 | |
| 	typ := ctx.typ
 | |
| 	switch {
 | |
| 	case implementsMarshalJSON(typ):
 | |
| 		return compileMarshalJSON(ctx)
 | |
| 	case implementsMarshalText(typ):
 | |
| 		return compileMarshalText(ctx)
 | |
| 	}
 | |
| 	switch typ.Kind() {
 | |
| 	case reflect.Ptr:
 | |
| 		return compilePtr(ctx)
 | |
| 	case reflect.String:
 | |
| 		return compileString(ctx)
 | |
| 	case reflect.Int:
 | |
| 		return compileIntString(ctx)
 | |
| 	case reflect.Int8:
 | |
| 		return compileInt8String(ctx)
 | |
| 	case reflect.Int16:
 | |
| 		return compileInt16String(ctx)
 | |
| 	case reflect.Int32:
 | |
| 		return compileInt32String(ctx)
 | |
| 	case reflect.Int64:
 | |
| 		return compileInt64String(ctx)
 | |
| 	case reflect.Uint:
 | |
| 		return compileUintString(ctx)
 | |
| 	case reflect.Uint8:
 | |
| 		return compileUint8String(ctx)
 | |
| 	case reflect.Uint16:
 | |
| 		return compileUint16String(ctx)
 | |
| 	case reflect.Uint32:
 | |
| 		return compileUint32String(ctx)
 | |
| 	case reflect.Uint64:
 | |
| 		return compileUint64String(ctx)
 | |
| 	case reflect.Uintptr:
 | |
| 		return compileUintString(ctx)
 | |
| 	}
 | |
| 	return nil, &errors.UnsupportedTypeError{Type: runtime.RType2Type(typ)}
 | |
| }
 | |
| 
 | |
| func compilePtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compile(ctx.withType(ctx.typ.Elem()), true)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = convertPtrOp(code)
 | |
| 	code.PtrNum++
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileMarshalJSON(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpMarshalJSON)
 | |
| 	typ := ctx.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 code, nil
 | |
| }
 | |
| 
 | |
| func compileMarshalText(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpMarshalText)
 | |
| 	typ := ctx.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 code, nil
 | |
| }
 | |
| 
 | |
| const intSize = 32 << (^uint(0) >> 63)
 | |
| 
 | |
| func compileInt(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpInt)
 | |
| 	code.NumBitSize = intSize
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileIntPtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInt(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpIntPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt8(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpInt)
 | |
| 	code.NumBitSize = 8
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt8Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInt8(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpIntPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt16(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpInt)
 | |
| 	code.NumBitSize = 16
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt16Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInt16(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpIntPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt32(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpInt)
 | |
| 	code.NumBitSize = 32
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt32Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInt32(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpIntPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt64(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpInt)
 | |
| 	code.NumBitSize = 64
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt64Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInt64(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpIntPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUint)
 | |
| 	code.NumBitSize = intSize
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUintPtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileUint(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpUintPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint8(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUint)
 | |
| 	code.NumBitSize = 8
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint8Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileUint8(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpUintPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint16(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUint)
 | |
| 	code.NumBitSize = 16
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint16Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileUint16(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpUintPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint32(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUint)
 | |
| 	code.NumBitSize = 32
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint32Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileUint32(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpUintPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint64(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUint)
 | |
| 	code.NumBitSize = 64
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint64Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileUint64(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpUintPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileIntString(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpIntString)
 | |
| 	code.NumBitSize = intSize
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt8String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpIntString)
 | |
| 	code.NumBitSize = 8
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt16String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpIntString)
 | |
| 	code.NumBitSize = 16
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt32String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpIntString)
 | |
| 	code.NumBitSize = 32
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInt64String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpIntString)
 | |
| 	code.NumBitSize = 64
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUintString(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUintString)
 | |
| 	code.NumBitSize = intSize
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint8String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUintString)
 | |
| 	code.NumBitSize = 8
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint16String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUintString)
 | |
| 	code.NumBitSize = 16
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint32String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUintString)
 | |
| 	code.NumBitSize = 32
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileUint64String(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpUintString)
 | |
| 	code.NumBitSize = 64
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileFloat32(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpFloat32)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileFloat32Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileFloat32(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpFloat32Ptr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileFloat64(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpFloat64)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileFloat64Ptr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileFloat64(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpFloat64Ptr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileString(ctx *compileContext) (*Opcode, error) {
 | |
| 	var op OpType
 | |
| 	if ctx.typ == runtime.Type2RType(jsonNumberType) {
 | |
| 		op = OpNumber
 | |
| 	} else {
 | |
| 		op = OpString
 | |
| 	}
 | |
| 	code := newOpCode(ctx, op)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileStringPtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileString(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if code.Op == OpNumber {
 | |
| 		code.Op = OpNumberPtr
 | |
| 	} else {
 | |
| 		code.Op = OpStringPtr
 | |
| 	}
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileBool(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpBool)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileBoolPtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileBool(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpBoolPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileBytes(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newOpCode(ctx, OpBytes)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileBytesPtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileBytes(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpBytesPtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInterface(ctx *compileContext) (*Opcode, error) {
 | |
| 	code := newInterfaceCode(ctx)
 | |
| 	ctx.incIndex()
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileInterfacePtr(ctx *compileContext) (*Opcode, error) {
 | |
| 	code, err := compileInterface(ctx)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Op = OpInterfacePtr
 | |
| 	return code, nil
 | |
| }
 | |
| 
 | |
| func compileSlice(ctx *compileContext) (*Opcode, error) {
 | |
| 	elem := ctx.typ.Elem()
 | |
| 	size := elem.Size()
 | |
| 
 | |
| 	header := newSliceHeaderCode(ctx)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	code, err := compileListElem(ctx.withType(elem).incIndent())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Flags |= IndirectFlags
 | |
| 
 | |
| 	// header => opcode => elem => end
 | |
| 	//             ^        |
 | |
| 	//             |________|
 | |
| 
 | |
| 	elemCode := newSliceElemCode(ctx, header, size)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	end := newOpCode(ctx, OpSliceEnd)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	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 compileListElem(ctx *compileContext) (*Opcode, error) {
 | |
| 	typ := ctx.typ
 | |
| 	switch {
 | |
| 	case isPtrMarshalJSONType(typ):
 | |
| 		return compileMarshalJSON(ctx)
 | |
| 	case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType):
 | |
| 		return compileMarshalText(ctx)
 | |
| 	case typ.Kind() == reflect.Map:
 | |
| 		return compilePtr(ctx.withType(runtime.PtrTo(typ)))
 | |
| 	default:
 | |
| 		code, err := compile(ctx, false)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if code.Op == OpMapPtr {
 | |
| 			code.PtrNum++
 | |
| 		}
 | |
| 		return code, nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func compileArray(ctx *compileContext) (*Opcode, error) {
 | |
| 	typ := ctx.typ
 | |
| 	elem := typ.Elem()
 | |
| 	alen := typ.Len()
 | |
| 	size := elem.Size()
 | |
| 
 | |
| 	header := newArrayHeaderCode(ctx, alen)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	code, err := compileListElem(ctx.withType(elem).incIndent())
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	code.Flags |= IndirectFlags
 | |
| 	// header => opcode => elem => end
 | |
| 	//             ^        |
 | |
| 	//             |________|
 | |
| 
 | |
| 	elemCode := newArrayElemCode(ctx, header, alen, size)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	end := newOpCode(ctx, OpArrayEnd)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	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 compileMap(ctx *compileContext) (*Opcode, error) {
 | |
| 	// header => code => value => code => key => code => value => code => end
 | |
| 	//                                     ^                       |
 | |
| 	//                                     |_______________________|
 | |
| 	ctx = ctx.incIndent()
 | |
| 	header := newMapHeaderCode(ctx)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	typ := ctx.typ
 | |
| 	keyType := ctx.typ.Key()
 | |
| 	keyCode, err := compileKey(ctx.withType(keyType))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	value := newMapValueCode(ctx, header)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	valueCode, err := compileMapValue(ctx.withType(typ.Elem()))
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	valueCode.Flags |= IndirectFlags
 | |
| 
 | |
| 	key := newMapKeyCode(ctx, header)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	ctx = ctx.decIndent()
 | |
| 
 | |
| 	end := newMapEndCode(ctx, header)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	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
 | |
| 	value.End = end
 | |
| 
 | |
| 	return (*Opcode)(unsafe.Pointer(header)), nil
 | |
| }
 | |
| 
 | |
| func compileMapValue(ctx *compileContext) (*Opcode, error) {
 | |
| 	switch ctx.typ.Kind() {
 | |
| 	case reflect.Map:
 | |
| 		return compilePtr(ctx.withType(runtime.PtrTo(ctx.typ)))
 | |
| 	default:
 | |
| 		code, err := compile(ctx, false)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if code.Op == OpMapPtr {
 | |
| 			code.PtrNum++
 | |
| 		}
 | |
| 		return code, nil
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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 recursiveCode(ctx *compileContext, jmp *CompiledCode) *Opcode {
 | |
| 	code := newRecursiveCode(ctx, jmp)
 | |
| 	ctx.incIndex()
 | |
| 	return code
 | |
| }
 | |
| 
 | |
| func compiledCode(ctx *compileContext) *Opcode {
 | |
| 	typ := ctx.typ
 | |
| 	typeptr := uintptr(unsafe.Pointer(typ))
 | |
| 	if cc, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
 | |
| 		return recursiveCode(ctx, cc)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func structHeader(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
 | |
| 	op := optimizeStructHeader(valueCode, tag)
 | |
| 	fieldCode.Op = op
 | |
| 	fieldCode.NumBitSize = valueCode.NumBitSize
 | |
| 	fieldCode.PtrNum = valueCode.PtrNum
 | |
| 	if op.IsMultipleOpHead() {
 | |
| 		return valueCode.BeforeLastCode()
 | |
| 	}
 | |
| 	ctx.decOpcodeIndex()
 | |
| 	return fieldCode
 | |
| }
 | |
| 
 | |
| func structField(ctx *compileContext, fieldCode *Opcode, valueCode *Opcode, tag *runtime.StructTag) *Opcode {
 | |
| 	op := optimizeStructField(valueCode, tag)
 | |
| 	fieldCode.Op = op
 | |
| 	fieldCode.NumBitSize = valueCode.NumBitSize
 | |
| 	fieldCode.PtrNum = valueCode.PtrNum
 | |
| 	if op.IsMultipleOpField() {
 | |
| 		return valueCode.BeforeLastCode()
 | |
| 	}
 | |
| 	ctx.decIndex()
 | |
| 	return fieldCode
 | |
| }
 | |
| 
 | |
| func isNotExistsField(head *Opcode) bool {
 | |
| 	if head == nil {
 | |
| 		return false
 | |
| 	}
 | |
| 	if head.Op != OpStructHead {
 | |
| 		return false
 | |
| 	}
 | |
| 	if (head.Flags & AnonymousHeadFlags) == 0 {
 | |
| 		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
 | |
| 	}
 | |
| 	return isNotExistsField(head.Next)
 | |
| }
 | |
| 
 | |
| func optimizeAnonymousFields(head *Opcode) {
 | |
| 	code := head
 | |
| 	var prev *Opcode
 | |
| 	removedFields := map[*Opcode]struct{}{}
 | |
| 	for {
 | |
| 		if code.Op == OpStructEnd {
 | |
| 			break
 | |
| 		}
 | |
| 		if code.Op == OpStructField {
 | |
| 			codeType := code.Next.Op.CodeType()
 | |
| 			if codeType == CodeStructField {
 | |
| 				if isNotExistsField(code.Next) {
 | |
| 					code.Next = code.NextField
 | |
| 					diff := code.Next.DisplayIdx - code.DisplayIdx
 | |
| 					for i := uint32(0); i < diff; i++ {
 | |
| 						code.Next.decOpcodeIndex()
 | |
| 					}
 | |
| 					linkPrevToNextField(code, removedFields)
 | |
| 					code = prev
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		prev = code
 | |
| 		code = code.NextField
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type structFieldPair struct {
 | |
| 	prevField   *Opcode
 | |
| 	curField    *Opcode
 | |
| 	isTaggedKey bool
 | |
| 	linked      bool
 | |
| }
 | |
| 
 | |
| func anonymousStructFieldPairMap(tags runtime.StructTags, named string, valueCode *Opcode) map[string][]structFieldPair {
 | |
| 	anonymousFields := map[string][]structFieldPair{}
 | |
| 	f := valueCode
 | |
| 	var prevAnonymousField *Opcode
 | |
| 	removedFields := map[*Opcode]struct{}{}
 | |
| 	for {
 | |
| 		existsKey := tags.ExistsKey(f.DisplayKey)
 | |
| 		isHeadOp := strings.Contains(f.Op.String(), "Head")
 | |
| 		if existsKey && f.Next != nil && strings.Contains(f.Next.Op.String(), "Recursive") {
 | |
| 			// through
 | |
| 		} else if isHeadOp && (f.Flags&AnonymousHeadFlags) == 0 {
 | |
| 			if existsKey {
 | |
| 				// TODO: need to remove this head
 | |
| 				f.Op = OpStructHead
 | |
| 				f.Flags |= AnonymousKeyFlags
 | |
| 				f.Flags |= AnonymousHeadFlags
 | |
| 			} else if named == "" {
 | |
| 				f.Flags |= AnonymousHeadFlags
 | |
| 			}
 | |
| 		} else if named == "" && f.Op == OpStructEnd {
 | |
| 			f.Op = OpStructAnonymousEnd
 | |
| 		} else if existsKey {
 | |
| 			diff := f.NextField.DisplayIdx - f.DisplayIdx
 | |
| 			for i := uint32(0); i < diff; i++ {
 | |
| 				f.NextField.decOpcodeIndex()
 | |
| 			}
 | |
| 			linkPrevToNextField(f, removedFields)
 | |
| 		}
 | |
| 
 | |
| 		if f.DisplayKey == "" {
 | |
| 			if f.NextField == nil {
 | |
| 				break
 | |
| 			}
 | |
| 			prevAnonymousField = f
 | |
| 			f = f.NextField
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		key := fmt.Sprintf("%s.%s", named, f.DisplayKey)
 | |
| 		anonymousFields[key] = append(anonymousFields[key], structFieldPair{
 | |
| 			prevField:   prevAnonymousField,
 | |
| 			curField:    f,
 | |
| 			isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
 | |
| 		})
 | |
| 		if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
 | |
| 			for k, v := range anonymousFieldPairRecursively(named, f.Next) {
 | |
| 				anonymousFields[k] = append(anonymousFields[k], v...)
 | |
| 			}
 | |
| 		}
 | |
| 		if f.NextField == nil {
 | |
| 			break
 | |
| 		}
 | |
| 		prevAnonymousField = f
 | |
| 		f = f.NextField
 | |
| 	}
 | |
| 	return anonymousFields
 | |
| }
 | |
| 
 | |
| func anonymousFieldPairRecursively(named string, valueCode *Opcode) map[string][]structFieldPair {
 | |
| 	anonymousFields := map[string][]structFieldPair{}
 | |
| 	f := valueCode
 | |
| 	var prevAnonymousField *Opcode
 | |
| 	for {
 | |
| 		if f.DisplayKey != "" && (f.Flags&AnonymousHeadFlags) != 0 {
 | |
| 			key := fmt.Sprintf("%s.%s", named, f.DisplayKey)
 | |
| 			anonymousFields[key] = append(anonymousFields[key], structFieldPair{
 | |
| 				prevField:   prevAnonymousField,
 | |
| 				curField:    f,
 | |
| 				isTaggedKey: (f.Flags & IsTaggedKeyFlags) != 0,
 | |
| 			})
 | |
| 			if f.Next != nil && f.NextField != f.Next && f.Next.Op.CodeType() == CodeStructField {
 | |
| 				for k, v := range anonymousFieldPairRecursively(named, f.Next) {
 | |
| 					anonymousFields[k] = append(anonymousFields[k], v...)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if f.NextField == nil {
 | |
| 			break
 | |
| 		}
 | |
| 		prevAnonymousField = f
 | |
| 		f = f.NextField
 | |
| 	}
 | |
| 	return anonymousFields
 | |
| }
 | |
| 
 | |
| func optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
 | |
| 	removedFields := map[*Opcode]struct{}{}
 | |
| 	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 = OpStructHead
 | |
| 						fieldPair.curField.Flags |= AnonymousHeadFlags
 | |
| 						fieldPair.curField.Flags |= AnonymousKeyFlags
 | |
| 					} else {
 | |
| 						diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
 | |
| 						for i := uint32(0); i < diff; i++ {
 | |
| 							fieldPair.curField.NextField.decOpcodeIndex()
 | |
| 						}
 | |
| 						removedFields[fieldPair.curField] = struct{}{}
 | |
| 						linkPrevToNextField(fieldPair.curField, removedFields)
 | |
| 					}
 | |
| 					fieldPair.linked = true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if len(taggedPairs) > 1 {
 | |
| 			for _, fieldPair := range taggedPairs {
 | |
| 				if !fieldPair.linked {
 | |
| 					if fieldPair.prevField == nil {
 | |
| 						// head operation
 | |
| 						fieldPair.curField.Op = OpStructHead
 | |
| 						fieldPair.curField.Flags |= AnonymousHeadFlags
 | |
| 						fieldPair.curField.Flags |= AnonymousKeyFlags
 | |
| 					} else {
 | |
| 						diff := fieldPair.curField.NextField.DisplayIdx - fieldPair.curField.DisplayIdx
 | |
| 						removedFields[fieldPair.curField] = struct{}{}
 | |
| 						for i := uint32(0); i < diff; i++ {
 | |
| 							fieldPair.curField.NextField.decOpcodeIndex()
 | |
| 						}
 | |
| 						linkPrevToNextField(fieldPair.curField, removedFields)
 | |
| 					}
 | |
| 					fieldPair.linked = true
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			for _, fieldPair := range taggedPairs {
 | |
| 				fieldPair.curField.Flags &= ^IsTaggedKeyFlags
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func isNilableType(typ *runtime.Type) bool {
 | |
| 	switch typ.Kind() {
 | |
| 	case reflect.Ptr:
 | |
| 		return true
 | |
| 	case reflect.Map:
 | |
| 		return true
 | |
| 	case reflect.Func:
 | |
| 		return true
 | |
| 	default:
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func compileStruct(ctx *compileContext, isPtr bool) (*Opcode, error) {
 | |
| 	if code := compiledCode(ctx); code != nil {
 | |
| 		return code, nil
 | |
| 	}
 | |
| 	typ := ctx.typ
 | |
| 	typeptr := uintptr(unsafe.Pointer(typ))
 | |
| 	compiled := &CompiledCode{}
 | |
| 	ctx.structTypeToCompiledCode[typeptr] = compiled
 | |
| 	// header => code => structField => code => end
 | |
| 	//                        ^          |
 | |
| 	//                        |__________|
 | |
| 	fieldNum := typ.NumField()
 | |
| 	indirect := runtime.IfaceIndir(typ)
 | |
| 	fieldIdx := 0
 | |
| 	disableIndirectConversion := false
 | |
| 	var (
 | |
| 		head      *Opcode
 | |
| 		code      *Opcode
 | |
| 		prevField *Opcode
 | |
| 	)
 | |
| 	ctx = ctx.incIndent()
 | |
| 	tags := runtime.StructTags{}
 | |
| 	anonymousFields := map[string][]structFieldPair{}
 | |
| 	for i := 0; i < fieldNum; i++ {
 | |
| 		field := typ.Field(i)
 | |
| 		if runtime.IsIgnoredStructField(field) {
 | |
| 			continue
 | |
| 		}
 | |
| 		tags = append(tags, runtime.StructTagFromField(field))
 | |
| 	}
 | |
| 	for i, tag := range tags {
 | |
| 		field := tag.Field
 | |
| 		fieldType := runtime.Type2RType(field.Type)
 | |
| 		fieldOpcodeIndex := ctx.opcodeIndex
 | |
| 		fieldPtrIndex := ctx.ptrIndex
 | |
| 		ctx.incIndex()
 | |
| 
 | |
| 		nilcheck := true
 | |
| 		addrForMarshaler := false
 | |
| 		isIndirectSpecialCase := isPtr && i == 0 && fieldNum == 1
 | |
| 		isNilableType := isNilableType(fieldType)
 | |
| 
 | |
| 		var valueCode *Opcode
 | |
| 		switch {
 | |
| 		case isIndirectSpecialCase && !isNilableType && isPtrMarshalJSONType(fieldType):
 | |
| 			// *struct{ field T } => struct { field *T }
 | |
| 			// func (*T) MarshalJSON() ([]byte, error)
 | |
| 			// move pointer position from head to first field
 | |
| 			code, err := compileMarshalJSON(ctx.withType(fieldType))
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			addrForMarshaler = true
 | |
| 			valueCode = code
 | |
| 			nilcheck = false
 | |
| 			indirect = false
 | |
| 			disableIndirectConversion = true
 | |
| 		case isIndirectSpecialCase && !isNilableType && isPtrMarshalTextType(fieldType):
 | |
| 			// *struct{ field T } => struct { field *T }
 | |
| 			// func (*T) MarshalText() ([]byte, error)
 | |
| 			// move pointer position from head to first field
 | |
| 			code, err := compileMarshalText(ctx.withType(fieldType))
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			addrForMarshaler = true
 | |
| 			valueCode = code
 | |
| 			nilcheck = false
 | |
| 			indirect = false
 | |
| 			disableIndirectConversion = true
 | |
| 		case isPtr && isPtrMarshalJSONType(fieldType):
 | |
| 			// *struct{ field T }
 | |
| 			// func (*T) MarshalJSON() ([]byte, error)
 | |
| 			code, err := compileMarshalJSON(ctx.withType(fieldType))
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			addrForMarshaler = true
 | |
| 			nilcheck = false
 | |
| 			valueCode = code
 | |
| 		case isPtr && isPtrMarshalTextType(fieldType):
 | |
| 			// *struct{ field T }
 | |
| 			// func (*T) MarshalText() ([]byte, error)
 | |
| 			code, err := compileMarshalText(ctx.withType(fieldType))
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			addrForMarshaler = true
 | |
| 			nilcheck = false
 | |
| 			valueCode = code
 | |
| 		default:
 | |
| 			code, err := compile(ctx.withType(fieldType), isPtr)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 			valueCode = code
 | |
| 		}
 | |
| 
 | |
| 		if field.Anonymous {
 | |
| 			tagKey := ""
 | |
| 			if tag.IsTaggedKey {
 | |
| 				tagKey = tag.Key
 | |
| 			}
 | |
| 			for k, v := range anonymousStructFieldPairMap(tags, tagKey, valueCode) {
 | |
| 				anonymousFields[k] = append(anonymousFields[k], v...)
 | |
| 			}
 | |
| 			valueCode.decIndent()
 | |
| 
 | |
| 			// fix issue144
 | |
| 			if !(isPtr && strings.Contains(valueCode.Op.String(), "Marshal")) {
 | |
| 				if indirect {
 | |
| 					valueCode.Flags |= IndirectFlags
 | |
| 				} else {
 | |
| 					valueCode.Flags &= ^IndirectFlags
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			if indirect {
 | |
| 				// if parent is indirect type, set child indirect property to true
 | |
| 				valueCode.Flags |= IndirectFlags
 | |
| 			} 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.
 | |
| 				if i == 0 {
 | |
| 					valueCode.Flags &= ^IndirectFlags
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		var flags OpFlags
 | |
| 		if indirect {
 | |
| 			flags |= IndirectFlags
 | |
| 		}
 | |
| 		if field.Anonymous {
 | |
| 			flags |= AnonymousKeyFlags
 | |
| 		}
 | |
| 		if tag.IsTaggedKey {
 | |
| 			flags |= IsTaggedKeyFlags
 | |
| 		}
 | |
| 		if nilcheck {
 | |
| 			flags |= NilCheckFlags
 | |
| 		}
 | |
| 		if addrForMarshaler {
 | |
| 			flags |= AddrForMarshalerFlags
 | |
| 		}
 | |
| 		if strings.Contains(valueCode.Op.String(), "Ptr") || valueCode.Op == OpInterface {
 | |
| 			flags |= IsNextOpPtrTypeFlags
 | |
| 		}
 | |
| 		if isNilableType {
 | |
| 			flags |= IsNilableTypeFlags
 | |
| 		}
 | |
| 		var key string
 | |
| 		if ctx.escapeKey {
 | |
| 			rctx := &RuntimeContext{Option: &Option{Flag: HTMLEscapeOption}}
 | |
| 			key = fmt.Sprintf(`%s:`, string(AppendString(rctx, []byte{}, tag.Key)))
 | |
| 		} else {
 | |
| 			key = fmt.Sprintf(`"%s":`, tag.Key)
 | |
| 		}
 | |
| 		fieldCode := &Opcode{
 | |
| 			Idx:        opcodeOffset(fieldPtrIndex),
 | |
| 			Next:       valueCode,
 | |
| 			Flags:      flags,
 | |
| 			Key:        key,
 | |
| 			Offset:     uint32(field.Offset),
 | |
| 			Type:       valueCode.Type,
 | |
| 			DisplayIdx: fieldOpcodeIndex,
 | |
| 			Indent:     ctx.indent,
 | |
| 			DisplayKey: tag.Key,
 | |
| 		}
 | |
| 		if fieldIdx == 0 {
 | |
| 			code = structHeader(ctx, fieldCode, valueCode, tag)
 | |
| 			head = fieldCode
 | |
| 			prevField = fieldCode
 | |
| 		} else {
 | |
| 			fieldCode.Idx = head.Idx
 | |
| 			code.Next = fieldCode
 | |
| 			code = structField(ctx, fieldCode, valueCode, tag)
 | |
| 			prevField.NextField = fieldCode
 | |
| 			fieldCode.PrevField = prevField
 | |
| 			prevField = fieldCode
 | |
| 		}
 | |
| 		fieldIdx++
 | |
| 	}
 | |
| 
 | |
| 	structEndCode := &Opcode{
 | |
| 		Op:     OpStructEnd,
 | |
| 		Next:   newEndOp(ctx),
 | |
| 		Type:   nil,
 | |
| 		Indent: ctx.indent,
 | |
| 	}
 | |
| 
 | |
| 	ctx = ctx.decIndent()
 | |
| 
 | |
| 	// no struct field
 | |
| 	if head == nil {
 | |
| 		head = &Opcode{
 | |
| 			Op:         OpStructHead,
 | |
| 			Idx:        opcodeOffset(ctx.ptrIndex),
 | |
| 			NextField:  structEndCode,
 | |
| 			Type:       typ,
 | |
| 			DisplayIdx: ctx.opcodeIndex,
 | |
| 			Indent:     ctx.indent,
 | |
| 		}
 | |
| 		structEndCode.PrevField = head
 | |
| 		ctx.incIndex()
 | |
| 		code = head
 | |
| 	}
 | |
| 
 | |
| 	structEndCode.DisplayIdx = ctx.opcodeIndex
 | |
| 	structEndCode.Idx = opcodeOffset(ctx.ptrIndex)
 | |
| 	ctx.incIndex()
 | |
| 
 | |
| 	if prevField != nil && prevField.NextField == nil {
 | |
| 		prevField.NextField = structEndCode
 | |
| 		structEndCode.PrevField = prevField
 | |
| 	}
 | |
| 
 | |
| 	head.End = structEndCode
 | |
| 	code.Next = structEndCode
 | |
| 	optimizeConflictAnonymousFields(anonymousFields)
 | |
| 	optimizeAnonymousFields(head)
 | |
| 	ret := (*Opcode)(unsafe.Pointer(head))
 | |
| 	compiled.Code = ret
 | |
| 
 | |
| 	delete(ctx.structTypeToCompiledCode, typeptr)
 | |
| 
 | |
| 	if !disableIndirectConversion && (head.Flags&IndirectFlags == 0) && isPtr {
 | |
| 		headCode := head
 | |
| 		for strings.Contains(headCode.Op.String(), "Head") {
 | |
| 			headCode.Flags |= IndirectFlags
 | |
| 			headCode = headCode.Next
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| 
 | |
| func implementsMarshalJSONType(typ *runtime.Type) bool {
 | |
| 	return typ.Implements(marshalJSONType) || typ.Implements(marshalJSONContextType)
 | |
| }
 | |
| 
 | |
| func isPtrMarshalJSONType(typ *runtime.Type) bool {
 | |
| 	return !implementsMarshalJSONType(typ) && implementsMarshalJSONType(runtime.PtrTo(typ))
 | |
| }
 | |
| 
 | |
| func isPtrMarshalTextType(typ *runtime.Type) bool {
 | |
| 	return !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType)
 | |
| }
 |