summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/script
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/script')
-rw-r--r--vendor/github.com/d5/tengo/script/compiled.go102
-rw-r--r--vendor/github.com/d5/tengo/script/conversion.go33
-rw-r--r--vendor/github.com/d5/tengo/script/script.go125
-rw-r--r--vendor/github.com/d5/tengo/script/variable.go36
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
}