Skip to content

Commit c295a8d

Browse files
authored
Disassembler for inspecting compiled Risor code (deepnoodle-ai#202)
1 parent a897b5c commit c295a8d

File tree

29 files changed

+1033
-444
lines changed

29 files changed

+1033
-444
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
name: Run tests
2929
command: |
3030
mkdir -p /tmp/test-reports
31-
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -coverprofile=coverage.out -covermode=atomic ./...
31+
gotestsum --junitfile /tmp/test-reports/unit-tests.xml -- -coverprofile=coverage.out -covermode=atomic ./... ./cmd/risor/...
3232
- codecov/upload
3333
- store_test_results:
3434
path: /tmp/test-reports

.codecov.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ coverage:
55
project:
66
default:
77
target: auto
8-
threshold: 1%
8+
threshold: 3%
99
if_ci_failed: error
1010
only_pulls: true
1111
patch:
1212
default:
1313
target: auto
14-
threshold: 1% # Allowed drop in coverage for the patch
14+
threshold: 3% # Allowed drop in coverage for the patch
1515
if_ci_failed: error
1616
only_pulls: true
1717
ignore:

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,7 @@ result, err := risor.Eval(ctx, "len(ex.Message)", risor.WithGlobal("ex", example
152152

153153
## Dependencies and Build Options
154154

155-
Risor is designed to have zero external dependencies in its core libraries.
156-
Although, strictly speaking, the core does have one external dependency on
157-
`github.com/stretchr/testify` for unit tests only.
158-
155+
Risor is designed to have minimal external dependencies in its core libraries.
159156
You can choose to opt into various add-on modules if they are of value in your
160157
application. The modules are present in this same Git repository, but must be
161158
installed with `go get` as separate dependencies:

cmd/risor/dis.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/risor-io/risor"
9+
"github.com/risor-io/risor/compiler"
10+
"github.com/risor-io/risor/dis"
11+
"github.com/risor-io/risor/parser"
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/viper"
14+
)
15+
16+
const disExample = ` risor dis -c "a := 1 + 2"
17+
18+
risor dis ./path/to/script.risor
19+
20+
risor dis ./path/to/script.risor --func myfunc`
21+
22+
var disCmd = &cobra.Command{
23+
Use: "dis",
24+
Short: "Disassemble Risor code",
25+
Example: disExample,
26+
Args: cobra.MaximumNArgs(1),
27+
Run: func(cmd *cobra.Command, args []string) {
28+
ctx := context.Background()
29+
processGlobalFlags()
30+
opts := getRisorOptions()
31+
code, err := getRisorCode(cmd, args)
32+
if err != nil {
33+
fatal(err)
34+
}
35+
36+
// Parse then compile the input code
37+
ast, err := parser.Parse(ctx, code)
38+
if err != nil {
39+
fatal(err)
40+
}
41+
cfg := risor.NewConfig(opts...)
42+
compiledCode, err := compiler.Compile(ast, cfg.CompilerOpts()...)
43+
if err != nil {
44+
fatal(err)
45+
}
46+
targetCode := compiledCode
47+
48+
// If a function name was provided, disassemble its code only
49+
if funcName := viper.GetString("func"); funcName != "" {
50+
var fn *compiler.Function
51+
for i := 0; i < compiledCode.ConstantsCount(); i++ {
52+
obj, ok := compiledCode.Constant(i).(*compiler.Function)
53+
if !ok {
54+
continue
55+
}
56+
if obj.Name() == funcName {
57+
fn = obj
58+
break
59+
}
60+
}
61+
if fn == nil {
62+
fatal(fmt.Sprintf("function %q not found", funcName))
63+
}
64+
targetCode = fn.Code()
65+
}
66+
67+
// Disassemble and print the instructions
68+
instructions, err := dis.Disassemble(targetCode)
69+
if err != nil {
70+
fmt.Println(err)
71+
os.Exit(1)
72+
}
73+
dis.Print(instructions, os.Stdout)
74+
},
75+
}
76+
77+
func init() {
78+
rootCmd.AddCommand(disCmd)
79+
disCmd.Flags().String("func", "", "Function name")
80+
viper.BindPFlag("func", disCmd.Flags().Lookup("func"))
81+
}

cmd/risor/dis_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestDisassembly(t *testing.T) {
13+
// Capture stdout
14+
old := os.Stdout
15+
r, w, _ := os.Pipe()
16+
os.Stdout = w
17+
defer func() { os.Stdout = old }()
18+
19+
disCmd.Run(disCmd, []string{"fixtures/ex1.risor"})
20+
21+
w.Close()
22+
23+
var buf bytes.Buffer
24+
_, _ = buf.ReadFrom(r)
25+
capturedOutput := buf.String()
26+
expected := `
27+
+--------+------------+----------+------+
28+
| OFFSET | OPCODE | OPERANDS | INFO |
29+
+--------+------------+----------+------+
30+
| 0 | LOAD_CONST | 0 | 3 |
31+
| 2 | LOAD_CONST | 1 | 4 |
32+
| 4 | BINARY_OP | 1 | + |
33+
+--------+------------+----------+------+
34+
`
35+
require.Equal(t, strings.TrimPrefix(expected, "\n"), capturedOutput)
36+
}

cmd/risor/fixtures/ex1.risor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
3 + 4

cmd/risor/go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ require (
2222
atomicgo.dev/keyboard v0.2.9
2323
github.com/aws/aws-sdk-go-v2/config v1.18.39
2424
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5
25-
github.com/fatih/color v1.15.0
25+
github.com/fatih/color v1.16.0
2626
github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f
2727
github.com/mattn/go-isatty v0.0.20
2828
github.com/mitchellh/go-homedir v1.1.0
@@ -41,6 +41,7 @@ require (
4141
github.com/risor-io/risor/os/s3fs v1.1.1
4242
github.com/spf13/cobra v1.7.0
4343
github.com/spf13/viper v1.16.0
44+
github.com/stretchr/testify v1.8.4
4445
)
4546

4647
require (
@@ -139,15 +140,19 @@ require (
139140
github.com/magiconair/properties v1.8.7 // indirect
140141
github.com/mailru/easyjson v0.7.6 // indirect
141142
github.com/mattn/go-colorable v0.1.13 // indirect
143+
github.com/mattn/go-runewidth v0.0.13 // indirect
142144
github.com/microsoft/go-mssqldb v1.6.0 // indirect
143145
github.com/mitchellh/copystructure v1.0.0 // indirect
144146
github.com/mitchellh/mapstructure v1.5.0 // indirect
145147
github.com/mitchellh/reflectwalk v1.0.0 // indirect
146148
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
147149
github.com/modern-go/reflect2 v1.0.2 // indirect
148150
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
151+
github.com/olekukonko/tablewriter v0.0.5 // indirect
149152
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
150153
github.com/pkg/errors v0.9.1 // indirect
154+
github.com/pmezard/go-difflib v1.0.0 // indirect
155+
github.com/rivo/uniseg v0.2.0 // indirect
151156
github.com/russross/blackfriday/v2 v2.1.0 // indirect
152157
github.com/ryanuber/go-glob v1.0.0 // indirect
153158
github.com/shopspring/decimal v1.2.0 // indirect

cmd/risor/go.sum

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,7 @@ github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH
215215
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
216216
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
217217
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
218-
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
219-
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
218+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
220219
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
221220
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
222221
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -404,6 +403,7 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
404403
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
405404
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
406405
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
406+
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
407407
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
408408
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
409409
github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
@@ -427,6 +427,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
427427
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
428428
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
429429
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
430+
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
431+
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
430432
github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
431433
github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc=
432434
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=

cmd/risor/main.go

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,13 @@
11
package main
22

3-
import (
4-
"encoding/json"
5-
"fmt"
6-
"os"
7-
"strings"
8-
9-
"github.com/spf13/cobra"
10-
)
11-
123
var (
134
version = "dev"
145
commit = "unknown"
156
date = "unknown"
167
)
178

189
func main() {
19-
cmdServe := &cobra.Command{
20-
Use: "serve",
21-
Short: "Run the Risor API server",
22-
Long: ``,
23-
Run: func(cmd *cobra.Command, args []string) {
24-
fmt.Println("Server")
25-
},
26-
}
27-
28-
cmdVersion := &cobra.Command{
29-
Use: "version",
30-
Short: "Print the version of Risor",
31-
Long: ``,
32-
Run: func(cmd *cobra.Command, args []string) {
33-
outFmt := cmd.Flag("output").Value.String()
34-
if strings.ToLower(outFmt) == "json" {
35-
info, err := json.MarshalIndent(map[string]interface{}{
36-
"version": version,
37-
"commit": commit,
38-
"date": date,
39-
}, "", " ")
40-
if err != nil {
41-
fmt.Println(err)
42-
os.Exit(1)
43-
}
44-
fmt.Println(string(info))
45-
} else {
46-
fmt.Println(version)
47-
}
48-
},
49-
}
50-
51-
cmdVersion.Flags().StringP("output", "o", "", "Set the output format")
52-
cmdVersion.RegisterFlagCompletionFunc("output",
53-
cobra.FixedCompletions(outputFormatsCompletion, cobra.ShellCompDirectiveNoFileComp))
54-
55-
rootCmd.AddCommand(cmdServe)
56-
rootCmd.AddCommand(cmdVersion)
57-
5810
if err := rootCmd.Execute(); err != nil {
59-
fmt.Println(err)
60-
os.Exit(1)
11+
fatal(err)
6112
}
6213
}

0 commit comments

Comments
 (0)