Skip to content

Add build tag: expr_debug #521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,14 @@ jobs:
go-version: ${{ matrix.go-version }}
- name: Test
run: go test ./...

debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Go 1.18
uses: actions/setup-go@v4
with:
go-version: 1.18
- name: Test
run: go test -tags=expr_debug -run=TestDebugger -v ./vm
10 changes: 7 additions & 3 deletions debug/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"strings"
"time"

. "github.com/expr-lang/expr/vm"
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"

. "github.com/expr-lang/expr/vm"
)

func StartDebugger(program *Program, env any) {
Expand Down Expand Up @@ -112,14 +113,17 @@ func StartDebugger(program *Program, env any) {
}

stack.Clear()
for i, value := range vm.Stack() {
for i, value := range vm.Stack {
stack.SetCellSimple(i, 0, fmt.Sprintf("% *d: ", 2, i))
stack.SetCellSimple(i, 1, fmt.Sprintf("%#v", value))
}
stack.ScrollToEnd()

scope.Clear()
s := vm.Scope()
var s *Scope
if len(vm.Scopes) > 0 {
s = vm.Scopes[len(vm.Scopes)-1]
}
if s != nil {
type pair struct {
key string
Expand Down
5 changes: 5 additions & 0 deletions vm/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build expr_debug

package vm

const debug = true
5 changes: 5 additions & 0 deletions vm/debug_off.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !expr_debug

package vm

const debug = false
40 changes: 40 additions & 0 deletions vm/debug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//go:build expr_debug

package vm_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/expr-lang/expr/compiler"
"github.com/expr-lang/expr/parser"
"github.com/expr-lang/expr/vm"
)

func TestDebugger(t *testing.T) {
input := `[1, 2]`

node, err := parser.Parse(input)
require.NoError(t, err)

program, err := compiler.Compile(node, nil)
require.NoError(t, err)

debug := vm.Debug()
go func() {
debug.Step()
debug.Step()
debug.Step()
debug.Step()
}()
go func() {
for range debug.Position() {
}
}()

_, err = debug.Run(program, nil)
require.NoError(t, err)
require.Len(t, debug.Stack, 0)
require.Nil(t, debug.Scopes)
}
11 changes: 11 additions & 0 deletions vm/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vm

import (
"reflect"
)

type Function = func(params ...any) (any, error)

var MemoryBudget uint = 1e6

var errorType = reflect.TypeOf((*error)(nil)).Elem()
100 changes: 44 additions & 56 deletions vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ import (
"github.com/expr-lang/expr/vm/runtime"
)

var MemoryBudget uint = 1e6
var errorType = reflect.TypeOf((*error)(nil)).Elem()

type Function = func(params ...any) (any, error)

func Run(program *Program, env any) (any, error) {
if program == nil {
return nil, fmt.Errorf("program is nil")
Expand All @@ -27,15 +22,24 @@ func Run(program *Program, env any) (any, error) {
return vm.Run(program, env)
}

func Debug() *VM {
vm := &VM{
debug: true,
step: make(chan struct{}, 0),
curr: make(chan int, 0),
}
return vm
}

type VM struct {
stack []any
Stack []any
Scopes []*Scope
ip int
scopes []*Scope
memory uint
memoryBudget uint
debug bool
step chan struct{}
curr chan int
memory uint
memoryBudget uint
}

type Scope struct {
Expand All @@ -47,15 +51,6 @@ type Scope struct {
Acc any
}

func Debug() *VM {
vm := &VM{
debug: true,
step: make(chan struct{}, 0),
curr: make(chan int, 0),
}
return vm
}

func (vm *VM) Run(program *Program, env any) (_ any, err error) {
defer func() {
if r := recover(); r != nil {
Expand All @@ -74,22 +69,22 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
}
}()

if vm.stack == nil {
vm.stack = make([]any, 0, 2)
if vm.Stack == nil {
vm.Stack = make([]any, 0, 2)
} else {
vm.stack = vm.stack[0:0]
vm.Stack = vm.Stack[0:0]
}

if vm.scopes != nil {
vm.scopes = vm.scopes[0:0]
if vm.Scopes != nil {
vm.Scopes = vm.Scopes[0:0]
}

vm.memoryBudget = MemoryBudget
vm.memory = 0
vm.ip = 0

for vm.ip < len(program.Bytecode) {
if vm.debug {
if debug && vm.debug {
<-vm.step
}

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

case OpJumpIfEnd:
scope := vm.Scope()
scope := vm.scope()
if scope.Index >= scope.Len {
vm.ip += arg
}
Expand Down Expand Up @@ -399,7 +394,7 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {

case OpValidateArgs:
fn := vm.pop().(Function)
mem, err := fn(vm.stack[len(vm.stack)-arg:]...)
mem, err := fn(vm.Stack[len(vm.Stack)-arg:]...)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -443,49 +438,49 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
vm.push(runtime.Deref(a))

case OpIncrementIndex:
vm.Scope().Index++
vm.scope().Index++

case OpDecrementIndex:
scope := vm.Scope()
scope := vm.scope()
scope.Index--

case OpIncrementCount:
scope := vm.Scope()
scope := vm.scope()
scope.Count++

case OpGetIndex:
vm.push(vm.Scope().Index)
vm.push(vm.scope().Index)

case OpSetIndex:
scope := vm.Scope()
scope := vm.scope()
scope.Index = vm.pop().(int)

case OpGetCount:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Count)

case OpGetLen:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Len)

case OpGetGroupBy:
vm.push(vm.Scope().GroupBy)
vm.push(vm.scope().GroupBy)

case OpGetAcc:
vm.push(vm.Scope().Acc)
vm.push(vm.scope().Acc)

case OpSetAcc:
vm.Scope().Acc = vm.pop()
vm.scope().Acc = vm.pop()

case OpPointer:
scope := vm.Scope()
scope := vm.scope()
vm.push(scope.Array.Index(scope.Index).Interface())

case OpThrow:
panic(vm.pop().(error))

case OpGroupBy:
scope := vm.Scope()
scope := vm.scope()
if scope.GroupBy == nil {
scope.GroupBy = make(map[any][]any)
}
Expand All @@ -496,46 +491,46 @@ func (vm *VM) Run(program *Program, env any) (_ any, err error) {
case OpBegin:
a := vm.pop()
array := reflect.ValueOf(a)
vm.scopes = append(vm.scopes, &Scope{
vm.Scopes = append(vm.Scopes, &Scope{
Array: array,
Len: array.Len(),
})

case OpEnd:
vm.scopes = vm.scopes[:len(vm.scopes)-1]
vm.Scopes = vm.Scopes[:len(vm.Scopes)-1]

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

if vm.debug {
if debug && vm.debug {
vm.curr <- vm.ip
}
}

if vm.debug {
if debug && vm.debug {
close(vm.curr)
close(vm.step)
}

if len(vm.stack) > 0 {
if len(vm.Stack) > 0 {
return vm.pop(), nil
}

return nil, nil
}

func (vm *VM) push(value any) {
vm.stack = append(vm.stack, value)
vm.Stack = append(vm.Stack, value)
}

func (vm *VM) current() any {
return vm.stack[len(vm.stack)-1]
return vm.Stack[len(vm.Stack)-1]
}

func (vm *VM) pop() any {
value := vm.stack[len(vm.stack)-1]
vm.stack = vm.stack[:len(vm.stack)-1]
value := vm.Stack[len(vm.Stack)-1]
vm.Stack = vm.Stack[:len(vm.Stack)-1]
return value
}

Expand All @@ -546,15 +541,8 @@ func (vm *VM) memGrow(size uint) {
}
}

func (vm *VM) Stack() []any {
return vm.stack
}

func (vm *VM) Scope() *Scope {
if len(vm.scopes) > 0 {
return vm.scopes[len(vm.scopes)-1]
}
return nil
func (vm *VM) scope() *Scope {
return vm.Scopes[len(vm.Scopes)-1]
}

func (vm *VM) Step() {
Expand Down
27 changes: 0 additions & 27 deletions vm/vm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,6 @@ func TestRun_NilProgram(t *testing.T) {
require.Error(t, err)
}

func TestRun_Debugger(t *testing.T) {
input := `[1, 2]`

node, err := parser.Parse(input)
require.NoError(t, err)

program, err := compiler.Compile(node, nil)
require.NoError(t, err)

debug := vm.Debug()
go func() {
debug.Step()
debug.Step()
debug.Step()
debug.Step()
}()
go func() {
for range debug.Position() {
}
}()

_, err = debug.Run(program, nil)
require.NoError(t, err)
require.Len(t, debug.Stack(), 0)
require.Nil(t, debug.Scope())
}

func TestRun_ReuseVM(t *testing.T) {
node, err := parser.Parse(`map(1..2, {#})`)
require.NoError(t, err)
Expand Down