mirror of
https://github.com/adnanh/webhook.git
synced 2025-07-05 02:38:31 +00:00
wip
This commit is contained in:
parent
e329b6d9ff
commit
568c711625
138 changed files with 22876 additions and 90497 deletions
3247
vendor/github.com/antonmedv/expr/vm/helpers.go
generated
vendored
Normal file
3247
vendor/github.com/antonmedv/expr/vm/helpers.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
53
vendor/github.com/antonmedv/expr/vm/opcodes.go
generated
vendored
Normal file
53
vendor/github.com/antonmedv/expr/vm/opcodes.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
package vm
|
||||
|
||||
const (
|
||||
OpPush byte = iota
|
||||
OpPop
|
||||
OpRot
|
||||
OpFetch
|
||||
OpFetchMap
|
||||
OpTrue
|
||||
OpFalse
|
||||
OpNil
|
||||
OpNegate
|
||||
OpNot
|
||||
OpEqual
|
||||
OpEqualInt
|
||||
OpEqualString
|
||||
OpJump
|
||||
OpJumpIfTrue
|
||||
OpJumpIfFalse
|
||||
OpJumpBackward
|
||||
OpIn
|
||||
OpLess
|
||||
OpMore
|
||||
OpLessOrEqual
|
||||
OpMoreOrEqual
|
||||
OpAdd
|
||||
OpSubtract
|
||||
OpMultiply
|
||||
OpDivide
|
||||
OpModulo
|
||||
OpExponent
|
||||
OpRange
|
||||
OpMatches
|
||||
OpMatchesConst
|
||||
OpContains
|
||||
OpStartsWith
|
||||
OpEndsWith
|
||||
OpIndex
|
||||
OpSlice
|
||||
OpProperty
|
||||
OpCall
|
||||
OpCallFast
|
||||
OpMethod
|
||||
OpArray
|
||||
OpMap
|
||||
OpLen
|
||||
OpCast
|
||||
OpStore
|
||||
OpLoad
|
||||
OpInc
|
||||
OpBegin
|
||||
OpEnd // This opcode must be at the end of this list.
|
||||
)
|
216
vendor/github.com/antonmedv/expr/vm/program.go
generated
vendored
Normal file
216
vendor/github.com/antonmedv/expr/vm/program.go
generated
vendored
Normal file
|
@ -0,0 +1,216 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/antonmedv/expr/file"
|
||||
)
|
||||
|
||||
type Program struct {
|
||||
Source *file.Source
|
||||
Locations map[int]file.Location
|
||||
Constants []interface{}
|
||||
Bytecode []byte
|
||||
}
|
||||
|
||||
func (program *Program) Disassemble() string {
|
||||
out := ""
|
||||
ip := 0
|
||||
for ip < len(program.Bytecode) {
|
||||
pp := ip
|
||||
op := program.Bytecode[ip]
|
||||
ip++
|
||||
|
||||
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))
|
||||
}
|
||||
back := func(label string) {
|
||||
a := readArg()
|
||||
out += fmt.Sprintf("%v\t%v\t%v\t(%v)\n", pp, label, a, ip-int(a))
|
||||
}
|
||||
argument := func(label string) {
|
||||
a := readArg()
|
||||
out += fmt.Sprintf("%v\t%v\t%v\n", pp, label, a)
|
||||
}
|
||||
constant := func(label string) {
|
||||
a := readArg()
|
||||
var c interface{}
|
||||
if int(a) < len(program.Constants) {
|
||||
c = program.Constants[a]
|
||||
}
|
||||
if r, ok := c.(*regexp.Regexp); ok {
|
||||
c = r.String()
|
||||
}
|
||||
out += fmt.Sprintf("%v\t%v\t%v\t%#v\n", pp, label, a, c)
|
||||
}
|
||||
|
||||
switch op {
|
||||
case OpPush:
|
||||
constant("OpPush")
|
||||
|
||||
case OpPop:
|
||||
code("OpPop")
|
||||
|
||||
case OpRot:
|
||||
code("OpRot")
|
||||
|
||||
case OpFetch:
|
||||
constant("OpFetch")
|
||||
|
||||
case OpFetchMap:
|
||||
constant("OpFetchMap")
|
||||
|
||||
case OpTrue:
|
||||
code("OpTrue")
|
||||
|
||||
case OpFalse:
|
||||
code("OpFalse")
|
||||
|
||||
case OpNil:
|
||||
code("OpNil")
|
||||
|
||||
case OpNegate:
|
||||
code("OpNegate")
|
||||
|
||||
case OpNot:
|
||||
code("OpNot")
|
||||
|
||||
case OpEqual:
|
||||
code("OpEqual")
|
||||
|
||||
case OpEqualInt:
|
||||
code("OpEqualInt")
|
||||
|
||||
case OpEqualString:
|
||||
code("OpEqualString")
|
||||
|
||||
case OpJump:
|
||||
jump("OpJump")
|
||||
|
||||
case OpJumpIfTrue:
|
||||
jump("OpJumpIfTrue")
|
||||
|
||||
case OpJumpIfFalse:
|
||||
jump("OpJumpIfFalse")
|
||||
|
||||
case OpJumpBackward:
|
||||
back("OpJumpBackward")
|
||||
|
||||
case OpIn:
|
||||
code("OpIn")
|
||||
|
||||
case OpLess:
|
||||
code("OpLess")
|
||||
|
||||
case OpMore:
|
||||
code("OpMore")
|
||||
|
||||
case OpLessOrEqual:
|
||||
code("OpLessOrEqual")
|
||||
|
||||
case OpMoreOrEqual:
|
||||
code("OpMoreOrEqual")
|
||||
|
||||
case OpAdd:
|
||||
code("OpAdd")
|
||||
|
||||
case OpSubtract:
|
||||
code("OpSubtract")
|
||||
|
||||
case OpMultiply:
|
||||
code("OpMultiply")
|
||||
|
||||
case OpDivide:
|
||||
code("OpDivide")
|
||||
|
||||
case OpModulo:
|
||||
code("OpModulo")
|
||||
|
||||
case OpExponent:
|
||||
code("OpExponent")
|
||||
|
||||
case OpRange:
|
||||
code("OpRange")
|
||||
|
||||
case OpMatches:
|
||||
code("OpMatches")
|
||||
|
||||
case OpMatchesConst:
|
||||
constant("OpMatchesConst")
|
||||
|
||||
case OpContains:
|
||||
code("OpContains")
|
||||
|
||||
case OpStartsWith:
|
||||
code("OpStartsWith")
|
||||
|
||||
case OpEndsWith:
|
||||
code("OpEndsWith")
|
||||
|
||||
case OpIndex:
|
||||
code("OpIndex")
|
||||
|
||||
case OpSlice:
|
||||
code("OpSlice")
|
||||
|
||||
case OpProperty:
|
||||
constant("OpProperty")
|
||||
|
||||
case OpCall:
|
||||
constant("OpCall")
|
||||
|
||||
case OpCallFast:
|
||||
constant("OpCallFast")
|
||||
|
||||
case OpMethod:
|
||||
constant("OpMethod")
|
||||
|
||||
case OpArray:
|
||||
code("OpArray")
|
||||
|
||||
case OpMap:
|
||||
code("OpMap")
|
||||
|
||||
case OpLen:
|
||||
code("OpLen")
|
||||
|
||||
case OpCast:
|
||||
argument("OpCast")
|
||||
|
||||
case OpStore:
|
||||
constant("OpStore")
|
||||
|
||||
case OpLoad:
|
||||
constant("OpLoad")
|
||||
|
||||
case OpInc:
|
||||
constant("OpInc")
|
||||
|
||||
case OpBegin:
|
||||
code("OpBegin")
|
||||
|
||||
case OpEnd:
|
||||
code("OpEnd")
|
||||
|
||||
default:
|
||||
out += fmt.Sprintf("%v\t%#x\n", pp, op)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
343
vendor/github.com/antonmedv/expr/vm/runtime.go
generated
vendored
Normal file
343
vendor/github.com/antonmedv/expr/vm/runtime.go
generated
vendored
Normal file
|
@ -0,0 +1,343 @@
|
|||
package vm
|
||||
|
||||
//go:generate go run ./generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Name string
|
||||
Size int
|
||||
}
|
||||
|
||||
type Scope map[string]interface{}
|
||||
|
||||
func fetch(from interface{}, i interface{}) interface{} {
|
||||
v := reflect.ValueOf(from)
|
||||
kind := v.Kind()
|
||||
|
||||
// Structures can be access through a pointer or through a value, when they
|
||||
// are accessed through a pointer we don't want to copy them to a value.
|
||||
if kind == reflect.Ptr && reflect.Indirect(v).Kind() == reflect.Struct {
|
||||
v = reflect.Indirect(v)
|
||||
kind = v.Kind()
|
||||
}
|
||||
|
||||
switch kind {
|
||||
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
value := v.Index(toInt(i))
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface()
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
value := v.MapIndex(reflect.ValueOf(i))
|
||||
if value.IsValid() {
|
||||
if value.CanInterface() {
|
||||
return value.Interface()
|
||||
}
|
||||
} else {
|
||||
elem := reflect.TypeOf(from).Elem()
|
||||
return reflect.Zero(elem).Interface()
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
value := v.FieldByName(reflect.ValueOf(i).String())
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface()
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("cannot fetch %v from %T", i, from))
|
||||
}
|
||||
|
||||
func slice(array, from, to interface{}) interface{} {
|
||||
v := reflect.ValueOf(array)
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.String:
|
||||
length := v.Len()
|
||||
a, b := toInt(from), toInt(to)
|
||||
|
||||
if b > length {
|
||||
b = length
|
||||
}
|
||||
if a > b {
|
||||
a = b
|
||||
}
|
||||
|
||||
value := v.Slice(a, b)
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Interface()
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
value := v.Elem()
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return slice(value.Interface(), from, to)
|
||||
}
|
||||
|
||||
}
|
||||
panic(fmt.Sprintf("cannot slice %v", from))
|
||||
}
|
||||
|
||||
func FetchFn(from interface{}, name string) reflect.Value {
|
||||
v := reflect.ValueOf(from)
|
||||
|
||||
// Methods can be defined on any type.
|
||||
if v.NumMethod() > 0 {
|
||||
method := v.MethodByName(name)
|
||||
if method.IsValid() {
|
||||
return method
|
||||
}
|
||||
}
|
||||
|
||||
d := v
|
||||
if v.Kind() == reflect.Ptr {
|
||||
d = v.Elem()
|
||||
}
|
||||
|
||||
switch d.Kind() {
|
||||
case reflect.Map:
|
||||
value := d.MapIndex(reflect.ValueOf(name))
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return value.Elem()
|
||||
}
|
||||
case reflect.Struct:
|
||||
// If struct has not method, maybe it has func field.
|
||||
// To access this field we need dereference value.
|
||||
value := d.FieldByName(name)
|
||||
if value.IsValid() {
|
||||
return value
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf(`cannot get "%v" from %T`, name, from))
|
||||
}
|
||||
|
||||
func in(needle interface{}, array interface{}) bool {
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
v := reflect.ValueOf(array)
|
||||
|
||||
switch v.Kind() {
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
value := v.Index(i)
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
if equal(value.Interface(), needle).(bool) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
case reflect.Map:
|
||||
n := reflect.ValueOf(needle)
|
||||
if !n.IsValid() {
|
||||
panic(fmt.Sprintf("cannot use %T as index to %T", needle, array))
|
||||
}
|
||||
value := v.MapIndex(n)
|
||||
if value.IsValid() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case reflect.Struct:
|
||||
n := reflect.ValueOf(needle)
|
||||
if !n.IsValid() || n.Kind() != reflect.String {
|
||||
panic(fmt.Sprintf("cannot use %T as field name of %T", needle, array))
|
||||
}
|
||||
value := v.FieldByName(n.String())
|
||||
if value.IsValid() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case reflect.Ptr:
|
||||
value := v.Elem()
|
||||
if value.IsValid() && value.CanInterface() {
|
||||
return in(needle, value.Interface())
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf(`operator "in"" not defined on %T`, array))
|
||||
}
|
||||
|
||||
func length(a interface{}) int {
|
||||
v := reflect.ValueOf(a)
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
|
||||
return v.Len()
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid argument for len (type %T)", a))
|
||||
}
|
||||
}
|
||||
|
||||
func negate(i interface{}) interface{} {
|
||||
switch v := i.(type) {
|
||||
case float32:
|
||||
return -v
|
||||
case float64:
|
||||
return -v
|
||||
|
||||
case int:
|
||||
return -v
|
||||
case int8:
|
||||
return -v
|
||||
case int16:
|
||||
return -v
|
||||
case int32:
|
||||
return -v
|
||||
case int64:
|
||||
return -v
|
||||
|
||||
case uint:
|
||||
return -v
|
||||
case uint8:
|
||||
return -v
|
||||
case uint16:
|
||||
return -v
|
||||
case uint32:
|
||||
return -v
|
||||
case uint64:
|
||||
return -v
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid operation: - %T", v))
|
||||
}
|
||||
}
|
||||
|
||||
func exponent(a, b interface{}) float64 {
|
||||
return math.Pow(toFloat64(a), toFloat64(b))
|
||||
}
|
||||
|
||||
func makeRange(min, max int) []int {
|
||||
size := max - min + 1
|
||||
rng := make([]int, size)
|
||||
for i := range rng {
|
||||
rng[i] = min + i
|
||||
}
|
||||
return rng
|
||||
}
|
||||
|
||||
func toInt(a interface{}) int {
|
||||
switch x := a.(type) {
|
||||
case float32:
|
||||
return int(x)
|
||||
case float64:
|
||||
return int(x)
|
||||
|
||||
case int:
|
||||
return x
|
||||
case int8:
|
||||
return int(x)
|
||||
case int16:
|
||||
return int(x)
|
||||
case int32:
|
||||
return int(x)
|
||||
case int64:
|
||||
return int(x)
|
||||
|
||||
case uint:
|
||||
return int(x)
|
||||
case uint8:
|
||||
return int(x)
|
||||
case uint16:
|
||||
return int(x)
|
||||
case uint32:
|
||||
return int(x)
|
||||
case uint64:
|
||||
return int(x)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid operation: int(%T)", x))
|
||||
}
|
||||
}
|
||||
|
||||
func toInt64(a interface{}) int64 {
|
||||
switch x := a.(type) {
|
||||
case float32:
|
||||
return int64(x)
|
||||
case float64:
|
||||
return int64(x)
|
||||
|
||||
case int:
|
||||
return int64(x)
|
||||
case int8:
|
||||
return int64(x)
|
||||
case int16:
|
||||
return int64(x)
|
||||
case int32:
|
||||
return int64(x)
|
||||
case int64:
|
||||
return x
|
||||
|
||||
case uint:
|
||||
return int64(x)
|
||||
case uint8:
|
||||
return int64(x)
|
||||
case uint16:
|
||||
return int64(x)
|
||||
case uint32:
|
||||
return int64(x)
|
||||
case uint64:
|
||||
return int64(x)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid operation: int64(%T)", x))
|
||||
}
|
||||
}
|
||||
|
||||
func toFloat64(a interface{}) float64 {
|
||||
switch x := a.(type) {
|
||||
case float32:
|
||||
return float64(x)
|
||||
case float64:
|
||||
return x
|
||||
|
||||
case int:
|
||||
return float64(x)
|
||||
case int8:
|
||||
return float64(x)
|
||||
case int16:
|
||||
return float64(x)
|
||||
case int32:
|
||||
return float64(x)
|
||||
case int64:
|
||||
return float64(x)
|
||||
|
||||
case uint:
|
||||
return float64(x)
|
||||
case uint8:
|
||||
return float64(x)
|
||||
case uint16:
|
||||
return float64(x)
|
||||
case uint32:
|
||||
return float64(x)
|
||||
case uint64:
|
||||
return float64(x)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid operation: float64(%T)", x))
|
||||
}
|
||||
}
|
||||
|
||||
func isNil(v interface{}) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
r := reflect.ValueOf(v)
|
||||
switch r.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
|
||||
return r.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
439
vendor/github.com/antonmedv/expr/vm/vm.go
generated
vendored
Normal file
439
vendor/github.com/antonmedv/expr/vm/vm.go
generated
vendored
Normal file
|
@ -0,0 +1,439 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/antonmedv/expr/file"
|
||||
)
|
||||
|
||||
var (
|
||||
MemoryBudget int = 1e6
|
||||
)
|
||||
|
||||
func Run(program *Program, env interface{}) (interface{}, error) {
|
||||
if program == nil {
|
||||
return nil, fmt.Errorf("program is nil")
|
||||
}
|
||||
|
||||
vm := VM{}
|
||||
return vm.Run(program, env)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 interface{}) (out interface{}, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
f := &file.Error{
|
||||
Location: program.Locations[vm.pp],
|
||||
Message: fmt.Sprintf("%v", r),
|
||||
}
|
||||
err = f.Bind(program.Source)
|
||||
}
|
||||
}()
|
||||
|
||||
vm.limit = MemoryBudget
|
||||
vm.ip = 0
|
||||
vm.pp = 0
|
||||
|
||||
if vm.stack == nil {
|
||||
vm.stack = make([]interface{}, 0, 2)
|
||||
} else {
|
||||
vm.stack = vm.stack[0:0]
|
||||
}
|
||||
|
||||
if vm.scopes != nil {
|
||||
vm.scopes = vm.scopes[0:0]
|
||||
}
|
||||
|
||||
vm.bytecode = program.Bytecode
|
||||
vm.constants = program.Constants
|
||||
|
||||
for vm.ip < len(vm.bytecode) {
|
||||
|
||||
if vm.debug {
|
||||
<-vm.step
|
||||
}
|
||||
|
||||
vm.pp = vm.ip
|
||||
vm.ip++
|
||||
op := vm.bytecode[vm.pp]
|
||||
|
||||
switch op {
|
||||
|
||||
case OpPush:
|
||||
vm.push(vm.constant())
|
||||
|
||||
case OpPop:
|
||||
vm.pop()
|
||||
|
||||
case OpRot:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(b)
|
||||
vm.push(a)
|
||||
|
||||
case OpFetch:
|
||||
vm.push(fetch(env, vm.constant()))
|
||||
|
||||
case OpFetchMap:
|
||||
vm.push(env.(map[string]interface{})[vm.constant().(string)])
|
||||
|
||||
case OpTrue:
|
||||
vm.push(true)
|
||||
|
||||
case OpFalse:
|
||||
vm.push(false)
|
||||
|
||||
case OpNil:
|
||||
vm.push(nil)
|
||||
|
||||
case OpNegate:
|
||||
v := negate(vm.pop())
|
||||
vm.push(v)
|
||||
|
||||
case OpNot:
|
||||
v := vm.pop().(bool)
|
||||
vm.push(!v)
|
||||
|
||||
case OpEqual:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(equal(a, b))
|
||||
|
||||
case OpEqualInt:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(a.(int) == b.(int))
|
||||
|
||||
case OpEqualString:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(a.(string) == b.(string))
|
||||
|
||||
case OpJump:
|
||||
offset := vm.arg()
|
||||
vm.ip += int(offset)
|
||||
|
||||
case OpJumpIfTrue:
|
||||
offset := vm.arg()
|
||||
if vm.current().(bool) {
|
||||
vm.ip += int(offset)
|
||||
}
|
||||
|
||||
case OpJumpIfFalse:
|
||||
offset := vm.arg()
|
||||
if !vm.current().(bool) {
|
||||
vm.ip += int(offset)
|
||||
}
|
||||
|
||||
case OpJumpBackward:
|
||||
offset := vm.arg()
|
||||
vm.ip -= int(offset)
|
||||
|
||||
case OpIn:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(in(a, b))
|
||||
|
||||
case OpLess:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(less(a, b))
|
||||
|
||||
case OpMore:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(more(a, b))
|
||||
|
||||
case OpLessOrEqual:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(lessOrEqual(a, b))
|
||||
|
||||
case OpMoreOrEqual:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(moreOrEqual(a, b))
|
||||
|
||||
case OpAdd:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(add(a, b))
|
||||
|
||||
case OpSubtract:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(subtract(a, b))
|
||||
|
||||
case OpMultiply:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(multiply(a, b))
|
||||
|
||||
case OpDivide:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(divide(a, b))
|
||||
|
||||
case OpModulo:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(modulo(a, b))
|
||||
|
||||
case OpExponent:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(exponent(a, b))
|
||||
|
||||
case OpRange:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
min := toInt(a)
|
||||
max := toInt(b)
|
||||
size := max - min + 1
|
||||
if vm.memory+size >= vm.limit {
|
||||
panic("memory budget exceeded")
|
||||
}
|
||||
vm.push(makeRange(min, max))
|
||||
vm.memory += size
|
||||
|
||||
case OpMatches:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
match, err := regexp.MatchString(b.(string), a.(string))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
vm.push(match)
|
||||
|
||||
case OpMatchesConst:
|
||||
a := vm.pop()
|
||||
r := vm.constant().(*regexp.Regexp)
|
||||
vm.push(r.MatchString(a.(string)))
|
||||
|
||||
case OpContains:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(strings.Contains(a.(string), b.(string)))
|
||||
|
||||
case OpStartsWith:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(strings.HasPrefix(a.(string), b.(string)))
|
||||
|
||||
case OpEndsWith:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(strings.HasSuffix(a.(string), b.(string)))
|
||||
|
||||
case OpIndex:
|
||||
b := vm.pop()
|
||||
a := vm.pop()
|
||||
vm.push(fetch(a, b))
|
||||
|
||||
case OpSlice:
|
||||
from := vm.pop()
|
||||
to := vm.pop()
|
||||
node := vm.pop()
|
||||
vm.push(slice(node, from, to))
|
||||
|
||||
case OpProperty:
|
||||
a := vm.pop()
|
||||
b := vm.constant()
|
||||
vm.push(fetch(a, b))
|
||||
|
||||
case OpCall:
|
||||
call := vm.constant().(Call)
|
||||
in := make([]reflect.Value, call.Size)
|
||||
for i := call.Size - 1; i >= 0; i-- {
|
||||
param := vm.pop()
|
||||
if param == nil && reflect.TypeOf(param) == nil {
|
||||
// In case of nil value and nil type use this hack,
|
||||
// otherwise reflect.Call will panic on zero value.
|
||||
in[i] = reflect.ValueOf(¶m).Elem()
|
||||
} else {
|
||||
in[i] = reflect.ValueOf(param)
|
||||
}
|
||||
}
|
||||
out := FetchFn(env, call.Name).Call(in)
|
||||
vm.push(out[0].Interface())
|
||||
|
||||
case OpCallFast:
|
||||
call := vm.constant().(Call)
|
||||
in := make([]interface{}, call.Size)
|
||||
for i := call.Size - 1; i >= 0; i-- {
|
||||
in[i] = vm.pop()
|
||||
}
|
||||
fn := FetchFn(env, call.Name).Interface()
|
||||
vm.push(fn.(func(...interface{}) interface{})(in...))
|
||||
|
||||
case OpMethod:
|
||||
call := vm.constants[vm.arg()].(Call)
|
||||
in := make([]reflect.Value, call.Size)
|
||||
for i := call.Size - 1; i >= 0; i-- {
|
||||
param := vm.pop()
|
||||
if param == nil && reflect.TypeOf(param) == nil {
|
||||
// In case of nil value and nil type use this hack,
|
||||
// otherwise reflect.Call will panic on zero value.
|
||||
in[i] = reflect.ValueOf(¶m).Elem()
|
||||
} else {
|
||||
in[i] = reflect.ValueOf(param)
|
||||
}
|
||||
}
|
||||
out := FetchFn(vm.pop(), call.Name).Call(in)
|
||||
vm.push(out[0].Interface())
|
||||
|
||||
case OpArray:
|
||||
size := vm.pop().(int)
|
||||
array := make([]interface{}, size)
|
||||
for i := size - 1; i >= 0; i-- {
|
||||
array[i] = vm.pop()
|
||||
}
|
||||
vm.push(array)
|
||||
vm.memory += size
|
||||
if vm.memory >= vm.limit {
|
||||
panic("memory budget exceeded")
|
||||
}
|
||||
|
||||
case OpMap:
|
||||
size := vm.pop().(int)
|
||||
m := make(map[string]interface{})
|
||||
for i := size - 1; i >= 0; i-- {
|
||||
value := vm.pop()
|
||||
key := vm.pop()
|
||||
m[key.(string)] = value
|
||||
}
|
||||
vm.push(m)
|
||||
vm.memory += size
|
||||
if vm.memory >= vm.limit {
|
||||
panic("memory budget exceeded")
|
||||
}
|
||||
|
||||
case OpLen:
|
||||
vm.push(length(vm.current()))
|
||||
|
||||
case OpCast:
|
||||
t := vm.arg()
|
||||
switch t {
|
||||
case 0:
|
||||
vm.push(toInt64(vm.pop()))
|
||||
case 1:
|
||||
vm.push(toFloat64(vm.pop()))
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
scope := vm.Scope()
|
||||
key := vm.constant().(string)
|
||||
value := vm.pop()
|
||||
scope[key] = value
|
||||
|
||||
case OpLoad:
|
||||
scope := vm.Scope()
|
||||
key := vm.constant().(string)
|
||||
vm.push(scope[key])
|
||||
|
||||
case OpInc:
|
||||
scope := vm.Scope()
|
||||
key := vm.constant().(string)
|
||||
i := scope[key].(int)
|
||||
i++
|
||||
scope[key] = i
|
||||
|
||||
case OpBegin:
|
||||
scope := make(Scope)
|
||||
vm.scopes = append(vm.scopes, scope)
|
||||
|
||||
case OpEnd:
|
||||
vm.scopes = vm.scopes[:len(vm.scopes)-1]
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown bytecode %#x", op))
|
||||
}
|
||||
|
||||
if vm.debug {
|
||||
vm.curr <- vm.ip
|
||||
}
|
||||
}
|
||||
|
||||
if vm.debug {
|
||||
close(vm.curr)
|
||||
close(vm.step)
|
||||
}
|
||||
|
||||
if len(vm.stack) > 0 {
|
||||
return vm.pop(), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (vm *VM) push(value interface{}) {
|
||||
vm.stack = append(vm.stack, value)
|
||||
}
|
||||
|
||||
func (vm *VM) current() interface{} {
|
||||
return vm.stack[len(vm.stack)-1]
|
||||
}
|
||||
|
||||
func (vm *VM) pop() interface{} {
|
||||
value := vm.stack[len(vm.stack)-1]
|
||||
vm.stack = vm.stack[:len(vm.stack)-1]
|
||||
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
|
||||
}
|
||||
|
||||
func (vm *VM) Scope() Scope {
|
||||
if len(vm.scopes) > 0 {
|
||||
return vm.scopes[len(vm.scopes)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VM) Step() {
|
||||
if vm.ip < len(vm.bytecode) {
|
||||
vm.step <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (vm *VM) Position() chan int {
|
||||
return vm.curr
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue