4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-27 21:39:22 +00:00

Update tengo vendor and load the stdlib. Fixes #789 (#792)

This commit is contained in:
Wim
2019-04-06 22:18:25 +02:00
committed by GitHub
parent cdf33e5748
commit 115d20373c
63 changed files with 7020 additions and 1304 deletions

View File

@ -3,6 +3,7 @@ package script
import (
"context"
"fmt"
"sync"
"github.com/d5/tengo/compiler"
"github.com/d5/tengo/objects"
@ -12,26 +13,39 @@ import (
// Compiled is a compiled instance of the user script.
// Use Script.Compile() to create Compiled object.
type Compiled struct {
symbolTable *compiler.SymbolTable
machine *runtime.VM
globalIndexes map[string]int // global symbol name to index
bytecode *compiler.Bytecode
globals []objects.Object
maxAllocs int64
lock sync.RWMutex
}
// Run executes the compiled script in the virtual machine.
func (c *Compiled) Run() error {
return c.machine.Run()
c.lock.Lock()
defer c.lock.Unlock()
v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs)
return v.Run()
}
// RunContext is like Run but includes a context.
func (c *Compiled) RunContext(ctx context.Context) (err error) {
c.lock.Lock()
defer c.lock.Unlock()
v := runtime.NewVM(c.bytecode, c.globals, c.maxAllocs)
ch := make(chan error, 1)
go func() {
ch <- c.machine.Run()
ch <- v.Run()
}()
select {
case <-ctx.Done():
c.machine.Abort()
v.Abort()
<-ch
err = ctx.Err()
case err = <-ch:
@ -40,30 +54,58 @@ func (c *Compiled) RunContext(ctx context.Context) (err error) {
return
}
// Clone creates a new copy of Compiled.
// Cloned copies are safe for concurrent use by multiple goroutines.
func (c *Compiled) Clone() *Compiled {
c.lock.Lock()
defer c.lock.Unlock()
clone := &Compiled{
globalIndexes: c.globalIndexes,
bytecode: c.bytecode,
globals: make([]objects.Object, len(c.globals)),
maxAllocs: c.maxAllocs,
}
// copy global objects
for idx, g := range c.globals {
if g != nil {
clone.globals[idx] = g
}
}
return clone
}
// IsDefined returns true if the variable name is defined (has value) before or after the execution.
func (c *Compiled) IsDefined(name string) bool {
symbol, _, ok := c.symbolTable.Resolve(name)
c.lock.RLock()
defer c.lock.RUnlock()
idx, ok := c.globalIndexes[name]
if !ok {
return false
}
v := c.machine.Globals()[symbol.Index]
v := c.globals[idx]
if v == nil {
return false
}
return *v != objects.UndefinedValue
return v != objects.UndefinedValue
}
// Get returns a variable identified by the name.
func (c *Compiled) Get(name string) *Variable {
value := &objects.UndefinedValue
c.lock.RLock()
defer c.lock.RUnlock()
symbol, _, ok := c.symbolTable.Resolve(name)
if ok && symbol.Scope == compiler.ScopeGlobal {
value = c.machine.Globals()[symbol.Index]
value := objects.UndefinedValue
if idx, ok := c.globalIndexes[name]; ok {
value = c.globals[idx]
if value == nil {
value = &objects.UndefinedValue
value = objects.UndefinedValue
}
}
@ -75,20 +117,21 @@ func (c *Compiled) Get(name string) *Variable {
// GetAll returns all the variables that are defined by the compiled script.
func (c *Compiled) GetAll() []*Variable {
var vars []*Variable
for _, name := range c.symbolTable.Names() {
symbol, _, ok := c.symbolTable.Resolve(name)
if ok && symbol.Scope == compiler.ScopeGlobal {
value := c.machine.Globals()[symbol.Index]
if value == nil {
value = &objects.UndefinedValue
}
c.lock.RLock()
defer c.lock.RUnlock()
vars = append(vars, &Variable{
name: name,
value: value,
})
var vars []*Variable
for name, idx := range c.globalIndexes {
value := c.globals[idx]
if value == nil {
value = objects.UndefinedValue
}
vars = append(vars, &Variable{
name: name,
value: value,
})
}
return vars
@ -97,17 +140,20 @@ func (c *Compiled) GetAll() []*Variable {
// Set replaces the value of a global variable identified by the name.
// An error will be returned if the name was not defined during compilation.
func (c *Compiled) Set(name string, value interface{}) error {
c.lock.Lock()
defer c.lock.Unlock()
obj, err := objects.FromInterface(value)
if err != nil {
return err
}
symbol, _, ok := c.symbolTable.Resolve(name)
if !ok || symbol.Scope != compiler.ScopeGlobal {
idx, ok := c.globalIndexes[name]
if !ok {
return fmt.Errorf("'%s' is not defined", name)
}
c.machine.Globals()[symbol.Index] = &obj
c.globals[idx] = obj
return nil
}

View File

@ -1,33 +0,0 @@
package script
import (
"github.com/d5/tengo/objects"
)
func objectToInterface(o objects.Object) interface{} {
switch val := o.(type) {
case *objects.Array:
return val.Value
case *objects.Map:
return val.Value
case *objects.Int:
return val.Value
case *objects.Float:
return val.Value
case *objects.Bool:
if val == objects.TrueValue {
return true
}
return false
case *objects.Char:
return val.Value
case *objects.String:
return val.Value
case *objects.Bytes:
return val.Value
case *objects.Undefined:
return nil
}
return o
}

View File

@ -14,17 +14,20 @@ import (
// Script can simplify compilation and execution of embedded scripts.
type Script struct {
variables map[string]*Variable
builtinFuncs []objects.Object
builtinModules map[string]*objects.Object
userModuleLoader compiler.ModuleLoader
modules *objects.ModuleMap
input []byte
maxAllocs int64
maxConstObjects int
enableFileImport bool
}
// New creates a Script instance with an input script.
func New(input []byte) *Script {
return &Script{
variables: make(map[string]*Variable),
input: input,
variables: make(map[string]*Variable),
input: input,
maxAllocs: -1,
maxConstObjects: -1,
}
}
@ -37,7 +40,7 @@ func (s *Script) Add(name string, value interface{}) error {
s.variables[name] = &Variable{
name: name,
value: &obj,
value: obj,
}
return nil
@ -55,38 +58,31 @@ func (s *Script) Remove(name string) bool {
return true
}
// SetBuiltinFunctions allows to define builtin functions.
func (s *Script) SetBuiltinFunctions(funcs []*objects.BuiltinFunction) {
if funcs != nil {
s.builtinFuncs = make([]objects.Object, len(funcs))
for idx, fn := range funcs {
s.builtinFuncs[idx] = fn
}
} else {
s.builtinFuncs = []objects.Object{}
}
// SetImports sets import modules.
func (s *Script) SetImports(modules *objects.ModuleMap) {
s.modules = modules
}
// SetBuiltinModules allows to define builtin modules.
func (s *Script) SetBuiltinModules(modules map[string]*objects.ImmutableMap) {
if modules != nil {
s.builtinModules = make(map[string]*objects.Object, len(modules))
for k, mod := range modules {
s.builtinModules[k] = objectPtr(mod)
}
} else {
s.builtinModules = map[string]*objects.Object{}
}
// SetMaxAllocs sets the maximum number of objects allocations during the run time.
// Compiled script will return runtime.ErrObjectAllocLimit error if it exceeds this limit.
func (s *Script) SetMaxAllocs(n int64) {
s.maxAllocs = n
}
// SetUserModuleLoader sets the user module loader for the compiler.
func (s *Script) SetUserModuleLoader(loader compiler.ModuleLoader) {
s.userModuleLoader = loader
// SetMaxConstObjects sets the maximum number of objects in the compiled constants.
func (s *Script) SetMaxConstObjects(n int) {
s.maxConstObjects = n
}
// EnableFileImport enables or disables module loading from local files.
// Local file modules are disabled by default.
func (s *Script) EnableFileImport(enable bool) {
s.enableFileImport = enable
}
// Compile compiles the script with all the defined variables, and, returns Compiled object.
func (s *Script) Compile() (*Compiled, error) {
symbolTable, builtinModules, globals, err := s.prepCompile()
symbolTable, globals, err := s.prepCompile()
if err != nil {
return nil, err
}
@ -100,19 +96,41 @@ func (s *Script) Compile() (*Compiled, error) {
return nil, err
}
c := compiler.NewCompiler(srcFile, symbolTable, nil, builtinModules, nil)
if s.userModuleLoader != nil {
c.SetModuleLoader(s.userModuleLoader)
}
c := compiler.NewCompiler(srcFile, symbolTable, nil, s.modules, nil)
c.EnableFileImport(s.enableFileImport)
if err := c.Compile(file); err != nil {
return nil, err
}
// reduce globals size
globals = globals[:symbolTable.MaxSymbols()+1]
// global symbol names to indexes
globalIndexes := make(map[string]int, len(globals))
for _, name := range symbolTable.Names() {
symbol, _, _ := symbolTable.Resolve(name)
if symbol.Scope == compiler.ScopeGlobal {
globalIndexes[name] = symbol.Index
}
}
// remove duplicates from constants
bytecode := c.Bytecode()
bytecode.RemoveDuplicates()
// check the constant objects limit
if s.maxConstObjects >= 0 {
cnt := bytecode.CountObjects()
if cnt > s.maxConstObjects {
return nil, fmt.Errorf("exceeding constant objects limit: %d", cnt)
}
}
return &Compiled{
symbolTable: symbolTable,
machine: runtime.NewVM(c.Bytecode(), globals, s.builtinFuncs, s.builtinModules),
globalIndexes: globalIndexes,
bytecode: bytecode,
globals: globals,
maxAllocs: s.maxAllocs,
}, nil
}
@ -141,39 +159,18 @@ func (s *Script) RunContext(ctx context.Context) (compiled *Compiled, err error)
return
}
func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, builtinModules map[string]bool, globals []*objects.Object, err error) {
func (s *Script) prepCompile() (symbolTable *compiler.SymbolTable, globals []objects.Object, err error) {
var names []string
for name := range s.variables {
names = append(names, name)
}
symbolTable = compiler.NewSymbolTable()
if s.builtinFuncs == nil {
s.builtinFuncs = make([]objects.Object, len(objects.Builtins))
for idx, fn := range objects.Builtins {
s.builtinFuncs[idx] = &objects.BuiltinFunction{
Name: fn.Name,
Value: fn.Value,
}
}
for idx, fn := range objects.Builtins {
symbolTable.DefineBuiltin(idx, fn.Name)
}
if s.builtinModules == nil {
s.builtinModules = make(map[string]*objects.Object)
}
for idx, fn := range s.builtinFuncs {
f := fn.(*objects.BuiltinFunction)
symbolTable.DefineBuiltin(idx, f.Name)
}
builtinModules = make(map[string]bool)
for name := range s.builtinModules {
builtinModules[name] = true
}
globals = make([]*objects.Object, runtime.GlobalsSize, runtime.GlobalsSize)
globals = make([]objects.Object, runtime.GlobalsSize)
for idx, name := range names {
symbol := symbolTable.Define(name)
@ -195,7 +192,3 @@ func (s *Script) copyVariables() map[string]*Variable {
return vars
}
func objectPtr(o objects.Object) *objects.Object {
return &o
}

View File

@ -9,7 +9,7 @@ import (
// Variable is a user-defined variable for the script.
type Variable struct {
name string
value *objects.Object
value objects.Object
}
// NewVariable creates a Variable.
@ -21,7 +21,7 @@ func NewVariable(name string, value interface{}) (*Variable, error) {
return &Variable{
name: name,
value: &obj,
value: obj,
}, nil
}
@ -32,18 +32,18 @@ func (v *Variable) Name() string {
// Value returns an empty interface of the variable value.
func (v *Variable) Value() interface{} {
return objectToInterface(*v.value)
return objects.ToInterface(v.value)
}
// ValueType returns the name of the value type.
func (v *Variable) ValueType() string {
return (*v.value).TypeName()
return v.value.TypeName()
}
// Int returns int value of the variable value.
// It returns 0 if the value is not convertible to int.
func (v *Variable) Int() int {
c, _ := objects.ToInt(*v.value)
c, _ := objects.ToInt(v.value)
return c
}
@ -51,7 +51,7 @@ func (v *Variable) Int() int {
// Int64 returns int64 value of the variable value.
// It returns 0 if the value is not convertible to int64.
func (v *Variable) Int64() int64 {
c, _ := objects.ToInt64(*v.value)
c, _ := objects.ToInt64(v.value)
return c
}
@ -59,7 +59,7 @@ func (v *Variable) Int64() int64 {
// Float returns float64 value of the variable value.
// It returns 0.0 if the value is not convertible to float64.
func (v *Variable) Float() float64 {
c, _ := objects.ToFloat64(*v.value)
c, _ := objects.ToFloat64(v.value)
return c
}
@ -67,7 +67,7 @@ func (v *Variable) Float() float64 {
// Char returns rune value of the variable value.
// It returns 0 if the value is not convertible to rune.
func (v *Variable) Char() rune {
c, _ := objects.ToRune(*v.value)
c, _ := objects.ToRune(v.value)
return c
}
@ -75,7 +75,7 @@ func (v *Variable) Char() rune {
// Bool returns bool value of the variable value.
// It returns 0 if the value is not convertible to bool.
func (v *Variable) Bool() bool {
c, _ := objects.ToBool(*v.value)
c, _ := objects.ToBool(v.value)
return c
}
@ -83,11 +83,11 @@ func (v *Variable) Bool() bool {
// Array returns []interface value of the variable value.
// It returns 0 if the value is not convertible to []interface.
func (v *Variable) Array() []interface{} {
switch val := (*v.value).(type) {
switch val := v.value.(type) {
case *objects.Array:
var arr []interface{}
for _, e := range val.Value {
arr = append(arr, objectToInterface(e))
arr = append(arr, objects.ToInterface(e))
}
return arr
}
@ -98,11 +98,11 @@ func (v *Variable) Array() []interface{} {
// Map returns map[string]interface{} value of the variable value.
// It returns 0 if the value is not convertible to map[string]interface{}.
func (v *Variable) Map() map[string]interface{} {
switch val := (*v.value).(type) {
switch val := v.value.(type) {
case *objects.Map:
kv := make(map[string]interface{})
for mk, mv := range val.Value {
kv[mk] = objectToInterface(mv)
kv[mk] = objects.ToInterface(mv)
}
return kv
}
@ -113,7 +113,7 @@ func (v *Variable) Map() map[string]interface{} {
// String returns string value of the variable value.
// It returns 0 if the value is not convertible to string.
func (v *Variable) String() string {
c, _ := objects.ToString(*v.value)
c, _ := objects.ToString(v.value)
return c
}
@ -121,7 +121,7 @@ func (v *Variable) String() string {
// Bytes returns a byte slice of the variable value.
// It returns nil if the value is not convertible to byte slice.
func (v *Variable) Bytes() []byte {
c, _ := objects.ToByteSlice(*v.value)
c, _ := objects.ToByteSlice(v.value)
return c
}
@ -129,7 +129,7 @@ func (v *Variable) Bytes() []byte {
// Error returns an error if the underlying value is error object.
// If not, this returns nil.
func (v *Variable) Error() error {
err, ok := (*v.value).(*objects.Error)
err, ok := v.value.(*objects.Error)
if ok {
return errors.New(err.String())
}
@ -140,10 +140,10 @@ func (v *Variable) Error() error {
// Object returns an underlying Object of the variable value.
// Note that returned Object is a copy of an actual Object used in the script.
func (v *Variable) Object() objects.Object {
return *v.value
return v.value
}
// IsUndefined returns true if the underlying value is undefined.
func (v *Variable) IsUndefined() bool {
return *v.value == objects.UndefinedValue
return v.value == objects.UndefinedValue
}