Skip to content

Commit db94b96

Browse files
authored
Add build tag: expr_debug (#521)
1 parent 1b3c10c commit db94b96

File tree

8 files changed

+123
-86
lines changed

8 files changed

+123
-86
lines changed

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,14 @@ jobs:
2020
go-version: ${{ matrix.go-version }}
2121
- name: Test
2222
run: go test ./...
23+
24+
debug:
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v3
28+
- name: Setup Go 1.18
29+
uses: actions/setup-go@v4
30+
with:
31+
go-version: 1.18
32+
- name: Test
33+
run: go test -tags=expr_debug -run=TestDebugger -v ./vm

debug/debugger.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import (
77
"strings"
88
"time"
99

10-
. "github.com/expr-lang/expr/vm"
1110
"github.com/gdamore/tcell/v2"
1211
"github.com/rivo/tview"
12+
13+
. "github.com/expr-lang/expr/vm"
1314
)
1415

1516
func StartDebugger(program *Program, env any) {
@@ -112,14 +113,17 @@ func StartDebugger(program *Program, env any) {
112113
}
113114

114115
stack.Clear()
115-
for i, value := range vm.Stack() {
116+
for i, value := range vm.Stack {
116117
stack.SetCellSimple(i, 0, fmt.Sprintf("% *d: ", 2, i))
117118
stack.SetCellSimple(i, 1, fmt.Sprintf("%#v", value))
118119
}
119120
stack.ScrollToEnd()
120121

121122
scope.Clear()
122-
s := vm.Scope()
123+
var s *Scope
124+
if len(vm.Scopes) > 0 {
125+
s = vm.Scopes[len(vm.Scopes)-1]
126+
}
123127
if s != nil {
124128
type pair struct {
125129
key string

vm/debug.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build expr_debug
2+
3+
package vm
4+
5+
const debug = true

vm/debug_off.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//go:build !expr_debug
2+
3+
package vm
4+
5+
const debug = false

vm/debug_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//go:build expr_debug
2+
3+
package vm_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/expr-lang/expr/compiler"
11+
"github.com/expr-lang/expr/parser"
12+
"github.com/expr-lang/expr/vm"
13+
)
14+
15+
func TestDebugger(t *testing.T) {
16+
input := `[1, 2]`
17+
18+
node, err := parser.Parse(input)
19+
require.NoError(t, err)
20+
21+
program, err := compiler.Compile(node, nil)
22+
require.NoError(t, err)
23+
24+
debug := vm.Debug()
25+
go func() {
26+
debug.Step()
27+
debug.Step()
28+
debug.Step()
29+
debug.Step()
30+
}()
31+
go func() {
32+
for range debug.Position() {
33+
}
34+
}()
35+
36+
_, err = debug.Run(program, nil)
37+
require.NoError(t, err)
38+
require.Len(t, debug.Stack, 0)
39+
require.Nil(t, debug.Scopes)
40+
}

vm/utils.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package vm
2+
3+
import (
4+
"reflect"
5+
)
6+
7+
type Function = func(params ...any) (any, error)
8+
9+
var MemoryBudget uint = 1e6
10+
11+
var errorType = reflect.TypeOf((*error)(nil)).Elem()

vm/vm.go

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ import (
1313
"github.com/expr-lang/expr/vm/runtime"
1414
)
1515

16-
var MemoryBudget uint = 1e6
17-
var errorType = reflect.TypeOf((*error)(nil)).Elem()
18-
19-
type Function = func(params ...any) (any, error)
20-
2116
func Run(program *Program, env any) (any, error) {
2217
if program == nil {
2318
return nil, fmt.Errorf("program is nil")
@@ -27,15 +22,24 @@ func Run(program *Program, env any) (any, error) {
2722
return vm.Run(program, env)
2823
}
2924

25+
func Debug() *VM {
26+
vm := &VM{
27+
debug: true,
28+
step: make(chan struct{}, 0),
29+
curr: make(chan int, 0),
30+
}
31+
return vm
32+
}
33+
3034
type VM struct {
31-
stack []any
35+
Stack []any
36+
Scopes []*Scope
3237
ip int
33-
scopes []*Scope
38+
memory uint
39+
memoryBudget uint
3440
debug bool
3541
step chan struct{}
3642
curr chan int
37-
memory uint
38-
memoryBudget uint
3943
}
4044

4145
type Scope struct {
@@ -47,15 +51,6 @@ type Scope struct {
4751
Acc any
4852
}
4953

50-
func Debug() *VM {
51-
vm := &VM{
52-
debug: true,
53-
step: make(chan struct{}, 0),
54-
curr: make(chan int, 0),
55-
}
56-
return vm
57-
}
58-
5954
func (vm *VM) Run(program *Program, env any) (_ any, err error) {
6055
defer func() {
6156
if r := recover(); r != nil {
@@ -74,22 +69,22 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
7469
}
7570
}()
7671

77-
if vm.stack == nil {
78-
vm.stack = make([]any, 0, 2)
72+
if vm.Stack == nil {
73+
vm.Stack = make([]any, 0, 2)
7974
} else {
80-
vm.stack = vm.stack[0:0]
75+
vm.Stack = vm.Stack[0:0]
8176
}
8277

83-
if vm.scopes != nil {
84-
vm.scopes = vm.scopes[0:0]
78+
if vm.Scopes != nil {
79+
vm.Scopes = vm.Scopes[0:0]
8580
}
8681

8782
vm.memoryBudget = MemoryBudget
8883
vm.memory = 0
8984
vm.ip = 0
9085

9186
for vm.ip < len(program.Bytecode) {
92-
if vm.debug {
87+
if debug && vm.debug {
9388
<-vm.step
9489
}
9590

@@ -204,7 +199,7 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
204199
}
205200

206201
case OpJumpIfEnd:
207-
scope := vm.Scope()
202+
scope := vm.scope()
208203
if scope.Index >= scope.Len {
209204
vm.ip += arg
210205
}
@@ -399,7 +394,7 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
399394

400395
case OpValidateArgs:
401396
fn := vm.pop().(Function)
402-
mem, err := fn(vm.stack[len(vm.stack)-arg:]...)
397+
mem, err := fn(vm.Stack[len(vm.Stack)-arg:]...)
403398
if err != nil {
404399
panic(err)
405400
}
@@ -443,49 +438,49 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
443438
vm.push(runtime.Deref(a))
444439

445440
case OpIncrementIndex:
446-
vm.Scope().Index++
441+
vm.scope().Index++
447442

448443
case OpDecrementIndex:
449-
scope := vm.Scope()
444+
scope := vm.scope()
450445
scope.Index--
451446

452447
case OpIncrementCount:
453-
scope := vm.Scope()
448+
scope := vm.scope()
454449
scope.Count++
455450

456451
case OpGetIndex:
457-
vm.push(vm.Scope().Index)
452+
vm.push(vm.scope().Index)
458453

459454
case OpSetIndex:
460-
scope := vm.Scope()
455+
scope := vm.scope()
461456
scope.Index = vm.pop().(int)
462457

463458
case OpGetCount:
464-
scope := vm.Scope()
459+
scope := vm.scope()
465460
vm.push(scope.Count)
466461

467462
case OpGetLen:
468-
scope := vm.Scope()
463+
scope := vm.scope()
469464
vm.push(scope.Len)
470465

471466
case OpGetGroupBy:
472-
vm.push(vm.Scope().GroupBy)
467+
vm.push(vm.scope().GroupBy)
473468

474469
case OpGetAcc:
475-
vm.push(vm.Scope().Acc)
470+
vm.push(vm.scope().Acc)
476471

477472
case OpSetAcc:
478-
vm.Scope().Acc = vm.pop()
473+
vm.scope().Acc = vm.pop()
479474

480475
case OpPointer:
481-
scope := vm.Scope()
476+
scope := vm.scope()
482477
vm.push(scope.Array.Index(scope.Index).Interface())
483478

484479
case OpThrow:
485480
panic(vm.pop().(error))
486481

487482
case OpGroupBy:
488-
scope := vm.Scope()
483+
scope := vm.scope()
489484
if scope.GroupBy == nil {
490485
scope.GroupBy = make(map[any][]any)
491486
}
@@ -496,46 +491,46 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
496491
case OpBegin:
497492
a := vm.pop()
498493
array := reflect.ValueOf(a)
499-
vm.scopes = append(vm.scopes, &Scope{
494+
vm.Scopes = append(vm.Scopes, &Scope{
500495
Array: array,
501496
Len: array.Len(),
502497
})
503498

504499
case OpEnd:
505-
vm.scopes = vm.scopes[:len(vm.scopes)-1]
500+
vm.Scopes = vm.Scopes[:len(vm.Scopes)-1]
506501

507502
default:
508503
panic(fmt.Sprintf("unknown bytecode %#x", op))
509504
}
510505

511-
if vm.debug {
506+
if debug && vm.debug {
512507
vm.curr <- vm.ip
513508
}
514509
}
515510

516-
if vm.debug {
511+
if debug && vm.debug {
517512
close(vm.curr)
518513
close(vm.step)
519514
}
520515

521-
if len(vm.stack) > 0 {
516+
if len(vm.Stack) > 0 {
522517
return vm.pop(), nil
523518
}
524519

525520
return nil, nil
526521
}
527522

528523
func (vm *VM) push(value any) {
529-
vm.stack = append(vm.stack, value)
524+
vm.Stack = append(vm.Stack, value)
530525
}
531526

532527
func (vm *VM) current() any {
533-
return vm.stack[len(vm.stack)-1]
528+
return vm.Stack[len(vm.Stack)-1]
534529
}
535530

536531
func (vm *VM) pop() any {
537-
value := vm.stack[len(vm.stack)-1]
538-
vm.stack = vm.stack[:len(vm.stack)-1]
532+
value := vm.Stack[len(vm.Stack)-1]
533+
vm.Stack = vm.Stack[:len(vm.Stack)-1]
539534
return value
540535
}
541536

@@ -546,15 +541,8 @@ func (vm *VM) memGrow(size uint) {
546541
}
547542
}
548543

549-
func (vm *VM) Stack() []any {
550-
return vm.stack
551-
}
552-
553-
func (vm *VM) Scope() *Scope {
554-
if len(vm.scopes) > 0 {
555-
return vm.scopes[len(vm.scopes)-1]
556-
}
557-
return nil
544+
func (vm *VM) scope() *Scope {
545+
return vm.Scopes[len(vm.Scopes)-1]
558546
}
559547

560548
func (vm *VM) Step() {

vm/vm_test.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -20,33 +20,6 @@ func TestRun_NilProgram(t *testing.T) {
2020
require.Error(t, err)
2121
}
2222

23-
func TestRun_Debugger(t *testing.T) {
24-
input := `[1, 2]`
25-
26-
node, err := parser.Parse(input)
27-
require.NoError(t, err)
28-
29-
program, err := compiler.Compile(node, nil)
30-
require.NoError(t, err)
31-
32-
debug := vm.Debug()
33-
go func() {
34-
debug.Step()
35-
debug.Step()
36-
debug.Step()
37-
debug.Step()
38-
}()
39-
go func() {
40-
for range debug.Position() {
41-
}
42-
}()
43-
44-
_, err = debug.Run(program, nil)
45-
require.NoError(t, err)
46-
require.Len(t, debug.Stack(), 0)
47-
require.Nil(t, debug.Scope())
48-
}
49-
5023
func TestRun_ReuseVM(t *testing.T) {
5124
node, err := parser.Parse(`map(1..2, {#})`)
5225
require.NoError(t, err)

0 commit comments

Comments
 (0)