diff options
author | Wim <wim@42.be> | 2019-04-06 22:18:25 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-06 22:18:25 +0200 |
commit | 115d20373c21b107a428a55247c64f900e116038 (patch) | |
tree | c7299b3d3be2a48c0f2d5bfbd856cbd1b27d2e55 /vendor/github.com/d5/tengo/script | |
parent | cdf33e5748c110e12097130bdb44637e3d14b229 (diff) | |
download | matterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.tar.gz matterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.tar.bz2 matterbridge-msglm-115d20373c21b107a428a55247c64f900e116038.zip |
Update tengo vendor and load the stdlib. Fixes #789 (#792)
Diffstat (limited to 'vendor/github.com/d5/tengo/script')
-rw-r--r-- | vendor/github.com/d5/tengo/script/compiled.go | 102 | ||||
-rw-r--r-- | vendor/github.com/d5/tengo/script/conversion.go | 33 | ||||
-rw-r--r-- | vendor/github.com/d5/tengo/script/script.go | 125 | ||||
-rw-r--r-- | vendor/github.com/d5/tengo/script/variable.go | 36 |
4 files changed, 151 insertions, 145 deletions
diff --git a/vendor/github.com/d5/tengo/script/compiled.go b/vendor/github.com/d5/tengo/script/compiled.go index 4acc46ee..ce50f498 100644 --- a/vendor/github.com/d5/tengo/script/compiled.go +++ b/vendor/github.com/d5/tengo/script/compiled.go @@ -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() + + value := objects.UndefinedValue - symbol, _, ok := c.symbolTable.Resolve(name) - if ok && symbol.Scope == compiler.ScopeGlobal { - value = c.machine.Globals()[symbol.Index] + 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 { + c.lock.RLock() + defer c.lock.RUnlock() + 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 - } - - vars = append(vars, &Variable{ - name: name, - value: value, - }) + + 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 } diff --git a/vendor/github.com/d5/tengo/script/conversion.go b/vendor/github.com/d5/tengo/script/conversion.go deleted file mode 100644 index c35c1411..00000000 --- a/vendor/github.com/d5/tengo/script/conversion.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/d5/tengo/script/script.go b/vendor/github.com/d5/tengo/script/script.go index e8db52a3..cdf36713 100644 --- a/vendor/github.com/d5/tengo/script/script.go +++ b/vendor/github.com/d5/tengo/script/script.go @@ -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) + 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] - if s.userModuleLoader != nil { - c.SetModuleLoader(s.userModuleLoader) + // 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 + } } - if err := c.Compile(file); err != nil { - return nil, err + // 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, - } - } - } - - if s.builtinModules == nil { - s.builtinModules = make(map[string]*objects.Object) + for idx, fn := range objects.Builtins { + symbolTable.DefineBuiltin(idx, fn.Name) } - 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 -} diff --git a/vendor/github.com/d5/tengo/script/variable.go b/vendor/github.com/d5/tengo/script/variable.go index c5e01bd9..df345115 100644 --- a/vendor/github.com/d5/tengo/script/variable.go +++ b/vendor/github.com/d5/tengo/script/variable.go @@ -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 } |