Skip to content

Commit e65ac79

Browse files
committed
feat: added the option to output opcodes as dot
1 parent 6f969b6 commit e65ac79

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

internal/encoder/opcode.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package encoder
22

33
import (
4+
"bytes"
45
"fmt"
6+
"sort"
57
"strings"
68
"unsafe"
79

@@ -555,6 +557,87 @@ func (c *Opcode) Dump() string {
555557
return strings.Join(codes, "\n")
556558
}
557559

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+
558641
func newSliceHeaderCode(ctx *compileContext, typ *runtime.Type) *Opcode {
559642
idx := opcodeOffset(ctx.ptrIndex)
560643
ctx.incPtrIndex()

internal/encoder/option.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type Option struct {
2323
ColorScheme *ColorScheme
2424
Context context.Context
2525
DebugOut io.Writer
26+
DebugDOTOut io.WriteCloser
2627
}
2728

2829
type EncodeFormat struct {

internal/encoder/vm/debug_vm.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package vm
22

33
import (
44
"fmt"
5+
"io"
56

67
"github.com/goccy/go-json/internal/encoder"
78
)
@@ -14,6 +15,11 @@ func DebugRun(ctx *encoder.RuntimeContext, b []byte, codeSet *encoder.OpcodeSet)
1415
} else {
1516
code = codeSet.NoescapeKeyCode
1617
}
18+
if wc := ctx.Option.DebugDOTOut; wc != nil {
19+
_, _ = io.WriteString(wc, code.DumpDOT())
20+
wc.Close()
21+
ctx.Option.DebugDOTOut = nil
22+
}
1723

1824
if err := recover(); err != nil {
1925
w := ctx.Option.DebugOut

option.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ func DebugWith(w io.Writer) EncodeOptionFunc {
4848
}
4949
}
5050

51+
// DebugDOT sets the destination to write opcodes graph.
52+
func DebugDOT(w io.WriteCloser) EncodeOptionFunc {
53+
return func(opt *EncodeOption) {
54+
opt.DebugDOTOut = w
55+
}
56+
}
57+
5158
// Colorize add an identifier for coloring to the string of the encoded result.
5259
func Colorize(scheme *ColorScheme) EncodeOptionFunc {
5360
return func(opt *EncodeOption) {

0 commit comments

Comments
 (0)