Simplify vm and opcodes

This commit is contained in:
Anton Medvedev
2022-10-24 23:26:22 +02:00
parent d9bd9e021c
commit cfc9843ccb
7 changed files with 188 additions and 225 deletions
+83 -93
View File
@@ -1,7 +1,6 @@
package compiler
import (
"encoding/binary"
"fmt"
"math"
"reflect"
@@ -22,8 +21,8 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
}()
c := &compiler{
index: make(map[interface{}]uint16),
locations: make(map[int]file.Location),
index: make(map[interface{}]Opcode),
locations: make([]file.Location, 0),
}
if config != nil {
@@ -35,9 +34,9 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
switch c.cast {
case reflect.Int64:
c.emit(OpCast, encode(0)...)
c.emit(OpCast, 0)
case reflect.Float64:
c.emit(OpCast, encode(1)...)
c.emit(OpCast, 1)
}
program = &Program{
@@ -50,43 +49,44 @@ func Compile(tree *parser.Tree, config *conf.Config) (program *Program, err erro
}
type compiler struct {
locations map[int]file.Location
locations []file.Location
constants []interface{}
bytecode []byte
index map[interface{}]uint16
bytecode []Opcode
index map[interface{}]Opcode
mapEnv bool
cast reflect.Kind
nodes []ast.Node
chains [][]int
}
func (c *compiler) emit(op byte, b ...byte) int {
func (c *compiler) emitLocation(loc file.Location, op Opcode, arg Opcode) int {
c.bytecode = append(c.bytecode, op)
current := len(c.bytecode)
c.bytecode = append(c.bytecode, b...)
c.bytecode = append(c.bytecode, arg)
c.locations = append(c.locations, loc)
return current
}
func (c *compiler) emit(op Opcode, args ...Opcode) int {
var arg Opcode = 0
if len(args) > 1 {
panic("too many arguments")
}
if len(args) == 1 {
arg = args[0]
}
var loc file.Location
if len(c.nodes) > 0 {
loc = c.nodes[len(c.nodes)-1].Location()
}
c.locations[current-1] = loc
return current
}
func (c *compiler) emitLoc(node ast.Node, op byte, b ...byte) int {
c.bytecode = append(c.bytecode, op)
current := len(c.bytecode)
c.bytecode = append(c.bytecode, b...)
c.locations[current-1] = node.Location()
return current
return c.emitLocation(loc, op, arg)
}
func (c *compiler) emitPush(value interface{}) int {
return c.emit(OpPush, c.makeConstant(value)...)
return c.emit(OpPush, c.addConstant(value))
}
func (c *compiler) makeConstant(constant interface{}) []byte {
func (c *compiler) addConstant(constant interface{}) Opcode {
indexable := true
hash := constant
switch reflect.TypeOf(constant).Kind() {
@@ -100,7 +100,7 @@ func (c *compiler) makeConstant(constant interface{}) []byte {
if indexable {
if p, ok := c.index[hash]; ok {
return encode(p)
return p
}
}
@@ -109,26 +109,24 @@ func (c *compiler) makeConstant(constant interface{}) []byte {
panic("exceeded constants max space limit")
}
p := uint16(len(c.constants) - 1)
p := Opcode(len(c.constants) - 1)
if indexable {
c.index[hash] = p
}
return encode(p)
return p
}
func (c *compiler) placeholder() []byte {
return []byte{0xFF, 0xFF}
func (c *compiler) placeholder() Opcode {
return 0
}
func (c *compiler) patchJump(placeholder int) {
offset := len(c.bytecode) - 2 - placeholder
b := encode(uint16(offset))
c.bytecode[placeholder] = b[0]
c.bytecode[placeholder+1] = b[1]
offset := len(c.bytecode) - 1 - placeholder
c.bytecode[placeholder] = Opcode(offset)
}
func (c *compiler) calcBackwardJump(to int) []byte {
return encode(uint16(len(c.bytecode) + 1 + 2 - to))
func (c *compiler) calcBackwardJump(to int) Opcode {
return Opcode(len(c.bytecode) + 2 - to)
}
func (c *compiler) compile(node ast.Node) {
@@ -191,14 +189,14 @@ func (c *compiler) NilNode(_ *ast.NilNode) {
func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
if c.mapEnv {
c.emit(OpFetchEnvFast, c.makeConstant(node.Value)...)
c.emit(OpFetchEnvFast, c.addConstant(node.Value))
} else if len(node.Index) > 0 {
c.emit(OpFetchEnvField, c.makeConstant(&runtime.Field{
c.emit(OpFetchEnvField, c.addConstant(&runtime.Field{
Index: node.Index,
Path: node.Value,
})...)
}))
} else {
c.emit(OpFetchEnv, c.makeConstant(node.Value)...)
c.emit(OpFetchEnv, c.addConstant(node.Value))
}
if node.Deref {
c.emit(OpDeref)
@@ -305,14 +303,14 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
case "or", "||":
c.compile(node.Left)
end := c.emit(OpJumpIfTrue, c.placeholder()...)
end := c.emit(OpJumpIfTrue, placeholder)
c.emit(OpPop)
c.compile(node.Right)
c.patchJump(end)
case "and", "&&":
c.compile(node.Left)
end := c.emit(OpJumpIfFalse, c.placeholder()...)
end := c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
c.compile(node.Right)
c.patchJump(end)
@@ -407,7 +405,7 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) {
func (c *compiler) MatchesNode(node *ast.MatchesNode) {
if node.Regexp != nil {
c.compile(node.Left)
c.emit(OpMatchesConst, c.makeConstant(node.Regexp)...)
c.emit(OpMatchesConst, c.addConstant(node.Regexp))
return
}
c.compile(node.Left)
@@ -441,9 +439,9 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
}
index = append(ident.Index, index...)
path = ident.Value + "." + path
c.emitLoc(ident, OpFetchEnvField, c.makeConstant(
c.emitLocation(ident.Location(), OpFetchEnvField, c.addConstant(
&runtime.Field{Index: index, Path: path},
)...)
))
goto deref
}
member, ok := base.(*ast.MemberNode)
@@ -463,7 +461,7 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
c.compile(base)
if node.Optional {
ph := c.emit(OpJumpIfNil, c.placeholder()...)
ph := c.emit(OpJumpIfNil, placeholder)
c.chains[len(c.chains)-1] = append(c.chains[len(c.chains)-1], ph)
}
@@ -471,9 +469,9 @@ func (c *compiler) MemberNode(node *ast.MemberNode) {
c.compile(node.Property)
c.emit(OpFetch)
} else {
c.emitLoc(node, op, c.makeConstant(
c.emitLocation(node.Location(), op, c.addConstant(
&runtime.Field{Index: index, Path: path},
)...)
))
}
deref:
@@ -505,12 +503,8 @@ func (c *compiler) CallNode(node *ast.CallNode) {
if node.Fast {
op = OpCallFast
}
i := len(node.Arguments)
if i > 255 {
panic("too many arguments")
}
c.compile(node.Callee)
c.emit(op, encode(uint16(i))...)
c.emit(op, Opcode(len(node.Arguments)))
}
func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
@@ -527,7 +521,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
var loopBreak int
c.emitLoop(func() {
c.compile(node.Arguments[1])
loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...)
loopBreak = c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
})
c.emit(OpTrue)
@@ -541,7 +535,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emit(OpNot)
loopBreak = c.emit(OpJumpIfFalse, c.placeholder()...)
loopBreak = c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
})
c.emit(OpTrue)
@@ -554,7 +548,7 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
var loopBreak int
c.emitLoop(func() {
c.compile(node.Arguments[1])
loopBreak = c.emit(OpJumpIfTrue, c.placeholder()...)
loopBreak = c.emit(OpJumpIfTrue, placeholder)
c.emit(OpPop)
})
c.emit(OpFalse)
@@ -562,39 +556,39 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
c.emit(OpEnd)
case "one":
count := c.makeConstant("count")
count := c.addConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emit(OpStore, count)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
c.emit(OpInc, count)
})
})
c.emit(OpLoad, count...)
c.emit(OpLoad, count)
c.emitPush(1)
c.emit(OpEqual)
c.emit(OpEnd)
case "filter":
count := c.makeConstant("count")
count := c.addConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emit(OpStore, count)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
c.emit(OpInc, count)
c.emit(OpLoad, c.makeConstant("array")...)
c.emit(OpLoad, c.makeConstant("i")...)
c.emit(OpLoad, c.addConstant("array"))
c.emit(OpLoad, c.addConstant("i"))
c.emit(OpFetch)
})
})
c.emit(OpLoad, count...)
c.emit(OpLoad, count)
c.emit(OpEnd)
c.emit(OpArray)
@@ -604,23 +598,23 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
size := c.emitLoop(func() {
c.compile(node.Arguments[1])
})
c.emit(OpLoad, size...)
c.emit(OpLoad, size)
c.emit(OpEnd)
c.emit(OpArray)
case "count":
count := c.makeConstant("count")
count := c.addConstant("count")
c.compile(node.Arguments[0])
c.emit(OpBegin)
c.emitPush(0)
c.emit(OpStore, count...)
c.emit(OpStore, count)
c.emitLoop(func() {
c.compile(node.Arguments[1])
c.emitCond(func() {
c.emit(OpInc, count...)
c.emit(OpInc, count)
})
})
c.emit(OpLoad, count...)
c.emit(OpLoad, count)
c.emit(OpEnd)
default:
@@ -629,39 +623,39 @@ func (c *compiler) BuiltinNode(node *ast.BuiltinNode) {
}
func (c *compiler) emitCond(body func()) {
noop := c.emit(OpJumpIfFalse, c.placeholder()...)
noop := c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
body()
jmp := c.emit(OpJump, c.placeholder()...)
jmp := c.emit(OpJump, placeholder)
c.patchJump(noop)
c.emit(OpPop)
c.patchJump(jmp)
}
func (c *compiler) emitLoop(body func()) []byte {
i := c.makeConstant("i")
size := c.makeConstant("size")
array := c.makeConstant("array")
func (c *compiler) emitLoop(body func()) Opcode {
i := c.addConstant("i")
size := c.addConstant("size")
array := c.addConstant("array")
c.emit(OpLen)
c.emit(OpStore, size...)
c.emit(OpStore, array...)
c.emit(OpStore, size)
c.emit(OpStore, array)
c.emitPush(0)
c.emit(OpStore, i...)
c.emit(OpStore, i)
cond := len(c.bytecode)
c.emit(OpLoad, i...)
c.emit(OpLoad, size...)
c.emit(OpLoad, i)
c.emit(OpLoad, size)
c.emit(OpLess)
end := c.emit(OpJumpIfFalse, c.placeholder()...)
end := c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
body()
c.emit(OpInc, i...)
c.emit(OpJumpBackward, c.calcBackwardJump(cond)...)
c.emit(OpInc, i)
c.emit(OpJumpBackward, c.calcBackwardJump(cond))
c.patchJump(end)
c.emit(OpPop)
@@ -674,18 +668,18 @@ func (c *compiler) ClosureNode(node *ast.ClosureNode) {
}
func (c *compiler) PointerNode(node *ast.PointerNode) {
c.emit(OpLoad, c.makeConstant("array")...)
c.emit(OpLoad, c.makeConstant("i")...)
c.emit(OpLoad, c.addConstant("array"))
c.emit(OpLoad, c.addConstant("i"))
c.emit(OpFetch)
}
func (c *compiler) ConditionalNode(node *ast.ConditionalNode) {
c.compile(node.Cond)
otherwise := c.emit(OpJumpIfFalse, c.placeholder()...)
otherwise := c.emit(OpJumpIfFalse, placeholder)
c.emit(OpPop)
c.compile(node.Exp1)
end := c.emit(OpJump, c.placeholder()...)
end := c.emit(OpJump, placeholder)
c.patchJump(otherwise)
c.emit(OpPop)
@@ -717,12 +711,6 @@ func (c *compiler) PairNode(node *ast.PairNode) {
c.compile(node.Value)
}
func encode(i uint16) []byte {
b := make([]byte, 2)
binary.LittleEndian.PutUint16(b, i)
return b
}
func kind(node ast.Node) reflect.Kind {
t := node.Type()
if t == nil {
@@ -741,3 +729,5 @@ func deref(t reflect.Type) reflect.Type {
func isStruct(t reflect.Type) bool {
return t.Kind() == reflect.Struct
}
const placeholder = math.MaxUint32
+44 -44
View File
@@ -46,8 +46,8 @@ func TestCompile(t *testing.T) {
Constants: []interface{}{
int(math.MaxUint16),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
},
},
},
@@ -57,16 +57,16 @@ func TestCompile(t *testing.T) {
Constants: []interface{}{
float64(.5),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
},
},
},
{
`true`,
vm.Program{
Bytecode: []byte{
vm.OpTrue,
Bytecode: []vm.Opcode{
vm.OpTrue, 0,
},
},
},
@@ -76,8 +76,8 @@ func TestCompile(t *testing.T) {
Constants: []interface{}{
"string",
},
Bytecode: []byte{
vm.OpPush, 0, 0,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
},
},
},
@@ -87,10 +87,10 @@ func TestCompile(t *testing.T) {
Constants: []interface{}{
"string",
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpPush, 0, 0,
vm.OpEqualString,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
vm.OpPush, 0,
vm.OpEqualString, 0,
},
},
},
@@ -100,10 +100,10 @@ func TestCompile(t *testing.T) {
Constants: []interface{}{
int64(1000000),
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpPush, 0, 0,
vm.OpEqualInt,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
vm.OpPush, 0,
vm.OpEqualInt, 0,
},
},
},
@@ -111,22 +111,22 @@ func TestCompile(t *testing.T) {
`-1`,
vm.Program{
Constants: []interface{}{-1},
Bytecode: []byte{
vm.OpPush, 0, 0,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
},
},
},
{
`true && true || true`,
vm.Program{
Bytecode: []byte{
vm.OpTrue,
vm.OpJumpIfFalse, 2, 0,
vm.OpPop,
vm.OpTrue,
vm.OpJumpIfTrue, 2, 0,
vm.OpPop,
vm.OpTrue,
Bytecode: []vm.Opcode{
vm.OpTrue, 0,
vm.OpJumpIfFalse, 4,
vm.OpPop, 0,
vm.OpTrue, 0,
vm.OpJumpIfTrue, 4,
vm.OpPop, 0,
vm.OpTrue, 0,
},
},
},
@@ -139,8 +139,8 @@ func TestCompile(t *testing.T) {
Path: "A.B.C.D",
},
},
Bytecode: []byte{
vm.OpFetchEnvField, 0, 0,
Bytecode: []vm.Opcode{
vm.OpFetchEnvField, 0,
},
},
},
@@ -157,10 +157,10 @@ func TestCompile(t *testing.T) {
Path: "B.C.D",
},
},
Bytecode: []byte{
vm.OpFetchEnvField, 0, 0,
vm.OpJumpIfNil, 3, 0,
vm.OpFetchField, 1, 0,
Bytecode: []vm.Opcode{
vm.OpFetchEnvField, 0,
vm.OpJumpIfNil, 2,
vm.OpFetchField, 1,
},
},
},
@@ -177,10 +177,10 @@ func TestCompile(t *testing.T) {
Path: "C.D",
},
},
Bytecode: []byte{
vm.OpFetchEnvField, 0, 0,
vm.OpJumpIfNil, 3, 0,
vm.OpFetchField, 1, 0,
Bytecode: []vm.Opcode{
vm.OpFetchEnvField, 0,
vm.OpJumpIfNil, 2,
vm.OpFetchField, 1,
},
},
},
@@ -198,11 +198,11 @@ func TestCompile(t *testing.T) {
Path: "C.D",
},
},
Bytecode: []byte{
vm.OpFetchEnvField, 0, 0,
vm.OpPush, 1, 0,
vm.OpFetch,
vm.OpFetchField, 2, 0,
Bytecode: []vm.Opcode{
vm.OpFetchEnvField, 0,
vm.OpPush, 1,
vm.OpFetch, 0,
vm.OpFetchField, 2,
},
},
},
@@ -222,9 +222,9 @@ func TestCompile_cast(t *testing.T) {
Constants: []interface{}{
1,
},
Bytecode: []byte{
vm.OpPush, 0, 0,
vm.OpCast, 1, 0,
Bytecode: []vm.Opcode{
vm.OpPush, 0,
vm.OpCast, 1,
},
}
+8 -2
View File
@@ -34,8 +34,10 @@ func StartDebugger(program *Program, env interface{}) {
AddItem(sub, 0, 1, false)
app.SetRoot(flex, true)
done := false
go func() {
out, err := vm.Run(program, env)
done = true
app.QueueUpdateDraw(func() {
sub.RemoveItem(stack)
sub.RemoveItem(scope)
@@ -147,7 +149,9 @@ func StartDebugger(program *Program, env interface{}) {
if autostep {
if breakpoint != ip {
time.Sleep(20 * time.Millisecond)
vm.Step()
if !done {
vm.Step()
}
} else {
autostep = false
}
@@ -166,7 +170,9 @@ func StartDebugger(program *Program, env interface{}) {
breakpoint = getSelectedPosition()
autostep = true
}
vm.Step()
if !done {
vm.Step()
}
}
return event
})
+3 -1
View File
@@ -1,7 +1,9 @@
package vm
type Opcode uint32
const (
OpPush byte = iota
OpPush Opcode = iota
OpPop
OpRot
OpFetch
+13 -24
View File
@@ -1,7 +1,6 @@
package vm
import (
"encoding/binary"
"fmt"
"regexp"
@@ -11,9 +10,9 @@ import (
type Program struct {
Source *file.Source
Locations map[int]file.Location
Locations []file.Location
Constants []interface{}
Bytecode []byte
Bytecode []Opcode
}
func (program *Program) Disassemble() string {
@@ -21,37 +20,27 @@ func (program *Program) Disassemble() string {
ip := 0
for ip < len(program.Bytecode) {
pp := ip
op := program.Bytecode[ip]
ip++
op, arg := program.Bytecode[ip], program.Bytecode[ip+1]
ip += 2
readArg := func() uint16 {
if ip+1 >= len(program.Bytecode) {
return 0
}
i := binary.LittleEndian.Uint16([]byte{program.Bytecode[ip], program.Bytecode[ip+1]})
ip += 2
return i
}
code := func(label string) {
out += fmt.Sprintf("%v\t%v\n", pp, label)
}
jump := func(label string) {
a := readArg()
out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, a, ip+int(a))
out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, arg, ip+int(arg))
}
back := func(label string) {
a := readArg()
out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, a, ip-int(a))
out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, arg, ip-int(arg))
}
argument := func(label string) {
a := readArg()
out += fmt.Sprintf("%v\t%v\t%v\n", pp, label, a)
out += fmt.Sprintf("%v\t%v\t%v\n", pp, label, arg)
}
constant := func(label string) {
a := readArg()
var c interface{}
if int(a) < len(program.Constants) {
c = program.Constants[a]
if int(arg) < len(program.Constants) {
c = program.Constants[arg]
} else {
c = "out of range"
}
if r, ok := c.(*regexp.Regexp); ok {
c = r.String()
@@ -59,7 +48,7 @@ func (program *Program) Disassemble() string {
if field, ok := c.(*runtime.Field); ok {
c = fmt.Sprintf("%v %v", field.Path, field.Index)
}
out += fmt.Sprintf("%v\t%v\t%v\t%v\n", pp, label, a, c)
out += fmt.Sprintf("%v\t%v\t%v\t%v\n", pp, label, arg, c)
}
switch op {
@@ -220,7 +209,7 @@ func (program *Program) Disassemble() string {
code("OpEnd")
default:
out += fmt.Sprintf("%v\t%#x\n", pp, op)
out += fmt.Sprintf("%v\t%#x\n", ip, op)
}
}
return out
+1 -1
View File
@@ -11,7 +11,7 @@ func TestProgram_Disassemble(t *testing.T) {
for op := vm.OpPush; op < vm.OpEnd; op++ {
program := vm.Program{
Constants: []interface{}{true},
Bytecode: []byte{op},
Bytecode: []vm.Opcode{op, 0},
}
d := program.Disassemble()
if strings.Contains(d, "\t0x") {
+36 -60
View File
@@ -24,17 +24,14 @@ func Run(program *Program, env interface{}) (interface{}, error) {
}
type VM struct {
stack []interface{}
constants []interface{}
bytecode []byte
ip int
pp int
scopes []Scope
debug bool
step chan struct{}
curr chan int
memory int
limit int
stack []interface{}
ip int
scopes []Scope
debug bool
step chan struct{}
curr chan int
memory int
memoryBudget int
}
type Scope map[string]interface{}
@@ -52,18 +49,13 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
defer func() {
if r := recover(); r != nil {
f := &file.Error{
Location: program.Locations[vm.pp],
Location: program.Locations[(vm.ip-2)/2],
Message: fmt.Sprintf("%v", r),
}
err = f.Bind(program.Source)
}
}()
vm.limit = MemoryBudget
vm.memory = 0
vm.ip = 0
vm.pp = 0
if vm.stack == nil {
vm.stack = make([]interface{}, 0, 2)
} else {
@@ -74,23 +66,22 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
vm.scopes = vm.scopes[0:0]
}
vm.bytecode = program.Bytecode
vm.constants = program.Constants
for vm.ip < len(vm.bytecode) {
vm.memoryBudget = MemoryBudget
vm.memory = 0
vm.ip = 0
for vm.ip < len(program.Bytecode) {
if vm.debug {
<-vm.step
}
vm.pp = vm.ip
vm.ip++
op := vm.bytecode[vm.pp]
op, arg := program.Bytecode[vm.ip], program.Bytecode[vm.ip+1]
vm.ip += 2
switch op {
case OpPush:
vm.push(vm.constant())
vm.push(program.Constants[arg])
case OpPop:
vm.pop()
@@ -108,16 +99,16 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
case OpFetchField:
a := vm.pop()
vm.push(runtime.FetchField(a, vm.constant().(*runtime.Field)))
vm.push(runtime.FetchField(a, program.Constants[arg].(*runtime.Field)))
case OpFetchEnv:
vm.push(runtime.Fetch(env, vm.constant()))
vm.push(runtime.Fetch(env, program.Constants[arg]))
case OpFetchEnvField:
vm.push(runtime.FetchField(env, vm.constant().(*runtime.Field)))
vm.push(runtime.FetchField(env, program.Constants[arg].(*runtime.Field)))
case OpFetchEnvFast:
vm.push(env.(map[string]interface{})[vm.constant().(string)])
vm.push(env.(map[string]interface{})[program.Constants[arg].(string)])
case OpTrue:
vm.push(true)
@@ -152,30 +143,27 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
vm.push(a.(string) == b.(string))
case OpJump:
offset := vm.arg()
offset := arg
vm.ip += int(offset)
case OpJumpIfTrue:
offset := vm.arg()
offset := arg
if vm.current().(bool) {
vm.ip += int(offset)
}
case OpJumpIfFalse:
offset := vm.arg()
if !vm.current().(bool) {
vm.ip += int(offset)
vm.ip += int(arg)
}
case OpJumpIfNil:
offset := vm.arg()
if runtime.IsNil(vm.current()) {
vm.ip += int(offset)
vm.ip += int(arg)
}
case OpJumpBackward:
offset := vm.arg()
vm.ip -= int(offset)
vm.ip -= int(arg)
case OpIn:
b := vm.pop()
@@ -238,7 +226,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
min := runtime.ToInt(a)
max := runtime.ToInt(b)
size := max - min + 1
if vm.memory+size >= vm.limit {
if vm.memory+size >= vm.memoryBudget {
panic("memory budget exceeded")
}
vm.push(runtime.MakeRange(min, max))
@@ -256,7 +244,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
case OpMatchesConst:
a := vm.pop()
r := vm.constant().(*regexp.Regexp)
r := program.Constants[arg].(*regexp.Regexp)
vm.push(r.MatchString(a.(string)))
case OpContains:
@@ -282,7 +270,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
case OpCall:
fn := reflect.ValueOf(vm.pop())
size := vm.arg()
size := arg
in := make([]reflect.Value, size)
for i := int(size) - 1; i >= 0; i-- {
param := vm.pop()
@@ -302,7 +290,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
case OpCallFast:
fn := vm.pop().(func(...interface{}) interface{})
size := vm.arg()
size := arg
in := make([]interface{}, size)
for i := int(size) - 1; i >= 0; i-- {
in[i] = vm.pop()
@@ -317,7 +305,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
}
vm.push(array)
vm.memory += size
if vm.memory >= vm.limit {
if vm.memory >= vm.memoryBudget {
panic("memory budget exceeded")
}
@@ -331,7 +319,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
}
vm.push(m)
vm.memory += size
if vm.memory >= vm.limit {
if vm.memory >= vm.memoryBudget {
panic("memory budget exceeded")
}
@@ -339,7 +327,7 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
vm.push(runtime.Length(vm.current()))
case OpCast:
t := vm.arg()
t := arg
switch t {
case 0:
vm.push(runtime.ToInt64(vm.pop()))
@@ -349,18 +337,18 @@ func (vm *VM) Run(program *Program, env interface{}) (out interface{}, err error
case OpStore:
scope := vm.Scope()
key := vm.constant().(string)
key := program.Constants[arg].(string)
value := vm.pop()
scope[key] = value
case OpLoad:
scope := vm.Scope()
key := vm.constant().(string)
key := program.Constants[arg].(string)
vm.push(scope[key])
case OpInc:
scope := vm.Scope()
key := vm.constant().(string)
key := program.Constants[arg].(string)
i := scope[key].(int)
i++
scope[key] = i
@@ -411,16 +399,6 @@ func (vm *VM) pop() interface{} {
return value
}
func (vm *VM) arg() uint16 {
b0, b1 := vm.bytecode[vm.ip], vm.bytecode[vm.ip+1]
vm.ip += 2
return uint16(b0) | uint16(b1)<<8
}
func (vm *VM) constant() interface{} {
return vm.constants[vm.arg()]
}
func (vm *VM) Stack() []interface{} {
return vm.stack
}
@@ -433,9 +411,7 @@ func (vm *VM) Scope() Scope {
}
func (vm *VM) Step() {
if vm.ip < len(vm.bytecode) {
vm.step <- struct{}{}
}
vm.step <- struct{}{}
}
func (vm *VM) Position() chan int {