This commit is contained in:
Adnan Hajdarevic 2021-04-03 18:01:13 +02:00
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

File diff suppressed because it is too large Load diff

53
vendor/github.com/antonmedv/expr/vm/opcodes.go generated vendored Normal file
View 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
View 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
View 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
View 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(&param).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(&param).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
}