add script.Compiled.Set function so compiled script can replace value of global variables

This commit is contained in:
Daniel Kang 2019-02-01 11:09:12 -08:00
parent 6420628310
commit 6a663c6360
3 changed files with 58 additions and 0 deletions

View file

@ -80,6 +80,15 @@ func (v *VM) Abort() {
// Run starts the execution.
func (v *VM) Run() error {
// reset VM states
v.sp = 0
v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex = 1
v.ip = -1
atomic.StoreInt64(&v.aborting, 0)
for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
v.ip++

View file

@ -2,6 +2,7 @@ package script
import (
"context"
"fmt"
"github.com/d5/tengo/compiler"
"github.com/d5/tengo/objects"
@ -92,3 +93,21 @@ func (c *Compiled) GetAll() []*Variable {
return vars
}
// 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 {
obj, err := objects.FromInterface(value)
if err != nil {
return err
}
symbol, _, ok := c.symbolTable.Resolve(name)
if !ok || symbol.Scope != compiler.ScopeGlobal {
return fmt.Errorf("'%s' is not defined", name)
}
c.machine.Globals()[symbol.Index] = &obj
return nil
}

View file

@ -47,6 +47,36 @@ func TestCompiled_IsDefined(t *testing.T) {
compiledIsDefined(t, c, "b", false)
}
func TestCompiled_Set(t *testing.T) {
c := compile(t, `a := b`, M{"b": "foo"})
compiledRun(t, c)
compiledGet(t, c, "a", "foo")
// replace value of 'b'
err := c.Set("b", "bar")
assert.NoError(t, err)
compiledRun(t, c)
compiledGet(t, c, "a", "bar")
// try to replace undefined variable
err = c.Set("c", 1984)
assert.Error(t, err) // 'c' is not defined
// case #2
c = compile(t, `
a := func() {
return func() {
return b + 5
}()
}()`, M{"b": 5})
compiledRun(t, c)
compiledGet(t, c, "a", int64(10))
err = c.Set("b", 10)
assert.NoError(t, err)
compiledRun(t, c)
compiledGet(t, c, "a", int64(15))
}
func TestCompiled_RunContext(t *testing.T) {
// machine completes normally
c := compile(t, `a := 5`, nil)