summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/runtime/vm.go
diff options
context:
space:
mode:
authorWim <wim@42.be>2019-04-06 22:18:25 +0200
committerGitHub <noreply@github.com>2019-04-06 22:18:25 +0200
commit115d20373c21b107a428a55247c64f900e116038 (patch)
treec7299b3d3be2a48c0f2d5bfbd856cbd1b27d2e55 /vendor/github.com/d5/tengo/runtime/vm.go
parentcdf33e5748c110e12097130bdb44637e3d14b229 (diff)
downloadmatterbridge-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/runtime/vm.go')
-rw-r--r--vendor/github.com/d5/tengo/runtime/vm.go758
1 files changed, 333 insertions, 425 deletions
diff --git a/vendor/github.com/d5/tengo/runtime/vm.go b/vendor/github.com/d5/tengo/runtime/vm.go
index 9066bfea..dde52db5 100644
--- a/vendor/github.com/d5/tengo/runtime/vm.go
+++ b/vendor/github.com/d5/tengo/runtime/vm.go
@@ -21,73 +21,46 @@ const (
MaxFrames = 1024
)
-var (
- truePtr = &objects.TrueValue
- falsePtr = &objects.FalseValue
- undefinedPtr = &objects.UndefinedValue
-)
-
// VM is a virtual machine that executes the bytecode compiled by Compiler.
type VM struct {
- constants []objects.Object
- stack []*objects.Object
- sp int
- globals []*objects.Object
- fileSet *source.FileSet
- frames []Frame
- framesIndex int
- curFrame *Frame
- curInsts []byte
- curIPLimit int
- ip int
- aborting int64
- builtinFuncs []objects.Object
- builtinModules map[string]*objects.Object
- err error
- errOffset int
+ constants []objects.Object
+ stack [StackSize]objects.Object
+ sp int
+ globals []objects.Object
+ fileSet *source.FileSet
+ frames [MaxFrames]Frame
+ framesIndex int
+ curFrame *Frame
+ curInsts []byte
+ ip int
+ aborting int64
+ maxAllocs int64
+ allocs int64
+ err error
}
// NewVM creates a VM.
-func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object, builtinFuncs []objects.Object, builtinModules map[string]*objects.Object) *VM {
+func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int64) *VM {
if globals == nil {
- globals = make([]*objects.Object, GlobalsSize)
+ globals = make([]objects.Object, GlobalsSize)
}
- if builtinModules == nil {
- builtinModules = make(map[string]*objects.Object)
+ v := &VM{
+ constants: bytecode.Constants,
+ sp: 0,
+ globals: globals,
+ fileSet: bytecode.FileSet,
+ framesIndex: 1,
+ ip: -1,
+ maxAllocs: maxAllocs,
}
- if builtinFuncs == nil {
- builtinFuncs = make([]objects.Object, len(objects.Builtins))
- for idx, fn := range objects.Builtins {
- builtinFuncs[idx] = &objects.BuiltinFunction{
- Name: fn.Name,
- Value: fn.Value,
- }
- }
- }
+ v.frames[0].fn = bytecode.MainFunction
+ v.frames[0].ip = -1
+ v.curFrame = &v.frames[0]
+ v.curInsts = v.curFrame.fn.Instructions
- frames := make([]Frame, MaxFrames)
- frames[0].fn = bytecode.MainFunction
- frames[0].freeVars = nil
- frames[0].ip = -1
- frames[0].basePointer = 0
-
- return &VM{
- constants: bytecode.Constants,
- stack: make([]*objects.Object, StackSize),
- sp: 0,
- globals: globals,
- fileSet: bytecode.FileSet,
- frames: frames,
- framesIndex: 1,
- curFrame: &(frames[0]),
- curInsts: frames[0].fn.Instructions,
- curIPLimit: len(frames[0].fn.Instructions) - 1,
- ip: -1,
- builtinFuncs: builtinFuncs,
- builtinModules: builtinModules,
- }
+ return v
}
// Abort aborts the execution.
@@ -101,109 +74,102 @@ func (v *VM) Run() (err error) {
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)
+ v.allocs = v.maxAllocs + 1
v.run()
+ atomic.StoreInt64(&v.aborting, 0)
+
err = v.err
if err != nil {
- filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-v.errOffset])
+ filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1))
err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos)
for v.framesIndex > 1 {
v.framesIndex--
v.curFrame = &v.frames[v.framesIndex-1]
- filePos = v.fileSet.Position(v.curFrame.fn.SourceMap[v.curFrame.ip-1])
+ filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1))
err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
}
return err
}
- // check if stack still has some objects left
- if v.sp > 0 && atomic.LoadInt64(&v.aborting) == 0 {
- panic(fmt.Errorf("non empty stack after execution: %d", v.sp))
- }
-
return nil
}
func (v *VM) run() {
-mainloop:
- for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
+ defer func() {
+ if r := recover(); r != nil {
+ if v.sp >= StackSize || v.framesIndex >= MaxFrames {
+ v.err = ErrStackOverflow
+ return
+ }
+
+ if v.ip < len(v.curInsts)-1 {
+ if err, ok := r.(error); ok {
+ v.err = err
+ } else {
+ v.err = fmt.Errorf("panic: %v", r)
+ }
+ }
+ }
+ }()
+
+ for atomic.LoadInt64(&v.aborting) == 0 {
v.ip++
switch v.curInsts[v.ip] {
case compiler.OpConstant:
- cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &v.constants[cidx]
+ v.stack[v.sp] = v.constants[cidx]
v.sp++
case compiler.OpNull:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = undefinedPtr
+ v.stack[v.sp] = objects.UndefinedValue
v.sp++
- case compiler.OpAdd:
- v.binaryOp(token.Add)
-
- case compiler.OpSub:
- v.binaryOp(token.Sub)
-
- case compiler.OpMul:
- v.binaryOp(token.Mul)
-
- case compiler.OpDiv:
- v.binaryOp(token.Quo)
-
- case compiler.OpRem:
- v.binaryOp(token.Rem)
-
- case compiler.OpBAnd:
- v.binaryOp(token.And)
+ case compiler.OpBinaryOp:
+ v.ip++
+ right := v.stack[v.sp-1]
+ left := v.stack[v.sp-2]
- case compiler.OpBOr:
- v.binaryOp(token.Or)
+ tok := token.Token(v.curInsts[v.ip])
+ res, e := left.BinaryOp(tok, right)
+ if e != nil {
+ v.sp -= 2
- case compiler.OpBXor:
- v.binaryOp(token.Xor)
+ if e == objects.ErrInvalidOperator {
+ v.err = fmt.Errorf("invalid operation: %s %s %s",
+ left.TypeName(), tok.String(), right.TypeName())
+ return
+ }
- case compiler.OpBAndNot:
- v.binaryOp(token.AndNot)
+ v.err = e
+ return
+ }
- case compiler.OpBShiftLeft:
- v.binaryOp(token.Shl)
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
- case compiler.OpBShiftRight:
- v.binaryOp(token.Shr)
+ v.stack[v.sp-2] = res
+ v.sp--
case compiler.OpEqual:
right := v.stack[v.sp-1]
left := v.stack[v.sp-2]
v.sp -= 2
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*left).Equals(*right) {
- v.stack[v.sp] = truePtr
+ if left.Equals(right) {
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -212,58 +178,32 @@ mainloop:
left := v.stack[v.sp-2]
v.sp -= 2
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*left).Equals(*right) {
- v.stack[v.sp] = falsePtr
+ if left.Equals(right) {
+ v.stack[v.sp] = objects.FalseValue
} else {
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
}
v.sp++
- case compiler.OpGreaterThan:
- v.binaryOp(token.Greater)
-
- case compiler.OpGreaterThanEqual:
- v.binaryOp(token.GreaterEq)
-
case compiler.OpPop:
v.sp--
case compiler.OpTrue:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
v.sp++
case compiler.OpFalse:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
v.sp++
case compiler.OpLNot:
operand := v.stack[v.sp-1]
v.sp--
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- if (*operand).IsFalsy() {
- v.stack[v.sp] = truePtr
+ if operand.IsFalsy() {
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -271,19 +211,20 @@ mainloop:
operand := v.stack[v.sp-1]
v.sp--
- switch x := (*operand).(type) {
+ switch x := operand.(type) {
case *objects.Int:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Int{Value: ^x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Int{Value: ^x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
default:
- v.err = fmt.Errorf("invalid operation: ^%s", (*operand).TypeName())
+ v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName())
return
}
@@ -291,63 +232,60 @@ mainloop:
operand := v.stack[v.sp-1]
v.sp--
- switch x := (*operand).(type) {
+ switch x := operand.(type) {
case *objects.Int:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Int{Value: -x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Int{Value: -x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
case *objects.Float:
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var res objects.Object = &objects.Float{Value: -x.Value}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var res objects.Object = &objects.Float{Value: -x.Value}
-
- v.stack[v.sp] = &res
+ v.stack[v.sp] = res
v.sp++
default:
- v.err = fmt.Errorf("invalid operation: -%s", (*operand).TypeName())
+ v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName())
return
}
case compiler.OpJumpFalsy:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
-
- condition := v.stack[v.sp-1]
v.sp--
-
- if (*condition).IsFalsy() {
+ if v.stack[v.sp].IsFalsy() {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1
}
case compiler.OpAndJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
- condition := *v.stack[v.sp-1]
- if condition.IsFalsy() {
+ if v.stack[v.sp-1].IsFalsy() {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.ip = pos - 1
} else {
v.sp--
}
case compiler.OpOrJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
- condition := *v.stack[v.sp-1]
- if !condition.IsFalsy() {
- v.ip = pos - 1
- } else {
+ if v.stack[v.sp-1].IsFalsy() {
v.sp--
+ } else {
+ pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
+ v.ip = pos - 1
}
case compiler.OpJump:
@@ -355,108 +293,127 @@ mainloop:
v.ip = pos - 1
case compiler.OpSetGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
-
v.sp--
+ globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
v.globals[globalIndex] = v.stack[v.sp]
case compiler.OpSetSelGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- numSelectors := int(v.curInsts[v.ip+3])
v.ip += 3
+ globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
+ numSelectors := int(v.curInsts[v.ip])
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
+
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil {
- v.errOffset = 3
v.err = e
return
}
case compiler.OpGetGlobal:
- globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
val := v.globals[globalIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
v.stack[v.sp] = val
v.sp++
case compiler.OpArray:
- numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
var elements []objects.Object
for i := v.sp - numElements; i < v.sp; i++ {
- elements = append(elements, *v.stack[i])
+ elements = append(elements, v.stack[i])
}
v.sp -= numElements
var arr objects.Object = &objects.Array{Value: elements}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &arr
+ v.stack[v.sp] = arr
v.sp++
case compiler.OpMap:
- numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
v.ip += 2
+ numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
kv := make(map[string]objects.Object)
for i := v.sp - numElements; i < v.sp; i += 2 {
- key := *v.stack[i]
- value := *v.stack[i+1]
+ key := v.stack[i]
+ value := v.stack[i+1]
kv[key.(*objects.String).Value] = value
}
v.sp -= numElements
var m objects.Object = &objects.Map{Value: kv}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &m
+ v.stack[v.sp] = m
v.sp++
case compiler.OpError:
value := v.stack[v.sp-1]
var e objects.Object = &objects.Error{
- Value: *value,
+ Value: value,
}
- v.stack[v.sp-1] = &e
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = e
case compiler.OpImmutable:
value := v.stack[v.sp-1]
- switch value := (*value).(type) {
+ switch value := value.(type) {
case *objects.Array:
var immutableArray objects.Object = &objects.ImmutableArray{
Value: value.Value,
}
- v.stack[v.sp-1] = &immutableArray
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = immutableArray
case *objects.Map:
var immutableMap objects.Object = &objects.ImmutableMap{
Value: value.Value,
}
- v.stack[v.sp-1] = &immutableMap
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp-1] = immutableMap
}
case compiler.OpIndex:
@@ -464,13 +421,13 @@ mainloop:
left := v.stack[v.sp-2]
v.sp -= 2
- switch left := (*left).(type) {
+ switch left := left.(type) {
case objects.Indexable:
- val, e := left.IndexGet(*index)
+ val, e := left.IndexGet(index)
if e != nil {
if e == objects.ErrInvalidIndexType {
- v.err = fmt.Errorf("invalid index type: %s", (*index).TypeName())
+ v.err = fmt.Errorf("invalid index type: %s", index.TypeName())
return
}
@@ -481,27 +438,17 @@ mainloop:
val = objects.UndefinedValue
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.Error: // e.value
- key, ok := (*index).(*objects.String)
+ key, ok := index.(*objects.String)
if !ok || key.Value != "value" {
v.err = fmt.Errorf("invalid index on error")
return
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &left.Value
+ v.stack[v.sp] = left.Value
v.sp++
default:
@@ -516,8 +463,8 @@ mainloop:
v.sp -= 3
var lowIdx int64
- if *low != objects.UndefinedValue {
- if low, ok := (*low).(*objects.Int); ok {
+ if low != objects.UndefinedValue {
+ if low, ok := low.(*objects.Int); ok {
lowIdx = low.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", low.TypeName())
@@ -525,13 +472,13 @@ mainloop:
}
}
- switch left := (*left).(type) {
+ switch left := left.(type) {
case *objects.Array:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -555,21 +502,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.ImmutableArray:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -593,22 +542,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.String:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -632,22 +582,23 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case *objects.Bytes:
numElements := int64(len(left.Value))
var highIdx int64
- if *high == objects.UndefinedValue {
+ if high == objects.UndefinedValue {
highIdx = numElements
- } else if high, ok := (*high).(*objects.Int); ok {
+ } else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
@@ -671,14 +622,15 @@ mainloop:
highIdx = numElements
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
+
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
}
@@ -686,12 +638,11 @@ mainloop:
numArgs := int(v.curInsts[v.ip+1])
v.ip++
- value := *v.stack[v.sp-1-numArgs]
+ value := v.stack[v.sp-1-numArgs]
switch callee := value.(type) {
case *objects.Closure:
if numArgs != callee.Fn.NumParameters {
- v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.Fn.NumParameters, numArgs)
return
@@ -700,14 +651,14 @@ mainloop:
// test if it's tail-call
if callee.Fn == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
- if nextOp == compiler.OpReturnValue ||
+ if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
}
v.sp -= numArgs + 1
v.ip = -1 // reset IP to beginning of the frame
- continue mainloop
+ continue
}
}
@@ -719,13 +670,11 @@ mainloop:
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Fn.Instructions
v.ip = -1
- v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++
v.sp = v.sp - numArgs + callee.Fn.NumLocals
case *objects.CompiledFunction:
if numArgs != callee.NumParameters {
- v.errOffset = 1
v.err = fmt.Errorf("wrong number of arguments: want=%d, got=%d",
callee.NumParameters, numArgs)
return
@@ -734,14 +683,14 @@ mainloop:
// test if it's tail-call
if callee == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
- if nextOp == compiler.OpReturnValue ||
+ if nextOp == compiler.OpReturn ||
(nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
}
v.sp -= numArgs + 1
v.ip = -1 // reset IP to beginning of the frame
- continue mainloop
+ continue
}
}
@@ -753,14 +702,13 @@ mainloop:
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = callee.Instructions
v.ip = -1
- v.curIPLimit = len(v.curInsts) - 1
v.framesIndex++
v.sp = v.sp - numArgs + callee.NumLocals
case objects.Callable:
var args []objects.Object
for _, arg := range v.stack[v.sp-numArgs : v.sp] {
- args = append(args, *arg)
+ args = append(args, arg)
}
ret, e := callee.Call(args...)
@@ -768,8 +716,6 @@ mainloop:
// runtime error
if e != nil {
- v.errOffset = 1
-
if e == objects.ErrWrongNumArguments {
v.err = fmt.Errorf("wrong number of arguments in call to '%s'",
value.TypeName())
@@ -791,65 +737,54 @@ mainloop:
ret = objects.UndefinedValue
}
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &ret
+ v.stack[v.sp] = ret
v.sp++
default:
- v.errOffset = 1
v.err = fmt.Errorf("not callable: %s", callee.TypeName())
return
}
- case compiler.OpReturnValue:
- retVal := v.stack[v.sp-1]
+ case compiler.OpReturn:
+ v.ip++
+ var retVal objects.Object
+ if int(v.curInsts[v.ip]) == 1 {
+ retVal = v.stack[v.sp-1]
+ } else {
+ retVal = objects.UndefinedValue
+ }
//v.sp--
v.framesIndex--
- lastFrame := v.frames[v.framesIndex]
v.curFrame = &v.frames[v.framesIndex-1]
v.curInsts = v.curFrame.fn.Instructions
- v.curIPLimit = len(v.curInsts) - 1
v.ip = v.curFrame.ip
//v.sp = lastFrame.basePointer - 1
- v.sp = lastFrame.basePointer
+ v.sp = v.frames[v.framesIndex].basePointer
// skip stack overflow check because (newSP) <= (oldSP)
v.stack[v.sp-1] = retVal
//v.sp++
- case compiler.OpReturn:
- v.framesIndex--
- lastFrame := v.frames[v.framesIndex]
- v.curFrame = &v.frames[v.framesIndex-1]
- v.curInsts = v.curFrame.fn.Instructions
- v.curIPLimit = len(v.curInsts) - 1
- v.ip = v.curFrame.ip
-
- //v.sp = lastFrame.basePointer - 1
- v.sp = lastFrame.basePointer
-
- // skip stack overflow check because (newSP) <= (oldSP)
- v.stack[v.sp-1] = undefinedPtr
- //v.sp++
-
case compiler.OpDefineLocal:
- localIndex := int(v.curInsts[v.ip+1])
v.ip++
+ localIndex := int(v.curInsts[v.ip])
sp := v.curFrame.basePointer + localIndex
// local variables can be mutated by other actions
// so always store the copy of popped value
- val := *v.stack[v.sp-1]
+ val := v.stack[v.sp-1]
v.sp--
- v.stack[sp] = &val
+ v.stack[sp] = val
case compiler.OpSetLocal:
localIndex := int(v.curInsts[v.ip+1])
@@ -862,7 +797,11 @@ mainloop:
val := v.stack[v.sp-1]
v.sp--
- *v.stack[sp] = *val // also use a copy of popped value
+ if obj, ok := v.stack[sp].(*objects.ObjectPtr); ok {
+ *obj.Value = val
+ val = obj
+ }
+ v.stack[sp] = val // also use a copy of popped value
case compiler.OpSetSelLocal:
localIndex := int(v.curInsts[v.ip+1])
@@ -870,172 +809,173 @@ mainloop:
v.ip += 2
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
+
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
sp := v.curFrame.basePointer + localIndex
if e := indexAssign(v.stack[sp], val, selectors); e != nil {
- v.errOffset = 2
v.err = e
return
}
case compiler.OpGetLocal:
- localIndex := int(v.curInsts[v.ip+1])
v.ip++
+ localIndex := int(v.curInsts[v.ip])
val := v.stack[v.curFrame.basePointer+localIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
+ if obj, ok := val.(*objects.ObjectPtr); ok {
+ val = *obj.Value
}
v.stack[v.sp] = val
v.sp++
case compiler.OpGetBuiltin:
- builtinIndex := int(v.curInsts[v.ip+1])
v.ip++
+ builtinIndex := int(v.curInsts[v.ip])
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &v.builtinFuncs[builtinIndex]
- v.sp++
-
- case compiler.OpGetBuiltinModule:
- val := v.stack[v.sp-1]
- v.sp--
-
- moduleName := (*val).(*objects.String).Value
-
- module, ok := v.builtinModules[moduleName]
- if !ok {
- v.errOffset = 3
- v.err = fmt.Errorf("module '%s' not found", moduleName)
- return
- }
-
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = module
+ v.stack[v.sp] = objects.Builtins[builtinIndex]
v.sp++
case compiler.OpClosure:
- constIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- numFree := int(v.curInsts[v.ip+3])
v.ip += 3
+ constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
+ numFree := int(v.curInsts[v.ip])
fn, ok := v.constants[constIndex].(*objects.CompiledFunction)
if !ok {
- v.errOffset = 3
v.err = fmt.Errorf("not function: %s", fn.TypeName())
return
}
- free := make([]*objects.Object, numFree)
+ free := make([]*objects.ObjectPtr, numFree)
for i := 0; i < numFree; i++ {
- free[i] = v.stack[v.sp-numFree+i]
+ switch freeVar := (v.stack[v.sp-numFree+i]).(type) {
+ case *objects.ObjectPtr:
+ free[i] = freeVar
+ default:
+ free[i] = &objects.ObjectPtr{Value: &v.stack[v.sp-numFree+i]}
+ }
}
- v.sp -= numFree
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ v.sp -= numFree
- var cl objects.Object = &objects.Closure{
+ var cl = &objects.Closure{
Fn: fn,
Free: free,
}
- v.stack[v.sp] = &cl
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
+ return
+ }
+
+ v.stack[v.sp] = cl
v.sp++
- case compiler.OpGetFree:
- freeIndex := int(v.curInsts[v.ip+1])
+ case compiler.OpGetFreePtr:
v.ip++
+ freeIndex := int(v.curInsts[v.ip])
val := v.curFrame.freeVars[freeIndex]
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ v.stack[v.sp] = val
+ v.sp++
+
+ case compiler.OpGetFree:
+ v.ip++
+ freeIndex := int(v.curInsts[v.ip])
+
+ val := *v.curFrame.freeVars[freeIndex].Value
v.stack[v.sp] = val
v.sp++
+ case compiler.OpSetFree:
+ v.ip++
+ freeIndex := int(v.curInsts[v.ip])
+
+ *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
+
+ v.sp--
+
+ case compiler.OpGetLocalPtr:
+ v.ip++
+ localIndex := int(v.curInsts[v.ip])
+
+ sp := v.curFrame.basePointer + localIndex
+ val := v.stack[sp]
+
+ var freeVar *objects.ObjectPtr
+ if obj, ok := val.(*objects.ObjectPtr); ok {
+ freeVar = obj
+ } else {
+ freeVar = &objects.ObjectPtr{Value: &val}
+ v.stack[sp] = freeVar
+ }
+
+ v.stack[v.sp] = freeVar
+ v.sp++
+
case compiler.OpSetSelFree:
- freeIndex := int(v.curInsts[v.ip+1])
- numSelectors := int(v.curInsts[v.ip+2])
v.ip += 2
+ freeIndex := int(v.curInsts[v.ip-1])
+ numSelectors := int(v.curInsts[v.ip])
// selectors and RHS value
- selectors := v.stack[v.sp-numSelectors : v.sp]
+ selectors := make([]objects.Object, numSelectors)
+ for i := 0; i < numSelectors; i++ {
+ selectors[i] = v.stack[v.sp-numSelectors+i]
+ }
val := v.stack[v.sp-numSelectors-1]
v.sp -= numSelectors + 1
- if e := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); e != nil {
- v.errOffset = 2
+ if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil {
v.err = e
return
}
- case compiler.OpSetFree:
- freeIndex := int(v.curInsts[v.ip+1])
- v.ip++
-
- val := v.stack[v.sp-1]
- v.sp--
-
- *v.curFrame.freeVars[freeIndex] = *val
-
case compiler.OpIteratorInit:
var iterator objects.Object
dst := v.stack[v.sp-1]
v.sp--
- iterable, ok := (*dst).(objects.Iterable)
+ iterable, ok := dst.(objects.Iterable)
if !ok {
- v.err = fmt.Errorf("not iterable: %s", (*dst).TypeName())
+ v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
return
}
iterator = iterable.Iterate()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
+ v.allocs--
+ if v.allocs == 0 {
+ v.err = ErrObjectAllocLimit
return
}
- v.stack[v.sp] = &iterator
+ v.stack[v.sp] = iterator
v.sp++
case compiler.OpIteratorNext:
iterator := v.stack[v.sp-1]
v.sp--
- hasMore := (*iterator).(objects.Iterator).Next()
-
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
+ hasMore := iterator.(objects.Iterator).Next()
if hasMore {
- v.stack[v.sp] = truePtr
+ v.stack[v.sp] = objects.TrueValue
} else {
- v.stack[v.sp] = falsePtr
+ v.stack[v.sp] = objects.FalseValue
}
v.sp++
@@ -1043,70 +983,61 @@ mainloop:
iterator := v.stack[v.sp-1]
v.sp--
- val := (*iterator).(objects.Iterator).Key()
+ val := iterator.(objects.Iterator).Key()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
case compiler.OpIteratorValue:
iterator := v.stack[v.sp-1]
v.sp--
- val := (*iterator).(objects.Iterator).Value()
+ val := iterator.(objects.Iterator).Value()
- if v.sp >= StackSize {
- v.err = ErrStackOverflow
- return
- }
-
- v.stack[v.sp] = &val
+ v.stack[v.sp] = val
v.sp++
default:
- panic(fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]))
+ v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])
+ return
}
}
}
-// Globals returns the global variables.
-func (v *VM) Globals() []*objects.Object {
- return v.globals
+// IsStackEmpty tests if the stack is empty or not.
+func (v *VM) IsStackEmpty() bool {
+ return v.sp == 0
}
-func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
+func indexAssign(dst, src objects.Object, selectors []objects.Object) error {
numSel := len(selectors)
for sidx := numSel - 1; sidx > 0; sidx-- {
- indexable, ok := (*dst).(objects.Indexable)
+ indexable, ok := dst.(objects.Indexable)
if !ok {
- return fmt.Errorf("not indexable: %s", (*dst).TypeName())
+ return fmt.Errorf("not indexable: %s", dst.TypeName())
}
- next, err := indexable.IndexGet(*selectors[sidx])
+ next, err := indexable.IndexGet(selectors[sidx])
if err != nil {
if err == objects.ErrInvalidIndexType {
- return fmt.Errorf("invalid index type: %s", (*selectors[sidx]).TypeName())
+ return fmt.Errorf("invalid index type: %s", selectors[sidx].TypeName())
}
return err
}
- dst = &next
+ dst = next
}
- indexAssignable, ok := (*dst).(objects.IndexAssignable)
+ indexAssignable, ok := dst.(objects.IndexAssignable)
if !ok {
- return fmt.Errorf("not index-assignable: %s", (*dst).TypeName())
+ return fmt.Errorf("not index-assignable: %s", dst.TypeName())
}
- if err := indexAssignable.IndexSet(*selectors[0], *src); err != nil {
+ if err := indexAssignable.IndexSet(selectors[0], src); err != nil {
if err == objects.ErrInvalidIndexValueType {
- return fmt.Errorf("invaid index value type: %s", (*src).TypeName())
+ return fmt.Errorf("invaid index value type: %s", src.TypeName())
}
return err
@@ -1114,26 +1045,3 @@ func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
return nil
}
-
-func (v *VM) binaryOp(tok token.Token) {
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
-
- res, e := (*left).BinaryOp(tok, *right)
- if e != nil {
- v.sp -= 2
- atomic.StoreInt64(&v.aborting, 1)
-
- if e == objects.ErrInvalidOperator {
- v.err = fmt.Errorf("invalid operation: %s + %s",
- (*left).TypeName(), (*right).TypeName())
- return
- }
-
- v.err = e
- return
- }
-
- v.stack[v.sp-2] = &res
- v.sp--
-}