mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 07:22:24 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			752 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			752 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package encoder
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"github.com/goccy/go-json/internal/runtime"
 | |
| )
 | |
| 
 | |
| const uintptrSize = 4 << (^uintptr(0) >> 63)
 | |
| 
 | |
| type OpFlags uint16
 | |
| 
 | |
| const (
 | |
| 	AnonymousHeadFlags     OpFlags = 1 << 0
 | |
| 	AnonymousKeyFlags      OpFlags = 1 << 1
 | |
| 	IndirectFlags          OpFlags = 1 << 2
 | |
| 	IsTaggedKeyFlags       OpFlags = 1 << 3
 | |
| 	NilCheckFlags          OpFlags = 1 << 4
 | |
| 	AddrForMarshalerFlags  OpFlags = 1 << 5
 | |
| 	IsNextOpPtrTypeFlags   OpFlags = 1 << 6
 | |
| 	IsNilableTypeFlags     OpFlags = 1 << 7
 | |
| 	MarshalerContextFlags  OpFlags = 1 << 8
 | |
| 	NonEmptyInterfaceFlags OpFlags = 1 << 9
 | |
| )
 | |
| 
 | |
| type Opcode struct {
 | |
| 	Op         OpType  // operation type
 | |
| 	Idx        uint32  // offset to access ptr
 | |
| 	Next       *Opcode // next opcode
 | |
| 	End        *Opcode // array/slice/struct/map end
 | |
| 	NextField  *Opcode // next struct field
 | |
| 	Key        string  // struct field key
 | |
| 	Offset     uint32  // offset size from struct header
 | |
| 	PtrNum     uint8   // pointer number: e.g. double pointer is 2.
 | |
| 	NumBitSize uint8
 | |
| 	Flags      OpFlags
 | |
| 
 | |
| 	Type       *runtime.Type // go type
 | |
| 	Jmp        *CompiledCode // for recursive call
 | |
| 	FieldQuery *FieldQuery   // field query for Interface / MarshalJSON / MarshalText
 | |
| 	ElemIdx    uint32        // offset to access array/slice elem
 | |
| 	Length     uint32        // offset to access slice length or array length
 | |
| 	Indent     uint32        // indent number
 | |
| 	Size       uint32        // array/slice elem size
 | |
| 	DisplayIdx uint32        // opcode index
 | |
| 	DisplayKey string        // key text to display
 | |
| }
 | |
| 
 | |
| func (c *Opcode) Validate() error {
 | |
| 	var prevIdx uint32
 | |
| 	for code := c; !code.IsEnd(); {
 | |
| 		if prevIdx != 0 {
 | |
| 			if code.DisplayIdx != prevIdx+1 {
 | |
| 				return fmt.Errorf(
 | |
| 					"invalid index. previous display index is %d but next is %d. dump = %s",
 | |
| 					prevIdx, code.DisplayIdx, c.Dump(),
 | |
| 				)
 | |
| 			}
 | |
| 		}
 | |
| 		prevIdx = code.DisplayIdx
 | |
| 		code = code.IterNext()
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *Opcode) IterNext() *Opcode {
 | |
| 	if c == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	switch c.Op.CodeType() {
 | |
| 	case CodeArrayElem, CodeSliceElem, CodeMapKey:
 | |
| 		return c.End
 | |
| 	default:
 | |
| 		return c.Next
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *Opcode) IsEnd() bool {
 | |
| 	if c == nil {
 | |
| 		return true
 | |
| 	}
 | |
| 	return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
 | |
| }
 | |
| 
 | |
| func (c *Opcode) MaxIdx() uint32 {
 | |
| 	max := uint32(0)
 | |
| 	for _, value := range []uint32{
 | |
| 		c.Idx,
 | |
| 		c.ElemIdx,
 | |
| 		c.Length,
 | |
| 		c.Size,
 | |
| 	} {
 | |
| 		if max < value {
 | |
| 			max = value
 | |
| 		}
 | |
| 	}
 | |
| 	return max
 | |
| }
 | |
| 
 | |
| func (c *Opcode) ToHeaderType(isString bool) OpType {
 | |
| 	switch c.Op {
 | |
| 	case OpInt:
 | |
| 		if isString {
 | |
| 			return OpStructHeadIntString
 | |
| 		}
 | |
| 		return OpStructHeadInt
 | |
| 	case OpIntPtr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadIntPtrString
 | |
| 		}
 | |
| 		return OpStructHeadIntPtr
 | |
| 	case OpUint:
 | |
| 		if isString {
 | |
| 			return OpStructHeadUintString
 | |
| 		}
 | |
| 		return OpStructHeadUint
 | |
| 	case OpUintPtr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadUintPtrString
 | |
| 		}
 | |
| 		return OpStructHeadUintPtr
 | |
| 	case OpFloat32:
 | |
| 		if isString {
 | |
| 			return OpStructHeadFloat32String
 | |
| 		}
 | |
| 		return OpStructHeadFloat32
 | |
| 	case OpFloat32Ptr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadFloat32PtrString
 | |
| 		}
 | |
| 		return OpStructHeadFloat32Ptr
 | |
| 	case OpFloat64:
 | |
| 		if isString {
 | |
| 			return OpStructHeadFloat64String
 | |
| 		}
 | |
| 		return OpStructHeadFloat64
 | |
| 	case OpFloat64Ptr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadFloat64PtrString
 | |
| 		}
 | |
| 		return OpStructHeadFloat64Ptr
 | |
| 	case OpString:
 | |
| 		if isString {
 | |
| 			return OpStructHeadStringString
 | |
| 		}
 | |
| 		return OpStructHeadString
 | |
| 	case OpStringPtr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadStringPtrString
 | |
| 		}
 | |
| 		return OpStructHeadStringPtr
 | |
| 	case OpNumber:
 | |
| 		if isString {
 | |
| 			return OpStructHeadNumberString
 | |
| 		}
 | |
| 		return OpStructHeadNumber
 | |
| 	case OpNumberPtr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadNumberPtrString
 | |
| 		}
 | |
| 		return OpStructHeadNumberPtr
 | |
| 	case OpBool:
 | |
| 		if isString {
 | |
| 			return OpStructHeadBoolString
 | |
| 		}
 | |
| 		return OpStructHeadBool
 | |
| 	case OpBoolPtr:
 | |
| 		if isString {
 | |
| 			return OpStructHeadBoolPtrString
 | |
| 		}
 | |
| 		return OpStructHeadBoolPtr
 | |
| 	case OpBytes:
 | |
| 		return OpStructHeadBytes
 | |
| 	case OpBytesPtr:
 | |
| 		return OpStructHeadBytesPtr
 | |
| 	case OpMap:
 | |
| 		return OpStructHeadMap
 | |
| 	case OpMapPtr:
 | |
| 		c.Op = OpMap
 | |
| 		return OpStructHeadMapPtr
 | |
| 	case OpArray:
 | |
| 		return OpStructHeadArray
 | |
| 	case OpArrayPtr:
 | |
| 		c.Op = OpArray
 | |
| 		return OpStructHeadArrayPtr
 | |
| 	case OpSlice:
 | |
| 		return OpStructHeadSlice
 | |
| 	case OpSlicePtr:
 | |
| 		c.Op = OpSlice
 | |
| 		return OpStructHeadSlicePtr
 | |
| 	case OpMarshalJSON:
 | |
| 		return OpStructHeadMarshalJSON
 | |
| 	case OpMarshalJSONPtr:
 | |
| 		return OpStructHeadMarshalJSONPtr
 | |
| 	case OpMarshalText:
 | |
| 		return OpStructHeadMarshalText
 | |
| 	case OpMarshalTextPtr:
 | |
| 		return OpStructHeadMarshalTextPtr
 | |
| 	}
 | |
| 	return OpStructHead
 | |
| }
 | |
| 
 | |
| func (c *Opcode) ToFieldType(isString bool) OpType {
 | |
| 	switch c.Op {
 | |
| 	case OpInt:
 | |
| 		if isString {
 | |
| 			return OpStructFieldIntString
 | |
| 		}
 | |
| 		return OpStructFieldInt
 | |
| 	case OpIntPtr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldIntPtrString
 | |
| 		}
 | |
| 		return OpStructFieldIntPtr
 | |
| 	case OpUint:
 | |
| 		if isString {
 | |
| 			return OpStructFieldUintString
 | |
| 		}
 | |
| 		return OpStructFieldUint
 | |
| 	case OpUintPtr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldUintPtrString
 | |
| 		}
 | |
| 		return OpStructFieldUintPtr
 | |
| 	case OpFloat32:
 | |
| 		if isString {
 | |
| 			return OpStructFieldFloat32String
 | |
| 		}
 | |
| 		return OpStructFieldFloat32
 | |
| 	case OpFloat32Ptr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldFloat32PtrString
 | |
| 		}
 | |
| 		return OpStructFieldFloat32Ptr
 | |
| 	case OpFloat64:
 | |
| 		if isString {
 | |
| 			return OpStructFieldFloat64String
 | |
| 		}
 | |
| 		return OpStructFieldFloat64
 | |
| 	case OpFloat64Ptr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldFloat64PtrString
 | |
| 		}
 | |
| 		return OpStructFieldFloat64Ptr
 | |
| 	case OpString:
 | |
| 		if isString {
 | |
| 			return OpStructFieldStringString
 | |
| 		}
 | |
| 		return OpStructFieldString
 | |
| 	case OpStringPtr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldStringPtrString
 | |
| 		}
 | |
| 		return OpStructFieldStringPtr
 | |
| 	case OpNumber:
 | |
| 		if isString {
 | |
| 			return OpStructFieldNumberString
 | |
| 		}
 | |
| 		return OpStructFieldNumber
 | |
| 	case OpNumberPtr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldNumberPtrString
 | |
| 		}
 | |
| 		return OpStructFieldNumberPtr
 | |
| 	case OpBool:
 | |
| 		if isString {
 | |
| 			return OpStructFieldBoolString
 | |
| 		}
 | |
| 		return OpStructFieldBool
 | |
| 	case OpBoolPtr:
 | |
| 		if isString {
 | |
| 			return OpStructFieldBoolPtrString
 | |
| 		}
 | |
| 		return OpStructFieldBoolPtr
 | |
| 	case OpBytes:
 | |
| 		return OpStructFieldBytes
 | |
| 	case OpBytesPtr:
 | |
| 		return OpStructFieldBytesPtr
 | |
| 	case OpMap:
 | |
| 		return OpStructFieldMap
 | |
| 	case OpMapPtr:
 | |
| 		c.Op = OpMap
 | |
| 		return OpStructFieldMapPtr
 | |
| 	case OpArray:
 | |
| 		return OpStructFieldArray
 | |
| 	case OpArrayPtr:
 | |
| 		c.Op = OpArray
 | |
| 		return OpStructFieldArrayPtr
 | |
| 	case OpSlice:
 | |
| 		return OpStructFieldSlice
 | |
| 	case OpSlicePtr:
 | |
| 		c.Op = OpSlice
 | |
| 		return OpStructFieldSlicePtr
 | |
| 	case OpMarshalJSON:
 | |
| 		return OpStructFieldMarshalJSON
 | |
| 	case OpMarshalJSONPtr:
 | |
| 		return OpStructFieldMarshalJSONPtr
 | |
| 	case OpMarshalText:
 | |
| 		return OpStructFieldMarshalText
 | |
| 	case OpMarshalTextPtr:
 | |
| 		return OpStructFieldMarshalTextPtr
 | |
| 	}
 | |
| 	return OpStructField
 | |
| }
 | |
| 
 | |
| func newOpCode(ctx *compileContext, typ *runtime.Type, op OpType) *Opcode {
 | |
| 	return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
 | |
| }
 | |
| 
 | |
| func opcodeOffset(idx int) uint32 {
 | |
| 	return uint32(idx) * uintptrSize
 | |
| }
 | |
| 
 | |
| func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
 | |
| 	addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
 | |
| 	return *(**Opcode)(unsafe.Pointer(&addr))
 | |
| }
 | |
| 
 | |
| func copyOpcode(code *Opcode) *Opcode {
 | |
| 	codeNum := ToEndCode(code).DisplayIdx + 1
 | |
| 	codeSlice := make([]Opcode, codeNum)
 | |
| 	head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
 | |
| 	ptr := head
 | |
| 	c := code
 | |
| 	for {
 | |
| 		*ptr = Opcode{
 | |
| 			Op:         c.Op,
 | |
| 			Key:        c.Key,
 | |
| 			PtrNum:     c.PtrNum,
 | |
| 			NumBitSize: c.NumBitSize,
 | |
| 			Flags:      c.Flags,
 | |
| 			Idx:        c.Idx,
 | |
| 			Offset:     c.Offset,
 | |
| 			Type:       c.Type,
 | |
| 			FieldQuery: c.FieldQuery,
 | |
| 			DisplayIdx: c.DisplayIdx,
 | |
| 			DisplayKey: c.DisplayKey,
 | |
| 			ElemIdx:    c.ElemIdx,
 | |
| 			Length:     c.Length,
 | |
| 			Size:       c.Size,
 | |
| 			Indent:     c.Indent,
 | |
| 			Jmp:        c.Jmp,
 | |
| 		}
 | |
| 		if c.End != nil {
 | |
| 			ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
 | |
| 		}
 | |
| 		if c.NextField != nil {
 | |
| 			ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
 | |
| 		}
 | |
| 		if c.Next != nil {
 | |
| 			ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
 | |
| 		}
 | |
| 		if c.IsEnd() {
 | |
| 			break
 | |
| 		}
 | |
| 		ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
 | |
| 		c = c.IterNext()
 | |
| 	}
 | |
| 	return head
 | |
| }
 | |
| 
 | |
| func setTotalLengthToInterfaceOp(code *Opcode) {
 | |
| 	for c := code; !c.IsEnd(); {
 | |
| 		if c.Op == OpInterface || c.Op == OpInterfacePtr {
 | |
| 			c.Length = uint32(code.TotalLength())
 | |
| 		}
 | |
| 		c = c.IterNext()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func ToEndCode(code *Opcode) *Opcode {
 | |
| 	c := code
 | |
| 	for !c.IsEnd() {
 | |
| 		c = c.IterNext()
 | |
| 	}
 | |
| 	return c
 | |
| }
 | |
| 
 | |
| func copyToInterfaceOpcode(code *Opcode) *Opcode {
 | |
| 	copied := copyOpcode(code)
 | |
| 	c := copied
 | |
| 	c = ToEndCode(c)
 | |
| 	c.Idx += uintptrSize
 | |
| 	c.ElemIdx = c.Idx + uintptrSize
 | |
| 	c.Length = c.Idx + 2*uintptrSize
 | |
| 	c.Op = OpInterfaceEnd
 | |
| 	return copied
 | |
| }
 | |
| 
 | |
| func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         op,
 | |
| 		Idx:        opcodeOffset(ctx.ptrIndex),
 | |
| 		Next:       next,
 | |
| 		Type:       typ,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
 | |
| 	return newOpCodeWithNext(ctx, typ, OpEnd, nil)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) TotalLength() int {
 | |
| 	var idx int
 | |
| 	code := c
 | |
| 	for !code.IsEnd() {
 | |
| 		maxIdx := int(code.MaxIdx() / uintptrSize)
 | |
| 		if idx < maxIdx {
 | |
| 			idx = maxIdx
 | |
| 		}
 | |
| 		if code.Op == OpRecursiveEnd {
 | |
| 			break
 | |
| 		}
 | |
| 		code = code.IterNext()
 | |
| 	}
 | |
| 	maxIdx := int(code.MaxIdx() / uintptrSize)
 | |
| 	if idx < maxIdx {
 | |
| 		idx = maxIdx
 | |
| 	}
 | |
| 	return idx + 1
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpHead(code *Opcode) string {
 | |
| 	var length uint32
 | |
| 	if code.Op.CodeType() == CodeArrayHead {
 | |
| 		length = code.Length
 | |
| 	} else {
 | |
| 		length = code.Length / uintptrSize
 | |
| 	}
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 		code.ElemIdx/uintptrSize,
 | |
| 		length,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpMapHead(code *Opcode) string {
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpMapEnd(code *Opcode) string {
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpElem(code *Opcode) string {
 | |
| 	var length uint32
 | |
| 	if code.Op.CodeType() == CodeArrayElem {
 | |
| 		length = code.Length
 | |
| 	} else {
 | |
| 		length = code.Length / uintptrSize
 | |
| 	}
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 		code.ElemIdx/uintptrSize,
 | |
| 		length,
 | |
| 		code.Size,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpField(code *Opcode) string {
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 		code.DisplayKey,
 | |
| 		code.Offset,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpKey(code *Opcode) string {
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) dumpValue(code *Opcode) string {
 | |
| 	return fmt.Sprintf(
 | |
| 		`[%03d]%s%s ([idx:%d])`,
 | |
| 		code.DisplayIdx,
 | |
| 		strings.Repeat("-", int(code.Indent)),
 | |
| 		code.Op,
 | |
| 		code.Idx/uintptrSize,
 | |
| 	)
 | |
| }
 | |
| 
 | |
| func (c *Opcode) Dump() string {
 | |
| 	codes := []string{}
 | |
| 	for code := c; !code.IsEnd(); {
 | |
| 		switch code.Op.CodeType() {
 | |
| 		case CodeSliceHead:
 | |
| 			codes = append(codes, c.dumpHead(code))
 | |
| 			code = code.Next
 | |
| 		case CodeMapHead:
 | |
| 			codes = append(codes, c.dumpMapHead(code))
 | |
| 			code = code.Next
 | |
| 		case CodeArrayElem, CodeSliceElem:
 | |
| 			codes = append(codes, c.dumpElem(code))
 | |
| 			code = code.End
 | |
| 		case CodeMapKey:
 | |
| 			codes = append(codes, c.dumpKey(code))
 | |
| 			code = code.End
 | |
| 		case CodeMapValue:
 | |
| 			codes = append(codes, c.dumpValue(code))
 | |
| 			code = code.Next
 | |
| 		case CodeMapEnd:
 | |
| 			codes = append(codes, c.dumpMapEnd(code))
 | |
| 			code = code.Next
 | |
| 		case CodeStructField:
 | |
| 			codes = append(codes, c.dumpField(code))
 | |
| 			code = code.Next
 | |
| 		case CodeStructEnd:
 | |
| 			codes = append(codes, c.dumpField(code))
 | |
| 			code = code.Next
 | |
| 		default:
 | |
| 			codes = append(codes, fmt.Sprintf(
 | |
| 				"[%03d]%s%s ([idx:%d])",
 | |
| 				code.DisplayIdx,
 | |
| 				strings.Repeat("-", int(code.Indent)),
 | |
| 				code.Op,
 | |
| 				code.Idx/uintptrSize,
 | |
| 			))
 | |
| 			code = code.Next
 | |
| 		}
 | |
| 	}
 | |
| 	return strings.Join(codes, "\n")
 | |
| }
 | |
| 
 | |
| func (c *Opcode) DumpDOT() string {
 | |
| 	type edge struct {
 | |
| 		from, to *Opcode
 | |
| 		label    string
 | |
| 		weight   int
 | |
| 	}
 | |
| 	var edges []edge
 | |
| 
 | |
| 	b := &bytes.Buffer{}
 | |
| 	fmt.Fprintf(b, "digraph \"%p\" {\n", c.Type)
 | |
| 	fmt.Fprintln(b, "mclimit=1.5;\nrankdir=TD;\nordering=out;\nnode[shape=box];")
 | |
| 	for code := c; !code.IsEnd(); {
 | |
| 		label := code.Op.String()
 | |
| 		fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, label)
 | |
| 		if p := code.Next; p != nil {
 | |
| 			edges = append(edges, edge{
 | |
| 				from:   code,
 | |
| 				to:     p,
 | |
| 				label:  "Next",
 | |
| 				weight: 10,
 | |
| 			})
 | |
| 		}
 | |
| 		if p := code.NextField; p != nil {
 | |
| 			edges = append(edges, edge{
 | |
| 				from:   code,
 | |
| 				to:     p,
 | |
| 				label:  "NextField",
 | |
| 				weight: 2,
 | |
| 			})
 | |
| 		}
 | |
| 		if p := code.End; p != nil {
 | |
| 			edges = append(edges, edge{
 | |
| 				from:   code,
 | |
| 				to:     p,
 | |
| 				label:  "End",
 | |
| 				weight: 1,
 | |
| 			})
 | |
| 		}
 | |
| 		if p := code.Jmp; p != nil {
 | |
| 			edges = append(edges, edge{
 | |
| 				from:   code,
 | |
| 				to:     p.Code,
 | |
| 				label:  "Jmp",
 | |
| 				weight: 1,
 | |
| 			})
 | |
| 		}
 | |
| 
 | |
| 		switch code.Op.CodeType() {
 | |
| 		case CodeSliceHead:
 | |
| 			code = code.Next
 | |
| 		case CodeMapHead:
 | |
| 			code = code.Next
 | |
| 		case CodeArrayElem, CodeSliceElem:
 | |
| 			code = code.End
 | |
| 		case CodeMapKey:
 | |
| 			code = code.End
 | |
| 		case CodeMapValue:
 | |
| 			code = code.Next
 | |
| 		case CodeMapEnd:
 | |
| 			code = code.Next
 | |
| 		case CodeStructField:
 | |
| 			code = code.Next
 | |
| 		case CodeStructEnd:
 | |
| 			code = code.Next
 | |
| 		default:
 | |
| 			code = code.Next
 | |
| 		}
 | |
| 		if code.IsEnd() {
 | |
| 			fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, code.Op.String())
 | |
| 		}
 | |
| 	}
 | |
| 	sort.Slice(edges, func(i, j int) bool {
 | |
| 		return edges[i].to.DisplayIdx < edges[j].to.DisplayIdx
 | |
| 	})
 | |
| 	for _, e := range edges {
 | |
| 		fmt.Fprintf(b, "\"%p\" -> \"%p\" [label=%q][weight=%d];\n", e.from, e.to, e.label, e.weight)
 | |
| 	}
 | |
| 	fmt.Fprint(b, "}")
 | |
| 	return b.String()
 | |
| }
 | |
| 
 | |
| func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
 | |
| 	idx := opcodeOffset(ctx.ptrIndex)
 | |
| 	ctx.incPtrIndex()
 | |
| 	elemIdx := opcodeOffset(ctx.ptrIndex)
 | |
| 	ctx.incPtrIndex()
 | |
| 	length := opcodeOffset(ctx.ptrIndex)
 | |
| 	return &Opcode{
 | |
| 		Op:         OpSlice,
 | |
| 		Type:       typ,
 | |
| 		Idx:        idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		ElemIdx:    elemIdx,
 | |
| 		Length:     length,
 | |
| 		Indent:     ctx.indent,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpSliceElem,
 | |
| 		Type:       typ,
 | |
| 		Idx:        head.Idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		ElemIdx:    head.ElemIdx,
 | |
| 		Length:     head.Length,
 | |
| 		Indent:     ctx.indent,
 | |
| 		Size:       uint32(size),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
 | |
| 	idx := opcodeOffset(ctx.ptrIndex)
 | |
| 	ctx.incPtrIndex()
 | |
| 	elemIdx := opcodeOffset(ctx.ptrIndex)
 | |
| 	return &Opcode{
 | |
| 		Op:         OpArray,
 | |
| 		Type:       typ,
 | |
| 		Idx:        idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		ElemIdx:    elemIdx,
 | |
| 		Indent:     ctx.indent,
 | |
| 		Length:     uint32(alen),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpArrayElem,
 | |
| 		Type:       typ,
 | |
| 		Idx:        head.Idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		ElemIdx:    head.ElemIdx,
 | |
| 		Length:     uint32(length),
 | |
| 		Indent:     ctx.indent,
 | |
| 		Size:       uint32(size),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
 | |
| 	idx := opcodeOffset(ctx.ptrIndex)
 | |
| 	ctx.incPtrIndex()
 | |
| 	return &Opcode{
 | |
| 		Op:         OpMap,
 | |
| 		Type:       typ,
 | |
| 		Idx:        idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpMapKey,
 | |
| 		Type:       typ,
 | |
| 		Idx:        head.Idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpMapValue,
 | |
| 		Type:       typ,
 | |
| 		Idx:        head.Idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpMapEnd,
 | |
| 		Type:       typ,
 | |
| 		Idx:        head.Idx,
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 		Next:       newEndOp(ctx, typ),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
 | |
| 	return &Opcode{
 | |
| 		Op:         OpRecursive,
 | |
| 		Type:       typ,
 | |
| 		Idx:        opcodeOffset(ctx.ptrIndex),
 | |
| 		Next:       newEndOp(ctx, typ),
 | |
| 		DisplayIdx: ctx.opcodeIndex,
 | |
| 		Indent:     ctx.indent,
 | |
| 		Jmp:        jmp,
 | |
| 	}
 | |
| }
 |