github.com/3JoB/go-json@v0.10.4/internal/encoder/opcode.go (about)

     1  package encoder
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  	"unsafe"
     9  
    10  	"github.com/3JoB/go-json/internal/runtime"
    11  )
    12  
    13  const uintptrSize = 4 << (^uintptr(0) >> 63)
    14  
    15  type OpFlags uint16
    16  
    17  const (
    18  	AnonymousHeadFlags     OpFlags = 1 << 0
    19  	AnonymousKeyFlags      OpFlags = 1 << 1
    20  	IndirectFlags          OpFlags = 1 << 2
    21  	IsTaggedKeyFlags       OpFlags = 1 << 3
    22  	NilCheckFlags          OpFlags = 1 << 4
    23  	AddrForMarshalerFlags  OpFlags = 1 << 5
    24  	IsNextOpPtrTypeFlags   OpFlags = 1 << 6
    25  	IsNilableTypeFlags     OpFlags = 1 << 7
    26  	MarshalerContextFlags  OpFlags = 1 << 8
    27  	NonEmptyInterfaceFlags OpFlags = 1 << 9
    28  )
    29  
    30  type Opcode struct {
    31  	Op         OpType  // operation type
    32  	Idx        uint32  // offset to access ptr
    33  	Next       *Opcode // next opcode
    34  	End        *Opcode // array/slice/struct/map end
    35  	NextField  *Opcode // next struct field
    36  	Key        string  // struct field key
    37  	Offset     uint32  // offset size from struct header
    38  	PtrNum     uint8   // pointer number: e.g. double pointer is 2.
    39  	NumBitSize uint8
    40  	Flags      OpFlags
    41  
    42  	Type       *runtime.Type // go type
    43  	Jmp        *CompiledCode // for recursive call
    44  	FieldQuery *FieldQuery   // field query for Interface / MarshalJSON / MarshalText
    45  	ElemIdx    uint32        // offset to access array/slice elem
    46  	Length     uint32        // offset to access slice length or array length
    47  	Indent     uint32        // indent number
    48  	Size       uint32        // array/slice elem size
    49  	DisplayIdx uint32        // opcode index
    50  	DisplayKey string        // key text to display
    51  }
    52  
    53  func (c *Opcode) Validate() error {
    54  	var prevIdx uint32
    55  	for code := c; !code.IsEnd(); {
    56  		if prevIdx != 0 {
    57  			if code.DisplayIdx != prevIdx+1 {
    58  				return fmt.Errorf(
    59  					"invalid index. previous display index is %d but next is %d. dump = %s",
    60  					prevIdx, code.DisplayIdx, c.Dump(),
    61  				)
    62  			}
    63  		}
    64  		prevIdx = code.DisplayIdx
    65  		code = code.IterNext()
    66  	}
    67  	return nil
    68  }
    69  
    70  func (c *Opcode) IterNext() *Opcode {
    71  	if c == nil {
    72  		return nil
    73  	}
    74  	switch c.Op.CodeType() {
    75  	case CodeArrayElem, CodeSliceElem, CodeMapKey:
    76  		return c.End
    77  	default:
    78  		return c.Next
    79  	}
    80  }
    81  
    82  func (c *Opcode) IsEnd() bool {
    83  	if c == nil {
    84  		return true
    85  	}
    86  	return c.Op == OpEnd || c.Op == OpInterfaceEnd || c.Op == OpRecursiveEnd
    87  }
    88  
    89  func (c *Opcode) MaxIdx() uint32 {
    90  	max := uint32(0)
    91  	for _, value := range []uint32{
    92  		c.Idx,
    93  		c.ElemIdx,
    94  		c.Length,
    95  		c.Size,
    96  	} {
    97  		if max < value {
    98  			max = value
    99  		}
   100  	}
   101  	return max
   102  }
   103  
   104  func (c *Opcode) ToHeaderType(isString bool) OpType {
   105  	switch c.Op {
   106  	case OpInt:
   107  		if isString {
   108  			return OpStructHeadIntString
   109  		}
   110  		return OpStructHeadInt
   111  	case OpIntPtr:
   112  		if isString {
   113  			return OpStructHeadIntPtrString
   114  		}
   115  		return OpStructHeadIntPtr
   116  	case OpUint:
   117  		if isString {
   118  			return OpStructHeadUintString
   119  		}
   120  		return OpStructHeadUint
   121  	case OpUintPtr:
   122  		if isString {
   123  			return OpStructHeadUintPtrString
   124  		}
   125  		return OpStructHeadUintPtr
   126  	case OpFloat32:
   127  		if isString {
   128  			return OpStructHeadFloat32String
   129  		}
   130  		return OpStructHeadFloat32
   131  	case OpFloat32Ptr:
   132  		if isString {
   133  			return OpStructHeadFloat32PtrString
   134  		}
   135  		return OpStructHeadFloat32Ptr
   136  	case OpFloat64:
   137  		if isString {
   138  			return OpStructHeadFloat64String
   139  		}
   140  		return OpStructHeadFloat64
   141  	case OpFloat64Ptr:
   142  		if isString {
   143  			return OpStructHeadFloat64PtrString
   144  		}
   145  		return OpStructHeadFloat64Ptr
   146  	case OpString:
   147  		if isString {
   148  			return OpStructHeadStringString
   149  		}
   150  		return OpStructHeadString
   151  	case OpStringPtr:
   152  		if isString {
   153  			return OpStructHeadStringPtrString
   154  		}
   155  		return OpStructHeadStringPtr
   156  	case OpNumber:
   157  		if isString {
   158  			return OpStructHeadNumberString
   159  		}
   160  		return OpStructHeadNumber
   161  	case OpNumberPtr:
   162  		if isString {
   163  			return OpStructHeadNumberPtrString
   164  		}
   165  		return OpStructHeadNumberPtr
   166  	case OpBool:
   167  		if isString {
   168  			return OpStructHeadBoolString
   169  		}
   170  		return OpStructHeadBool
   171  	case OpBoolPtr:
   172  		if isString {
   173  			return OpStructHeadBoolPtrString
   174  		}
   175  		return OpStructHeadBoolPtr
   176  	case OpBytes:
   177  		return OpStructHeadBytes
   178  	case OpBytesPtr:
   179  		return OpStructHeadBytesPtr
   180  	case OpMap:
   181  		return OpStructHeadMap
   182  	case OpMapPtr:
   183  		c.Op = OpMap
   184  		return OpStructHeadMapPtr
   185  	case OpArray:
   186  		return OpStructHeadArray
   187  	case OpArrayPtr:
   188  		c.Op = OpArray
   189  		return OpStructHeadArrayPtr
   190  	case OpSlice:
   191  		return OpStructHeadSlice
   192  	case OpSlicePtr:
   193  		c.Op = OpSlice
   194  		return OpStructHeadSlicePtr
   195  	case OpMarshalJSON:
   196  		return OpStructHeadMarshalJSON
   197  	case OpMarshalJSONPtr:
   198  		return OpStructHeadMarshalJSONPtr
   199  	case OpMarshalText:
   200  		return OpStructHeadMarshalText
   201  	case OpMarshalTextPtr:
   202  		return OpStructHeadMarshalTextPtr
   203  	}
   204  	return OpStructHead
   205  }
   206  
   207  func (c *Opcode) ToFieldType(isString bool) OpType {
   208  	switch c.Op {
   209  	case OpInt:
   210  		if isString {
   211  			return OpStructFieldIntString
   212  		}
   213  		return OpStructFieldInt
   214  	case OpIntPtr:
   215  		if isString {
   216  			return OpStructFieldIntPtrString
   217  		}
   218  		return OpStructFieldIntPtr
   219  	case OpUint:
   220  		if isString {
   221  			return OpStructFieldUintString
   222  		}
   223  		return OpStructFieldUint
   224  	case OpUintPtr:
   225  		if isString {
   226  			return OpStructFieldUintPtrString
   227  		}
   228  		return OpStructFieldUintPtr
   229  	case OpFloat32:
   230  		if isString {
   231  			return OpStructFieldFloat32String
   232  		}
   233  		return OpStructFieldFloat32
   234  	case OpFloat32Ptr:
   235  		if isString {
   236  			return OpStructFieldFloat32PtrString
   237  		}
   238  		return OpStructFieldFloat32Ptr
   239  	case OpFloat64:
   240  		if isString {
   241  			return OpStructFieldFloat64String
   242  		}
   243  		return OpStructFieldFloat64
   244  	case OpFloat64Ptr:
   245  		if isString {
   246  			return OpStructFieldFloat64PtrString
   247  		}
   248  		return OpStructFieldFloat64Ptr
   249  	case OpString:
   250  		if isString {
   251  			return OpStructFieldStringString
   252  		}
   253  		return OpStructFieldString
   254  	case OpStringPtr:
   255  		if isString {
   256  			return OpStructFieldStringPtrString
   257  		}
   258  		return OpStructFieldStringPtr
   259  	case OpNumber:
   260  		if isString {
   261  			return OpStructFieldNumberString
   262  		}
   263  		return OpStructFieldNumber
   264  	case OpNumberPtr:
   265  		if isString {
   266  			return OpStructFieldNumberPtrString
   267  		}
   268  		return OpStructFieldNumberPtr
   269  	case OpBool:
   270  		if isString {
   271  			return OpStructFieldBoolString
   272  		}
   273  		return OpStructFieldBool
   274  	case OpBoolPtr:
   275  		if isString {
   276  			return OpStructFieldBoolPtrString
   277  		}
   278  		return OpStructFieldBoolPtr
   279  	case OpBytes:
   280  		return OpStructFieldBytes
   281  	case OpBytesPtr:
   282  		return OpStructFieldBytesPtr
   283  	case OpMap:
   284  		return OpStructFieldMap
   285  	case OpMapPtr:
   286  		c.Op = OpMap
   287  		return OpStructFieldMapPtr
   288  	case OpArray:
   289  		return OpStructFieldArray
   290  	case OpArrayPtr:
   291  		c.Op = OpArray
   292  		return OpStructFieldArrayPtr
   293  	case OpSlice:
   294  		return OpStructFieldSlice
   295  	case OpSlicePtr:
   296  		c.Op = OpSlice
   297  		return OpStructFieldSlicePtr
   298  	case OpMarshalJSON:
   299  		return OpStructFieldMarshalJSON
   300  	case OpMarshalJSONPtr:
   301  		return OpStructFieldMarshalJSONPtr
   302  	case OpMarshalText:
   303  		return OpStructFieldMarshalText
   304  	case OpMarshalTextPtr:
   305  		return OpStructFieldMarshalTextPtr
   306  	}
   307  	return OpStructField
   308  }
   309  
   310  func newOpCode(ctx *compileContext, typ *runtime.Type, op OpType) *Opcode {
   311  	return newOpCodeWithNext(ctx, typ, op, newEndOp(ctx, typ))
   312  }
   313  
   314  func opcodeOffset(idx int) uint32 {
   315  	return uint32(idx) * uintptrSize
   316  }
   317  
   318  func getCodeAddrByIdx(head *Opcode, idx uint32) *Opcode {
   319  	addr := uintptr(unsafe.Pointer(head)) + uintptr(idx)*unsafe.Sizeof(Opcode{})
   320  	return *(**Opcode)(unsafe.Pointer(&addr))
   321  }
   322  
   323  func copyOpcode(code *Opcode) *Opcode {
   324  	codeNum := ToEndCode(code).DisplayIdx + 1
   325  	codeSlice := make([]Opcode, codeNum)
   326  	head := (*Opcode)((*runtime.SliceHeader)(unsafe.Pointer(&codeSlice)).Data)
   327  	ptr := head
   328  	c := code
   329  	for {
   330  		*ptr = Opcode{
   331  			Op:         c.Op,
   332  			Key:        c.Key,
   333  			PtrNum:     c.PtrNum,
   334  			NumBitSize: c.NumBitSize,
   335  			Flags:      c.Flags,
   336  			Idx:        c.Idx,
   337  			Offset:     c.Offset,
   338  			Type:       c.Type,
   339  			FieldQuery: c.FieldQuery,
   340  			DisplayIdx: c.DisplayIdx,
   341  			DisplayKey: c.DisplayKey,
   342  			ElemIdx:    c.ElemIdx,
   343  			Length:     c.Length,
   344  			Size:       c.Size,
   345  			Indent:     c.Indent,
   346  			Jmp:        c.Jmp,
   347  		}
   348  		if c.End != nil {
   349  			ptr.End = getCodeAddrByIdx(head, c.End.DisplayIdx)
   350  		}
   351  		if c.NextField != nil {
   352  			ptr.NextField = getCodeAddrByIdx(head, c.NextField.DisplayIdx)
   353  		}
   354  		if c.Next != nil {
   355  			ptr.Next = getCodeAddrByIdx(head, c.Next.DisplayIdx)
   356  		}
   357  		if c.IsEnd() {
   358  			break
   359  		}
   360  		ptr = getCodeAddrByIdx(head, c.DisplayIdx+1)
   361  		c = c.IterNext()
   362  	}
   363  	return head
   364  }
   365  
   366  func setTotalLengthToInterfaceOp(code *Opcode) {
   367  	for c := code; !c.IsEnd(); {
   368  		if c.Op == OpInterface || c.Op == OpInterfacePtr {
   369  			c.Length = uint32(code.TotalLength())
   370  		}
   371  		c = c.IterNext()
   372  	}
   373  }
   374  
   375  func ToEndCode(code *Opcode) *Opcode {
   376  	c := code
   377  	for !c.IsEnd() {
   378  		c = c.IterNext()
   379  	}
   380  	return c
   381  }
   382  
   383  func copyToInterfaceOpcode(code *Opcode) *Opcode {
   384  	copied := copyOpcode(code)
   385  	c := copied
   386  	c = ToEndCode(c)
   387  	c.Idx += uintptrSize
   388  	c.ElemIdx = c.Idx + uintptrSize
   389  	c.Length = c.Idx + 2*uintptrSize
   390  	c.Op = OpInterfaceEnd
   391  	return copied
   392  }
   393  
   394  func newOpCodeWithNext(ctx *compileContext, typ *runtime.Type, op OpType, next *Opcode) *Opcode {
   395  	return &Opcode{
   396  		Op:         op,
   397  		Idx:        opcodeOffset(ctx.ptrIndex),
   398  		Next:       next,
   399  		Type:       typ,
   400  		DisplayIdx: ctx.opcodeIndex,
   401  		Indent:     ctx.indent,
   402  	}
   403  }
   404  
   405  func newEndOp(ctx *compileContext, typ *runtime.Type) *Opcode {
   406  	return newOpCodeWithNext(ctx, typ, OpEnd, nil)
   407  }
   408  
   409  func (c *Opcode) TotalLength() int {
   410  	var idx int
   411  	code := c
   412  	for !code.IsEnd() {
   413  		maxIdx := int(code.MaxIdx() / uintptrSize)
   414  		if idx < maxIdx {
   415  			idx = maxIdx
   416  		}
   417  		if code.Op == OpRecursiveEnd {
   418  			break
   419  		}
   420  		code = code.IterNext()
   421  	}
   422  	maxIdx := int(code.MaxIdx() / uintptrSize)
   423  	if idx < maxIdx {
   424  		idx = maxIdx
   425  	}
   426  	return idx + 1
   427  }
   428  
   429  func (c *Opcode) dumpHead(code *Opcode) string {
   430  	var length uint32
   431  	if code.Op.CodeType() == CodeArrayHead {
   432  		length = code.Length
   433  	} else {
   434  		length = code.Length / uintptrSize
   435  	}
   436  	return fmt.Sprintf(
   437  		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d])`,
   438  		code.DisplayIdx,
   439  		strings.Repeat("-", int(code.Indent)),
   440  		code.Op,
   441  		code.Idx/uintptrSize,
   442  		code.ElemIdx/uintptrSize,
   443  		length,
   444  	)
   445  }
   446  
   447  func (c *Opcode) dumpMapHead(code *Opcode) string {
   448  	return fmt.Sprintf(
   449  		`[%03d]%s%s ([idx:%d])`,
   450  		code.DisplayIdx,
   451  		strings.Repeat("-", int(code.Indent)),
   452  		code.Op,
   453  		code.Idx/uintptrSize,
   454  	)
   455  }
   456  
   457  func (c *Opcode) dumpMapEnd(code *Opcode) string {
   458  	return fmt.Sprintf(
   459  		`[%03d]%s%s ([idx:%d])`,
   460  		code.DisplayIdx,
   461  		strings.Repeat("-", int(code.Indent)),
   462  		code.Op,
   463  		code.Idx/uintptrSize,
   464  	)
   465  }
   466  
   467  func (c *Opcode) dumpElem(code *Opcode) string {
   468  	var length uint32
   469  	if code.Op.CodeType() == CodeArrayElem {
   470  		length = code.Length
   471  	} else {
   472  		length = code.Length / uintptrSize
   473  	}
   474  	return fmt.Sprintf(
   475  		`[%03d]%s%s ([idx:%d][elemIdx:%d][length:%d][size:%d])`,
   476  		code.DisplayIdx,
   477  		strings.Repeat("-", int(code.Indent)),
   478  		code.Op,
   479  		code.Idx/uintptrSize,
   480  		code.ElemIdx/uintptrSize,
   481  		length,
   482  		code.Size,
   483  	)
   484  }
   485  
   486  func (c *Opcode) dumpField(code *Opcode) string {
   487  	return fmt.Sprintf(
   488  		`[%03d]%s%s ([idx:%d][key:%s][offset:%d])`,
   489  		code.DisplayIdx,
   490  		strings.Repeat("-", int(code.Indent)),
   491  		code.Op,
   492  		code.Idx/uintptrSize,
   493  		code.DisplayKey,
   494  		code.Offset,
   495  	)
   496  }
   497  
   498  func (c *Opcode) dumpKey(code *Opcode) string {
   499  	return fmt.Sprintf(
   500  		`[%03d]%s%s ([idx:%d])`,
   501  		code.DisplayIdx,
   502  		strings.Repeat("-", int(code.Indent)),
   503  		code.Op,
   504  		code.Idx/uintptrSize,
   505  	)
   506  }
   507  
   508  func (c *Opcode) dumpValue(code *Opcode) string {
   509  	return fmt.Sprintf(
   510  		`[%03d]%s%s ([idx:%d])`,
   511  		code.DisplayIdx,
   512  		strings.Repeat("-", int(code.Indent)),
   513  		code.Op,
   514  		code.Idx/uintptrSize,
   515  	)
   516  }
   517  
   518  func (c *Opcode) Dump() string {
   519  	codes := []string{}
   520  	for code := c; !code.IsEnd(); {
   521  		switch code.Op.CodeType() {
   522  		case CodeSliceHead:
   523  			codes = append(codes, c.dumpHead(code))
   524  			code = code.Next
   525  		case CodeMapHead:
   526  			codes = append(codes, c.dumpMapHead(code))
   527  			code = code.Next
   528  		case CodeArrayElem, CodeSliceElem:
   529  			codes = append(codes, c.dumpElem(code))
   530  			code = code.End
   531  		case CodeMapKey:
   532  			codes = append(codes, c.dumpKey(code))
   533  			code = code.End
   534  		case CodeMapValue:
   535  			codes = append(codes, c.dumpValue(code))
   536  			code = code.Next
   537  		case CodeMapEnd:
   538  			codes = append(codes, c.dumpMapEnd(code))
   539  			code = code.Next
   540  		case CodeStructField:
   541  			codes = append(codes, c.dumpField(code))
   542  			code = code.Next
   543  		case CodeStructEnd:
   544  			codes = append(codes, c.dumpField(code))
   545  			code = code.Next
   546  		default:
   547  			codes = append(codes, fmt.Sprintf(
   548  				"[%03d]%s%s ([idx:%d])",
   549  				code.DisplayIdx,
   550  				strings.Repeat("-", int(code.Indent)),
   551  				code.Op,
   552  				code.Idx/uintptrSize,
   553  			))
   554  			code = code.Next
   555  		}
   556  	}
   557  	return strings.Join(codes, "\n")
   558  }
   559  
   560  func (c *Opcode) DumpDOT() string {
   561  	type edge struct {
   562  		from, to *Opcode
   563  		label    string
   564  		weight   int
   565  	}
   566  	var edges []edge
   567  
   568  	b := &bytes.Buffer{}
   569  	fmt.Fprintf(b, "digraph \"%p\" {\n", c.Type)
   570  	fmt.Fprintln(b, "mclimit=1.5;\nrankdir=TD;\nordering=out;\nnode[shape=box];")
   571  	for code := c; !code.IsEnd(); {
   572  		label := code.Op.String()
   573  		fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, label)
   574  		if p := code.Next; p != nil {
   575  			edges = append(edges, edge{
   576  				from:   code,
   577  				to:     p,
   578  				label:  "Next",
   579  				weight: 10,
   580  			})
   581  		}
   582  		if p := code.NextField; p != nil {
   583  			edges = append(edges, edge{
   584  				from:   code,
   585  				to:     p,
   586  				label:  "NextField",
   587  				weight: 2,
   588  			})
   589  		}
   590  		if p := code.End; p != nil {
   591  			edges = append(edges, edge{
   592  				from:   code,
   593  				to:     p,
   594  				label:  "End",
   595  				weight: 1,
   596  			})
   597  		}
   598  		if p := code.Jmp; p != nil {
   599  			edges = append(edges, edge{
   600  				from:   code,
   601  				to:     p.Code,
   602  				label:  "Jmp",
   603  				weight: 1,
   604  			})
   605  		}
   606  
   607  		switch code.Op.CodeType() {
   608  		case CodeSliceHead:
   609  			code = code.Next
   610  		case CodeMapHead:
   611  			code = code.Next
   612  		case CodeArrayElem, CodeSliceElem:
   613  			code = code.End
   614  		case CodeMapKey:
   615  			code = code.End
   616  		case CodeMapValue:
   617  			code = code.Next
   618  		case CodeMapEnd:
   619  			code = code.Next
   620  		case CodeStructField:
   621  			code = code.Next
   622  		case CodeStructEnd:
   623  			code = code.Next
   624  		default:
   625  			code = code.Next
   626  		}
   627  		if code.IsEnd() {
   628  			fmt.Fprintf(b, "\"%p\" [label=%q];\n", code, code.Op.String())
   629  		}
   630  	}
   631  	sort.Slice(edges, func(i, j int) bool {
   632  		return edges[i].to.DisplayIdx < edges[j].to.DisplayIdx
   633  	})
   634  	for _, e := range edges {
   635  		fmt.Fprintf(b, "\"%p\" -> \"%p\" [label=%q][weight=%d];\n", e.from, e.to, e.label, e.weight)
   636  	}
   637  	fmt.Fprint(b, "}")
   638  	return b.String()
   639  }
   640  
   641  func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
   642  	idx := opcodeOffset(ctx.ptrIndex)
   643  	ctx.incPtrIndex()
   644  	elemIdx := opcodeOffset(ctx.ptrIndex)
   645  	ctx.incPtrIndex()
   646  	length := opcodeOffset(ctx.ptrIndex)
   647  	return &Opcode{
   648  		Op:         OpSlice,
   649  		Type:       typ,
   650  		Idx:        idx,
   651  		DisplayIdx: ctx.opcodeIndex,
   652  		ElemIdx:    elemIdx,
   653  		Length:     length,
   654  		Indent:     ctx.indent,
   655  	}
   656  }
   657  
   658  func newSliceElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, size uintptr) *Opcode {
   659  	return &Opcode{
   660  		Op:         OpSliceElem,
   661  		Type:       typ,
   662  		Idx:        head.Idx,
   663  		DisplayIdx: ctx.opcodeIndex,
   664  		ElemIdx:    head.ElemIdx,
   665  		Length:     head.Length,
   666  		Indent:     ctx.indent,
   667  		Size:       uint32(size),
   668  	}
   669  }
   670  
   671  func newArrayHeaderCode(ctx *compileContext, typ *runtime.Type, alen int) *Opcode {
   672  	idx := opcodeOffset(ctx.ptrIndex)
   673  	ctx.incPtrIndex()
   674  	elemIdx := opcodeOffset(ctx.ptrIndex)
   675  	return &Opcode{
   676  		Op:         OpArray,
   677  		Type:       typ,
   678  		Idx:        idx,
   679  		DisplayIdx: ctx.opcodeIndex,
   680  		ElemIdx:    elemIdx,
   681  		Indent:     ctx.indent,
   682  		Length:     uint32(alen),
   683  	}
   684  }
   685  
   686  func newArrayElemCode(ctx *compileContext, typ *runtime.Type, head *Opcode, length int, size uintptr) *Opcode {
   687  	return &Opcode{
   688  		Op:         OpArrayElem,
   689  		Type:       typ,
   690  		Idx:        head.Idx,
   691  		DisplayIdx: ctx.opcodeIndex,
   692  		ElemIdx:    head.ElemIdx,
   693  		Length:     uint32(length),
   694  		Indent:     ctx.indent,
   695  		Size:       uint32(size),
   696  	}
   697  }
   698  
   699  func newMapHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
   700  	idx := opcodeOffset(ctx.ptrIndex)
   701  	ctx.incPtrIndex()
   702  	return &Opcode{
   703  		Op:         OpMap,
   704  		Type:       typ,
   705  		Idx:        idx,
   706  		DisplayIdx: ctx.opcodeIndex,
   707  		Indent:     ctx.indent,
   708  	}
   709  }
   710  
   711  func newMapKeyCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   712  	return &Opcode{
   713  		Op:         OpMapKey,
   714  		Type:       typ,
   715  		Idx:        head.Idx,
   716  		DisplayIdx: ctx.opcodeIndex,
   717  		Indent:     ctx.indent,
   718  	}
   719  }
   720  
   721  func newMapValueCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   722  	return &Opcode{
   723  		Op:         OpMapValue,
   724  		Type:       typ,
   725  		Idx:        head.Idx,
   726  		DisplayIdx: ctx.opcodeIndex,
   727  		Indent:     ctx.indent,
   728  	}
   729  }
   730  
   731  func newMapEndCode(ctx *compileContext, typ *runtime.Type, head *Opcode) *Opcode {
   732  	return &Opcode{
   733  		Op:         OpMapEnd,
   734  		Type:       typ,
   735  		Idx:        head.Idx,
   736  		DisplayIdx: ctx.opcodeIndex,
   737  		Indent:     ctx.indent,
   738  		Next:       newEndOp(ctx, typ),
   739  	}
   740  }
   741  
   742  func newRecursiveCode(ctx *compileContext, typ *runtime.Type, jmp *CompiledCode) *Opcode {
   743  	return &Opcode{
   744  		Op:         OpRecursive,
   745  		Type:       typ,
   746  		Idx:        opcodeOffset(ctx.ptrIndex),
   747  		Next:       newEndOp(ctx, typ),
   748  		DisplayIdx: ctx.opcodeIndex,
   749  		Indent:     ctx.indent,
   750  		Jmp:        jmp,
   751  	}
   752  }