Skip to content

Commit 2482543

Browse files
authored
Add memory budget
1 parent 07dcc3d commit 2482543

File tree

5 files changed

+54
-4
lines changed

5 files changed

+54
-4
lines changed

cmd/exe/main.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/antonmedv/expr/internal/conf"
1515
"github.com/antonmedv/expr/optimizer"
1616
"github.com/antonmedv/expr/parser"
17+
"github.com/antonmedv/expr/vm"
1718
"github.com/sanity-io/litter"
1819
)
1920

@@ -118,7 +119,20 @@ func printDisassemble() {
118119
}
119120

120121
func runProgram() {
121-
out, err := expr.Eval(input(), nil)
122+
tree, err := parser.Parse(input())
123+
check(err)
124+
125+
_, err = checker.Check(tree, nil)
126+
check(err)
127+
128+
if opt {
129+
optimizer.Optimize(&tree.Node)
130+
}
131+
132+
program, err := compiler.Compile(tree, nil)
133+
check(err)
134+
135+
out, err := vm.Run(program, nil)
122136
check(err)
123137

124138
litter.Dump(out)

optimizer/optimizer.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ func (*constRange) Exit(node *Node) {
237237
if min, ok := n.Left.(*IntegerNode); ok {
238238
if max, ok := n.Right.(*IntegerNode); ok {
239239
size := max.Value - min.Value + 1
240+
// In this case array is too big. Skip generation,
241+
// and wait for memory budget detection on runtime.
242+
if size > 1e6 {
243+
return
244+
}
240245
value := make([]int, size)
241246
for i := range value {
242247
value[i] = min.Value + i

vm/runtime.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,18 @@ func exponent(a, b interface{}) float64 {
218218
return math.Pow(toFloat64(a), toFloat64(b))
219219
}
220220

221-
func makeRange(a, b interface{}) []int {
221+
func makeRange(a, b interface{}, budget int) ([]int, int) {
222222
min := toInt(a)
223223
max := toInt(b)
224224
size := max - min + 1
225+
if size >= budget {
226+
panic("memory budget exceeded")
227+
}
225228
rng := make([]int, size)
226229
for i := range rng {
227230
rng[i] = min + i
228231
}
229-
return rng
232+
return rng, size
230233
}
231234

232235
func toInt(a interface{}) int {

vm/vm.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import (
99
"github.com/antonmedv/expr/file"
1010
)
1111

12+
var (
13+
MemoryBudget int = 1e6
14+
)
15+
1216
func Run(program *Program, env interface{}) (out interface{}, err error) {
1317
if program == nil {
1418
return nil, fmt.Errorf("program is nil")
@@ -40,12 +44,15 @@ type VM struct {
4044
debug bool
4145
step chan struct{}
4246
curr chan int
47+
memory int
48+
limit int
4349
}
4450

4551
func NewVM(debug bool) *VM {
4652
vm := &VM{
4753
stack: make([]interface{}, 0, 2),
4854
debug: debug,
55+
limit: MemoryBudget,
4956
}
5057
if vm.debug {
5158
vm.step = make(chan struct{}, 0)
@@ -64,6 +71,10 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
6471
<-vm.step
6572
}
6673

74+
if vm.memory >= vm.limit {
75+
panic("memory budget exceeded")
76+
}
77+
6778
vm.pp = vm.ip
6879
vm.ip++
6980
op := vm.bytecode[vm.pp]
@@ -198,7 +209,9 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
198209
case OpRange:
199210
b := vm.pop()
200211
a := vm.pop()
201-
vm.push(makeRange(a, b))
212+
c, size := makeRange(a, b, vm.limit)
213+
vm.push(c)
214+
vm.memory += size
202215

203216
case OpMatches:
204217
b := vm.pop()
@@ -294,6 +307,7 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
294307
array[i] = vm.pop()
295308
}
296309
vm.push(array)
310+
vm.memory += size
297311

298312
case OpMap:
299313
size := vm.pop().(int)
@@ -304,6 +318,7 @@ func (vm *VM) Run(program *Program, env interface{}) interface{} {
304318
m[key.(string)] = value
305319
}
306320
vm.push(m)
321+
vm.memory += size
307322

308323
case OpLen:
309324
vm.push(length(vm.current()))

vm/vm_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,16 @@ func TestRun_helpers(t *testing.T) {
9494
}
9595
}
9696
}
97+
98+
func TestRun_memory_budget(t *testing.T) {
99+
input := `map(1..100, {map(1..100, {map(1..100, {0})})})`
100+
101+
tree, err := parser.Parse(input)
102+
require.NoError(t, err)
103+
104+
program, err := compiler.Compile(tree, nil)
105+
require.NoError(t, err)
106+
107+
_, err = vm.Run(program, nil)
108+
require.Error(t, err)
109+
}

0 commit comments

Comments
 (0)