mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Simplify vm and opcodes
This commit is contained in:
+83
-93
@@ -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
@@ -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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -1,7 +1,9 @@
|
||||
package vm
|
||||
|
||||
type Opcode uint32
|
||||
|
||||
const (
|
||||
OpPush byte = iota
|
||||
OpPush Opcode = iota
|
||||
OpPop
|
||||
OpRot
|
||||
OpFetch
|
||||
|
||||
+13
-24
@@ -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
@@ -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") {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user