2019-01-11 13:27:28 +03:00
|
|
|
package runtime
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
import (
|
2019-01-16 23:23:20 +03:00
|
|
|
"errors"
|
2019-01-09 10:17:42 +03:00
|
|
|
"fmt"
|
2019-01-20 17:33:22 +03:00
|
|
|
"sync/atomic"
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
"github.com/d5/tengo/compiler"
|
2019-01-11 13:27:28 +03:00
|
|
|
"github.com/d5/tengo/compiler/token"
|
2019-01-09 10:17:42 +03:00
|
|
|
"github.com/d5/tengo/objects"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-01-15 09:24:33 +03:00
|
|
|
// StackSize is the maximum stack size.
|
|
|
|
StackSize = 2048
|
|
|
|
|
|
|
|
// GlobalsSize is the maximum number of global variables.
|
2019-01-10 01:10:47 +03:00
|
|
|
GlobalsSize = 1024
|
2019-01-15 09:24:33 +03:00
|
|
|
|
|
|
|
// MaxFrames is the maximum number of function frames.
|
|
|
|
MaxFrames = 1024
|
2019-01-09 10:17:42 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-01-14 01:24:39 +03:00
|
|
|
truePtr = &objects.TrueValue
|
|
|
|
falsePtr = &objects.FalseValue
|
|
|
|
undefinedPtr = &objects.UndefinedValue
|
2019-01-09 10:17:42 +03:00
|
|
|
builtinFuncs []objects.Object
|
|
|
|
)
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// VM is a virtual machine that executes the bytecode compiled by Compiler.
|
2019-01-09 10:17:42 +03:00
|
|
|
type VM struct {
|
|
|
|
constants []objects.Object
|
|
|
|
stack []*objects.Object
|
|
|
|
sp int
|
|
|
|
globals []*objects.Object
|
|
|
|
frames []Frame
|
|
|
|
framesIndex int
|
2019-01-14 02:21:19 +03:00
|
|
|
curFrame *Frame
|
|
|
|
curInsts []byte
|
|
|
|
curIPLimit int
|
2019-01-20 18:41:57 +03:00
|
|
|
ip int
|
2019-01-20 17:33:22 +03:00
|
|
|
aborting int64
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// NewVM creates a VM.
|
2019-01-09 10:17:42 +03:00
|
|
|
func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
|
|
|
|
if globals == nil {
|
|
|
|
globals = make([]*objects.Object, GlobalsSize)
|
2019-01-09 23:39:40 +03:00
|
|
|
} else if len(globals) < GlobalsSize {
|
|
|
|
g := make([]*objects.Object, GlobalsSize)
|
|
|
|
copy(g, globals)
|
|
|
|
globals = g
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
frames := make([]Frame, MaxFrames)
|
|
|
|
frames[0].fn = &objects.CompiledFunction{Instructions: bytecode.Instructions}
|
|
|
|
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,
|
|
|
|
frames: frames,
|
|
|
|
framesIndex: 1,
|
2019-01-14 02:21:19 +03:00
|
|
|
curFrame: &(frames[0]),
|
|
|
|
curInsts: frames[0].fn.Instructions,
|
|
|
|
curIPLimit: len(frames[0].fn.Instructions) - 1,
|
2019-01-20 18:41:57 +03:00
|
|
|
ip: -1,
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// Abort aborts the execution.
|
2019-01-09 10:17:42 +03:00
|
|
|
func (v *VM) Abort() {
|
2019-01-20 17:33:22 +03:00
|
|
|
atomic.StoreInt64(&v.aborting, 1)
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// Run starts the execution.
|
2019-01-09 10:17:42 +03:00
|
|
|
func (v *VM) Run() error {
|
2019-01-20 18:41:57 +03:00
|
|
|
for v.ip < v.curIPLimit && (atomic.LoadInt64(&v.aborting) == 0) {
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
switch compiler.Opcode(v.curInsts[v.ip]) {
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpConstant:
|
2019-01-20 18:41:57 +03:00
|
|
|
cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-11 09:34:28 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = &v.constants[cidx]
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpNull:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = undefinedPtr
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpAdd:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Add, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSub:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Sub, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpMul:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Mul, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpDiv:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Quo, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpRem:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Rem, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBAnd:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.And, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBOr:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Or, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBXor:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Xor, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBAndNot:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.AndNot, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBShiftLeft:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Shl, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBShiftRight:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Shr, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpEqual:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
if (*right).Equals(*left) {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = truePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = falsePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpNotEqual:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
if (*right).Equals(*left) {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = falsePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = truePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGreaterThan:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.Greater, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGreaterThanEqual:
|
2019-01-14 15:32:09 +03:00
|
|
|
right := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
res, err := (*left).BinaryOp(token.GreaterEq, *right)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpPop:
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpTrue:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = truePtr
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpFalse:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = falsePtr
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpLNot:
|
2019-01-14 15:32:09 +03:00
|
|
|
operand := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
if (*operand).IsFalsy() {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = truePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = falsePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpBComplement:
|
2019-01-14 15:32:09 +03:00
|
|
|
operand := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
switch x := (*operand).(type) {
|
|
|
|
case *objects.Int:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
var res objects.Object = &objects.Int{Value: ^x.Value}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("invalid operation on %s", (*operand).TypeName())
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpMinus:
|
2019-01-14 15:32:09 +03:00
|
|
|
operand := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
switch x := (*operand).(type) {
|
|
|
|
case *objects.Int:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
var res objects.Object = &objects.Int{Value: -x.Value}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
2019-01-09 10:17:42 +03:00
|
|
|
case *objects.Float:
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
var res objects.Object = &objects.Float{Value: -x.Value}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &res
|
|
|
|
v.sp++
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("invalid operation on %s", (*operand).TypeName())
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpJumpFalsy:
|
2019-01-20 18:41:57 +03:00
|
|
|
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
condition := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
if (*condition).IsFalsy() {
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = pos - 1
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpAndJump:
|
2019-01-20 18:41:57 +03:00
|
|
|
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
condition := *v.stack[v.sp-1]
|
|
|
|
if condition.IsFalsy() {
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = pos - 1
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpOrJump:
|
2019-01-20 18:41:57 +03:00
|
|
|
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
condition := *v.stack[v.sp-1]
|
|
|
|
if !condition.IsFalsy() {
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = pos - 1
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpJump:
|
2019-01-20 18:41:57 +03:00
|
|
|
pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip = pos - 1
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSetGlobal:
|
2019-01-20 18:41:57 +03:00
|
|
|
globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp--
|
|
|
|
|
|
|
|
v.globals[globalIndex] = v.stack[v.sp]
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
case compiler.OpSetSelGlobal:
|
2019-01-20 18:41:57 +03:00
|
|
|
globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
numSelectors := int(v.curInsts[v.ip+3])
|
|
|
|
v.ip += 3
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
// selectors and RHS value
|
|
|
|
selectors := v.stack[v.sp-numSelectors : v.sp]
|
|
|
|
val := v.stack[v.sp-numSelectors-1]
|
|
|
|
v.sp -= numSelectors + 1
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
if err := indexAssign(v.globals[globalIndex], val, selectors); err != nil {
|
2019-01-09 10:17:42 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGetGlobal:
|
2019-01-20 18:41:57 +03:00
|
|
|
globalIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
val := v.globals[globalIndex]
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpArray:
|
2019-01-20 18:41:57 +03:00
|
|
|
numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
var elements []objects.Object
|
|
|
|
for i := v.sp - numElements; i < v.sp; i++ {
|
|
|
|
elements = append(elements, *v.stack[i])
|
|
|
|
}
|
|
|
|
v.sp -= numElements
|
|
|
|
|
|
|
|
var arr objects.Object = &objects.Array{Value: elements}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &arr
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpMap:
|
2019-01-20 18:41:57 +03:00
|
|
|
numElements := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
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]
|
|
|
|
kv[key.(*objects.String).Value] = value
|
|
|
|
}
|
|
|
|
v.sp -= numElements
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
var m objects.Object = &objects.Map{Value: kv}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
v.stack[v.sp] = &m
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp++
|
|
|
|
|
2019-01-16 23:23:20 +03:00
|
|
|
case compiler.OpError:
|
|
|
|
value := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
|
|
|
var err objects.Object = &objects.Error{
|
|
|
|
Value: *value,
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &err
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpIndex:
|
2019-01-14 15:32:09 +03:00
|
|
|
index := v.stack[v.sp-1]
|
|
|
|
left := v.stack[v.sp-2]
|
|
|
|
v.sp -= 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
switch left := (*left).(type) {
|
2019-01-21 10:32:58 +03:00
|
|
|
case objects.Indexable:
|
|
|
|
val, err := left.IndexGet(*index)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2019-01-18 12:43:46 +03:00
|
|
|
}
|
2019-01-21 10:32:58 +03:00
|
|
|
if val == nil {
|
|
|
|
val = objects.UndefinedValue
|
2019-01-18 12:43:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-16 23:23:20 +03:00
|
|
|
case *objects.Error: // err.value
|
|
|
|
key, ok := (*index).(*objects.String)
|
|
|
|
if !ok || key.Value != "value" {
|
|
|
|
return errors.New("invalid selector on error")
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &left.Value
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
2019-01-21 10:32:58 +03:00
|
|
|
return objects.ErrNotIndexable
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSliceIndex:
|
2019-01-14 15:32:09 +03:00
|
|
|
high := v.stack[v.sp-1]
|
|
|
|
low := v.stack[v.sp-2]
|
|
|
|
left := v.stack[v.sp-3]
|
|
|
|
v.sp -= 3
|
|
|
|
|
|
|
|
var lowIdx, highIdx int64
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
switch low := (*low).(type) {
|
|
|
|
case *objects.Undefined:
|
2019-01-14 15:32:09 +03:00
|
|
|
//lowIdx = 0
|
2019-01-09 10:17:42 +03:00
|
|
|
case *objects.Int:
|
2019-01-14 15:32:09 +03:00
|
|
|
lowIdx = low.Value
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("non-integer slice index: %s", low.TypeName())
|
|
|
|
}
|
|
|
|
|
|
|
|
switch high := (*high).(type) {
|
|
|
|
case *objects.Undefined:
|
2019-01-14 15:32:09 +03:00
|
|
|
highIdx = -1 // will be replaced by number of elements
|
2019-01-09 10:17:42 +03:00
|
|
|
case *objects.Int:
|
2019-01-14 15:32:09 +03:00
|
|
|
highIdx = high.Value
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("non-integer slice index: %s", high.TypeName())
|
|
|
|
}
|
|
|
|
|
|
|
|
switch left := (*left).(type) {
|
|
|
|
case *objects.Array:
|
2019-01-14 15:32:09 +03:00
|
|
|
numElements := int64(len(left.Value))
|
|
|
|
|
|
|
|
if lowIdx < 0 || lowIdx >= numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", lowIdx)
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
if highIdx < 0 {
|
|
|
|
highIdx = numElements
|
|
|
|
} else if highIdx < 0 || highIdx > numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", highIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lowIdx > highIdx {
|
|
|
|
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case *objects.String:
|
2019-01-14 15:32:09 +03:00
|
|
|
numElements := int64(len(left.Value))
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if lowIdx < 0 || lowIdx >= numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", lowIdx)
|
|
|
|
}
|
|
|
|
if highIdx < 0 {
|
|
|
|
highIdx = numElements
|
|
|
|
} else if highIdx < 0 || highIdx > numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", highIdx)
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
if lowIdx > highIdx {
|
|
|
|
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-18 12:43:46 +03:00
|
|
|
case *objects.Bytes:
|
|
|
|
numElements := int64(len(left.Value))
|
|
|
|
|
|
|
|
if lowIdx < 0 || lowIdx >= numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", lowIdx)
|
|
|
|
}
|
|
|
|
if highIdx < 0 {
|
|
|
|
highIdx = numElements
|
|
|
|
} else if highIdx < 0 || highIdx > numElements {
|
|
|
|
return fmt.Errorf("index out of bounds: %d", highIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lowIdx > highIdx {
|
|
|
|
return fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("cannot slice %s", left.TypeName())
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpCall:
|
2019-01-20 18:41:57 +03:00
|
|
|
numArgs := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
callee := *v.stack[v.sp-1-numArgs]
|
|
|
|
|
|
|
|
switch callee := callee.(type) {
|
|
|
|
case *objects.Closure:
|
|
|
|
if err := v.callFunction(callee.Fn, callee.Free, numArgs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case *objects.CompiledFunction:
|
|
|
|
if err := v.callFunction(callee, nil, numArgs); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case objects.Callable:
|
|
|
|
var args []objects.Object
|
|
|
|
for _, arg := range v.stack[v.sp-numArgs : v.sp] {
|
|
|
|
args = append(args, *arg)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret, err := callee.Call(args...)
|
|
|
|
v.sp -= numArgs + 1
|
|
|
|
|
|
|
|
// runtime error
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// nil return -> undefined
|
|
|
|
if ret == nil {
|
|
|
|
ret = objects.UndefinedValue
|
|
|
|
}
|
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &ret
|
|
|
|
v.sp++
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("calling non-function: %s", callee.TypeName())
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpReturnValue:
|
2019-01-20 18:41:57 +03:00
|
|
|
//numRets := int(compiler.ReadUint8(v.curInsts[v.ip+1:]))
|
|
|
|
//_ = int64(compiler.ReadUint8(v.curInsts[v.ip+1:]))
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
// TODO: multi-value return is not fully implemented yet
|
|
|
|
//var rets []*objects.Object
|
|
|
|
//for i := 0; i < numRets; i++ {
|
|
|
|
// val := v.pop()
|
|
|
|
// rets = append(rets, val)
|
|
|
|
//}
|
|
|
|
retVal := v.stack[v.sp-1]
|
|
|
|
//v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
v.framesIndex--
|
2019-01-14 02:21:19 +03:00
|
|
|
lastFrame := v.frames[v.framesIndex]
|
|
|
|
v.curFrame = &v.frames[v.framesIndex-1]
|
|
|
|
v.curInsts = v.curFrame.fn.Instructions
|
|
|
|
v.curIPLimit = len(v.curInsts) - 1
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = v.curFrame.ip
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
//v.sp = lastFrame.basePointer - 1
|
|
|
|
v.sp = lastFrame.basePointer
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
//for _, retVal := range rets {
|
|
|
|
// if err := v.push(retVal); err != nil {
|
|
|
|
// return err
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
if v.sp-1 >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp-1] = retVal
|
|
|
|
//v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpReturn:
|
|
|
|
v.framesIndex--
|
2019-01-14 02:21:19 +03:00
|
|
|
lastFrame := v.frames[v.framesIndex]
|
|
|
|
v.curFrame = &v.frames[v.framesIndex-1]
|
|
|
|
v.curInsts = v.curFrame.fn.Instructions
|
|
|
|
v.curIPLimit = len(v.curInsts) - 1
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = v.curFrame.ip
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 02:21:19 +03:00
|
|
|
v.sp = lastFrame.basePointer - 1
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-11 09:34:28 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = undefinedPtr
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
case compiler.OpDefineLocal:
|
2019-01-20 18:41:57 +03:00
|
|
|
localIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-11 09:34:28 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
sp := v.curFrame.basePointer + localIndex
|
2019-01-11 09:34:28 +03:00
|
|
|
|
|
|
|
// local variables can be mutated by other actions
|
|
|
|
// so always store the copy of popped value
|
2019-01-14 15:32:09 +03:00
|
|
|
val := *v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
v.stack[sp] = &val
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSetLocal:
|
2019-01-20 18:41:57 +03:00
|
|
|
localIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
sp := v.curFrame.basePointer + localIndex
|
2019-01-11 09:34:28 +03:00
|
|
|
|
|
|
|
// update pointee of v.stack[sp] instead of replacing the pointer itself.
|
|
|
|
// this is needed because there can be free variables referencing the same local variables.
|
2019-01-14 15:32:09 +03:00
|
|
|
val := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
*v.stack[sp] = *val // also use a copy of popped value
|
2019-01-09 10:17:42 +03:00
|
|
|
|
|
|
|
case compiler.OpSetSelLocal:
|
2019-01-20 18:41:57 +03:00
|
|
|
localIndex := int(v.curInsts[v.ip+1])
|
|
|
|
numSelectors := int(v.curInsts[v.ip+2])
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
// selectors and RHS value
|
|
|
|
selectors := v.stack[v.sp-numSelectors : v.sp]
|
|
|
|
val := v.stack[v.sp-numSelectors-1]
|
|
|
|
v.sp -= numSelectors + 1
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
sp := v.curFrame.basePointer + localIndex
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
if err := indexAssign(v.stack[sp], val, selectors); err != nil {
|
2019-01-09 10:17:42 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGetLocal:
|
2019-01-20 18:41:57 +03:00
|
|
|
localIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
val := v.stack[v.curFrame.basePointer+localIndex]
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGetBuiltin:
|
2019-01-20 18:41:57 +03:00
|
|
|
builtinIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &builtinFuncs[builtinIndex]
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpClosure:
|
2019-01-20 18:41:57 +03:00
|
|
|
constIndex := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
numFree := int(v.curInsts[v.ip+3])
|
|
|
|
v.ip += 3
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
if err := v.pushClosure(constIndex, numFree); err != nil {
|
2019-01-09 10:17:42 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpGetFree:
|
2019-01-20 18:41:57 +03:00
|
|
|
freeIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 02:21:19 +03:00
|
|
|
val := v.curFrame.freeVars[freeIndex]
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSetSelFree:
|
2019-01-20 18:41:57 +03:00
|
|
|
freeIndex := int(v.curInsts[v.ip+1])
|
|
|
|
numSelectors := int(v.curInsts[v.ip+2])
|
|
|
|
v.ip += 2
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
// selectors and RHS value
|
|
|
|
selectors := v.stack[v.sp-numSelectors : v.sp]
|
|
|
|
val := v.stack[v.sp-numSelectors-1]
|
|
|
|
v.sp -= numSelectors + 1
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
if err := indexAssign(v.curFrame.freeVars[freeIndex], val, selectors); err != nil {
|
2019-01-09 10:17:42 +03:00
|
|
|
return err
|
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpSetFree:
|
2019-01-20 18:41:57 +03:00
|
|
|
freeIndex := int(v.curInsts[v.ip+1])
|
|
|
|
v.ip++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
val := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 02:21:19 +03:00
|
|
|
*v.curFrame.freeVars[freeIndex] = *val
|
2019-01-11 09:34:28 +03:00
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpIteratorInit:
|
|
|
|
var iterator objects.Object
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
dst := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
switch dst := (*dst).(type) {
|
|
|
|
case *objects.Array:
|
|
|
|
iterator = objects.NewArrayIterator(dst)
|
|
|
|
case *objects.Map:
|
|
|
|
iterator = objects.NewMapIterator(dst)
|
2019-01-18 12:43:46 +03:00
|
|
|
case *objects.ImmutableMap:
|
2019-01-17 12:56:05 +03:00
|
|
|
iterator = objects.NewModuleMapIterator(dst)
|
2019-01-09 10:17:42 +03:00
|
|
|
case *objects.String:
|
|
|
|
iterator = objects.NewStringIterator(dst)
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("non-iterable type: %s", dst.TypeName())
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-11 09:34:28 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = &iterator
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpIteratorNext:
|
2019-01-14 15:32:09 +03:00
|
|
|
iterator := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
b := (*iterator).(objects.Iterator).Next()
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
if b {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = truePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
} else {
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = falsePtr
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpIteratorKey:
|
2019-01-14 15:32:09 +03:00
|
|
|
iterator := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
val := (*iterator).(objects.Iterator).Key()
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
case compiler.OpIteratorValue:
|
2019-01-14 15:32:09 +03:00
|
|
|
iterator := v.stack[v.sp-1]
|
|
|
|
v.sp--
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
val := (*iterator).(objects.Iterator).Value()
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
2019-01-14 15:32:09 +03:00
|
|
|
|
|
|
|
v.stack[v.sp] = &val
|
|
|
|
v.sp++
|
|
|
|
|
2019-01-17 12:56:05 +03:00
|
|
|
case compiler.OpModule:
|
2019-01-20 18:41:57 +03:00
|
|
|
cidx := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
|
|
|
|
v.ip += 2
|
2019-01-17 12:56:05 +03:00
|
|
|
|
|
|
|
if err := v.importModule(v.constants[cidx].(*objects.CompiledModule)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
default:
|
2019-01-20 18:41:57 +03:00
|
|
|
return fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-12 08:50:23 +03:00
|
|
|
// check if stack still has some objects left
|
2019-01-20 17:33:22 +03:00
|
|
|
if v.sp > 0 && atomic.LoadInt64(&v.aborting) == 0 {
|
2019-01-12 08:50:23 +03:00
|
|
|
return fmt.Errorf("non empty stack after execution")
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// Globals returns the global variables.
|
2019-01-09 23:39:40 +03:00
|
|
|
func (v *VM) Globals() []*objects.Object {
|
|
|
|
return v.globals
|
|
|
|
}
|
|
|
|
|
2019-01-15 09:24:33 +03:00
|
|
|
// FrameInfo returns the current function call frame information.
|
2019-01-20 18:41:57 +03:00
|
|
|
func (v *VM) FrameInfo() (frameIndex, ip int) {
|
2019-01-09 10:17:42 +03:00
|
|
|
return v.framesIndex - 1, v.frames[v.framesIndex-1].ip
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VM) pushClosure(constIndex, numFree int) error {
|
|
|
|
c := v.constants[constIndex]
|
|
|
|
|
|
|
|
fn, ok := c.(*objects.CompiledFunction)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("not a function: %s", fn.TypeName())
|
|
|
|
}
|
|
|
|
|
|
|
|
free := make([]*objects.Object, numFree)
|
|
|
|
for i := 0; i < numFree; i++ {
|
|
|
|
free[i] = v.stack[v.sp-numFree+i]
|
|
|
|
}
|
|
|
|
v.sp -= numFree
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
var cl objects.Object = &objects.Closure{
|
|
|
|
Fn: fn,
|
|
|
|
Free: free,
|
|
|
|
}
|
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
v.stack[v.sp] = &cl
|
|
|
|
v.sp++
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-14 15:32:09 +03:00
|
|
|
return nil
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Object, numArgs int) error {
|
|
|
|
if numArgs != fn.NumParameters {
|
|
|
|
return fmt.Errorf("wrong number of arguments: want=%d, got=%d",
|
|
|
|
fn.NumParameters, numArgs)
|
|
|
|
}
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
// check if this is a tail-call (recursive call right before return)
|
2019-01-14 02:21:19 +03:00
|
|
|
if fn == v.curFrame.fn { // recursion
|
2019-01-20 18:41:57 +03:00
|
|
|
nextOp := compiler.Opcode(v.curInsts[v.ip+1])
|
2019-01-11 11:53:04 +03:00
|
|
|
if nextOp == compiler.OpReturnValue || // tail call
|
2019-01-11 09:34:28 +03:00
|
|
|
(nextOp == compiler.OpPop &&
|
2019-01-20 18:41:57 +03:00
|
|
|
compiler.OpReturn == compiler.Opcode(v.curInsts[v.ip+2])) {
|
2019-01-11 09:34:28 +03:00
|
|
|
|
|
|
|
// stack before tail-call
|
|
|
|
//
|
|
|
|
// |--------|
|
|
|
|
// | | <- SP current
|
|
|
|
// |--------|
|
|
|
|
// | *ARG2 | for next function (tail-call)
|
|
|
|
// |--------|
|
|
|
|
// | *ARG1 | for next function (tail-call)
|
|
|
|
// |--------|
|
2019-01-13 13:24:32 +03:00
|
|
|
// | FUNC | function itself
|
|
|
|
// |--------|
|
2019-01-11 09:34:28 +03:00
|
|
|
// | LOCAL3 | for current function
|
|
|
|
// |--------|
|
|
|
|
// | LOCAL2 | for current function
|
|
|
|
// |--------|
|
|
|
|
// | ARG2 | for current function
|
|
|
|
// |--------|
|
|
|
|
// | ARG1 | <- BP for current function
|
|
|
|
// |--------|
|
|
|
|
|
2019-01-14 02:21:19 +03:00
|
|
|
for p := 0; p < numArgs; p++ {
|
|
|
|
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
|
|
|
|
}
|
2019-01-13 13:24:32 +03:00
|
|
|
v.sp -= numArgs + 1
|
2019-01-21 10:32:58 +03:00
|
|
|
v.ip = -1 // reset IP to beginning of the frame
|
2019-01-11 09:34:28 +03:00
|
|
|
|
|
|
|
// stack after tail-call
|
|
|
|
//
|
|
|
|
// |--------|
|
|
|
|
// | |
|
|
|
|
// |--------|
|
|
|
|
// | *ARG2 |
|
|
|
|
// |--------|
|
2019-01-13 13:24:32 +03:00
|
|
|
// | *ARG1 |
|
|
|
|
// |--------|
|
|
|
|
// | FUNC | <- SP current
|
2019-01-11 09:34:28 +03:00
|
|
|
// |--------|
|
|
|
|
// | LOCAL3 | for current function
|
|
|
|
// |--------|
|
|
|
|
// | LOCAL2 | for current function
|
|
|
|
// |--------|
|
|
|
|
// | *ARG2 | (copied)
|
|
|
|
// |--------|
|
|
|
|
// | *ARG1 | <- BP (copied)
|
|
|
|
// |--------|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-20 18:41:57 +03:00
|
|
|
// store current ip before call
|
|
|
|
v.curFrame.ip = v.ip
|
|
|
|
|
|
|
|
// update call frame
|
2019-01-14 02:21:19 +03:00
|
|
|
v.curFrame = &(v.frames[v.framesIndex])
|
|
|
|
v.curFrame.fn = fn
|
|
|
|
v.curFrame.freeVars = freeVars
|
|
|
|
v.curFrame.basePointer = v.sp - numArgs
|
|
|
|
v.curInsts = fn.Instructions
|
2019-01-20 18:41:57 +03:00
|
|
|
v.ip = -1
|
2019-01-14 02:21:19 +03:00
|
|
|
v.curIPLimit = len(v.curInsts) - 1
|
2019-01-09 10:17:42 +03:00
|
|
|
v.framesIndex++
|
|
|
|
|
|
|
|
v.sp = v.sp - numArgs + fn.NumLocals
|
|
|
|
|
2019-01-11 09:34:28 +03:00
|
|
|
// stack after the function call
|
|
|
|
//
|
|
|
|
// |--------|
|
|
|
|
// | | <- SP after function call
|
|
|
|
// |--------|
|
|
|
|
// | LOCAL4 | (BP+3)
|
|
|
|
// |--------|
|
|
|
|
// | LOCAL3 | (BP+2) <- SP before function call
|
|
|
|
// |--------|
|
|
|
|
// | ARG2 | (BP+1)
|
|
|
|
// |--------|
|
|
|
|
// | ARG1 | (BP+0) <- BP
|
|
|
|
// |--------|
|
|
|
|
|
2019-01-09 10:17:42 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-18 12:43:46 +03:00
|
|
|
// TODO: should reuse *objects.ImmutableMap for the same imports?
|
2019-01-17 12:56:05 +03:00
|
|
|
func (v *VM) importModule(compiledModule *objects.CompiledModule) error {
|
|
|
|
// import module is basically to create a new instance of VM
|
|
|
|
// and run the module code and retrieve all global variables after execution.
|
|
|
|
moduleVM := NewVM(&compiler.Bytecode{
|
|
|
|
Instructions: compiledModule.Instructions,
|
|
|
|
Constants: v.constants,
|
|
|
|
}, nil)
|
|
|
|
if err := moduleVM.Run(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
mmValue := make(map[string]objects.Object)
|
|
|
|
for name, index := range compiledModule.Globals {
|
|
|
|
mmValue[name] = *moduleVM.globals[index]
|
|
|
|
}
|
|
|
|
|
2019-01-18 12:43:46 +03:00
|
|
|
var mm objects.Object = &objects.ImmutableMap{Value: mmValue}
|
2019-01-17 12:56:05 +03:00
|
|
|
|
|
|
|
if v.sp >= StackSize {
|
|
|
|
return ErrStackOverflow
|
|
|
|
}
|
|
|
|
|
|
|
|
v.stack[v.sp] = &mm
|
|
|
|
v.sp++
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
func indexAssign(dst, src *objects.Object, selectors []*objects.Object) error {
|
2019-01-09 10:17:42 +03:00
|
|
|
numSel := len(selectors)
|
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
for sidx := numSel - 1; sidx > 0; sidx-- {
|
|
|
|
indexable, ok := (*dst).(objects.Indexable)
|
|
|
|
if !ok {
|
|
|
|
return objects.ErrNotIndexable
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
next, err := indexable.IndexGet(*selectors[sidx])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
dst = &next
|
|
|
|
}
|
2019-01-09 10:17:42 +03:00
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
indexAssignable, ok := (*dst).(objects.IndexAssignable)
|
|
|
|
if !ok {
|
|
|
|
return objects.ErrNotIndexAssignable
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
2019-01-21 10:32:58 +03:00
|
|
|
return indexAssignable.IndexSet(*selectors[0], *src)
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
builtinFuncs = make([]objects.Object, len(objects.Builtins))
|
|
|
|
for i, b := range objects.Builtins {
|
2019-01-10 06:07:03 +03:00
|
|
|
builtinFuncs[i] = &objects.BuiltinFunction{Value: b.Func}
|
2019-01-09 10:17:42 +03:00
|
|
|
}
|
|
|
|
}
|