xgo/runtime/vm.go

1199 lines
25 KiB
Go
Raw Normal View History

2019-01-11 13:27:28 +03:00
package runtime
2019-01-09 10:17:42 +03:00
import (
"fmt"
"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 (
StackSize = 2048
2019-01-10 01:10:47 +03:00
GlobalsSize = 1024
2019-01-09 10:17:42 +03:00
MaxFrames = 1024
)
var (
truePtr = &objects.TrueValue
falsePtr = &objects.FalseValue
undefinedPtr = &objects.UndefinedValue
2019-01-09 10:17:42 +03:00
builtinFuncs []objects.Object
)
type VM struct {
constants []objects.Object
stack []*objects.Object
sp int
globals []*objects.Object
frames []Frame
framesIndex int
curFrame *Frame
curInsts []byte
curIPLimit int
2019-01-09 10:17:42 +03:00
aborting bool
}
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,
curFrame: &(frames[0]),
curInsts: frames[0].fn.Instructions,
curIPLimit: len(frames[0].fn.Instructions) - 1,
2019-01-09 10:17:42 +03:00
}
}
func (v *VM) Abort() {
v.aborting = true
}
func (v *VM) Run() error {
var ip int
for v.curFrame.ip < v.curIPLimit && !v.aborting {
v.curFrame.ip++
2019-01-09 10:17:42 +03:00
ip = v.curFrame.ip
2019-01-09 10:17:42 +03:00
switch compiler.Opcode(v.curInsts[ip]) {
2019-01-09 10:17:42 +03:00
case compiler.OpConstant:
cidx := compiler.ReadUint16(v.curInsts[ip+1:])
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &v.constants[cidx]
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpNull:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = undefinedPtr
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpAdd:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpSub:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpMul:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpDiv:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpRem:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBAnd:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBOr:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBXor:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBAndNot:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBShiftLeft:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBShiftRight:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpEqual:
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) {
v.stack[v.sp] = truePtr
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = falsePtr
2019-01-09 10:17:42 +03:00
}
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpNotEqual:
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) {
v.stack[v.sp] = falsePtr
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = truePtr
2019-01-09 10:17:42 +03:00
}
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpGreaterThan:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpGreaterThanEqual:
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
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpPop:
v.sp--
2019-01-09 10:17:42 +03:00
case compiler.OpTrue:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = truePtr
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpFalse:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = falsePtr
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpLNot:
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() {
v.stack[v.sp] = truePtr
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = falsePtr
2019-01-09 10:17:42 +03:00
}
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpBComplement:
operand := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
switch x := (*operand).(type) {
case *objects.Int:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +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-09 10:17:42 +03:00
case compiler.OpMinus:
operand := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
switch x := (*operand).(type) {
case *objects.Int:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +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:
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +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-09 10:17:42 +03:00
case compiler.OpJumpFalsy:
pos := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
condition := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
if (*condition).IsFalsy() {
v.curFrame.ip = pos - 1
2019-01-09 10:17:42 +03:00
}
2019-01-09 10:17:42 +03:00
case compiler.OpAndJump:
pos := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
condition := *v.stack[v.sp-1]
if condition.IsFalsy() {
v.curFrame.ip = pos - 1
2019-01-09 10:17:42 +03:00
} else {
v.sp--
2019-01-09 10:17:42 +03:00
}
2019-01-09 10:17:42 +03:00
case compiler.OpOrJump:
pos := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
condition := *v.stack[v.sp-1]
if !condition.IsFalsy() {
v.curFrame.ip = pos - 1
2019-01-09 10:17:42 +03:00
} else {
v.sp--
2019-01-09 10:17:42 +03:00
}
2019-01-09 10:17:42 +03:00
case compiler.OpJump:
pos := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.ip = pos - 1
2019-01-09 10:17:42 +03:00
case compiler.OpSetGlobal:
globalIndex := compiler.ReadUint16(v.curInsts[ip+1:])
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
v.sp--
v.globals[globalIndex] = v.stack[v.sp]
2019-01-09 10:17:42 +03:00
case compiler.OpSetSelGlobal:
globalIndex := compiler.ReadUint16(v.curInsts[ip+1:])
numSelectors := int(compiler.ReadUint8(v.curInsts[ip+3:]))
v.curFrame.ip += 3
2019-01-09 10:17:42 +03:00
// pop selector outcomes (left to right)
selectors := make([]interface{}, numSelectors, numSelectors)
for i := 0; i < numSelectors; i++ {
sel := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
switch sel := (*sel).(type) {
case *objects.String: // map key
selectors[i] = sel.Value
case *objects.Int: // array index
selectors[i] = int(sel.Value)
default:
return fmt.Errorf("invalid selector type: %s", sel.TypeName())
}
}
// RHS value
val := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
if err := selectorAssign(v.globals[globalIndex], val, selectors); err != nil {
return err
}
2019-01-09 10:17:42 +03:00
case compiler.OpGetGlobal:
globalIndex := compiler.ReadUint16(v.curInsts[ip+1:])
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
val := v.globals[globalIndex]
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = val
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpArray:
numElements := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.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}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &arr
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpMap:
numElements := int(compiler.ReadUint16(v.curInsts[ip+1:]))
v.curFrame.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
var map_ objects.Object = &objects.Map{Value: kv}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &map_
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpIndex:
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) {
case *objects.Array:
idx, ok := (*index).(*objects.Int)
if !ok {
return fmt.Errorf("non-integer array index: %s", left.TypeName())
}
if idx.Value < 0 || idx.Value >= int64(len(left.Value)) {
return fmt.Errorf("index out of bounds: %d", index)
2019-01-09 10:17:42 +03:00
}
if v.sp >= StackSize {
return ErrStackOverflow
}
v.stack[v.sp] = &left.Value[idx.Value]
v.sp++
2019-01-09 10:17:42 +03:00
case *objects.String:
idx, ok := (*index).(*objects.Int)
if !ok {
return fmt.Errorf("non-integer array index: %s", left.TypeName())
}
str := []rune(left.Value)
if idx.Value < 0 || idx.Value >= int64(len(str)) {
return fmt.Errorf("index out of bounds: %d", index)
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
var val objects.Object = &objects.Char{Value: str[idx.Value]}
v.stack[v.sp] = &val
v.sp++
2019-01-09 10:17:42 +03:00
case *objects.Map:
key, ok := (*index).(*objects.String)
if !ok {
return fmt.Errorf("non-string map key: %s", left.TypeName())
}
var res = objects.UndefinedValue
val, ok := left.Value[key.Value]
if ok {
res = val
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &res
v.sp++
2019-01-09 10:17:42 +03:00
default:
return fmt.Errorf("type %s does not support indexing", left.TypeName())
}
2019-01-09 10:17:42 +03:00
case compiler.OpSliceIndex:
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:
//lowIdx = 0
2019-01-09 10:17:42 +03:00
case *objects.Int:
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:
highIdx = -1 // will be replaced by number of elements
2019-01-09 10:17:42 +03:00
case *objects.Int:
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:
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
}
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:
numElements := int64(len(left.Value))
2019-01-09 10:17:42 +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
}
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-09 10:17:42 +03:00
default:
return fmt.Errorf("cannot slice %s", left.TypeName())
}
2019-01-09 10:17:42 +03:00
case compiler.OpCall:
numArgs := int(compiler.ReadUint8(v.curInsts[ip+1:]))
v.curFrame.ip += 1
2019-01-09 10:17:42 +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-09 10:17:42 +03:00
case compiler.OpReturnValue:
//numRets := int(compiler.ReadUint8(v.curInsts[ip+1:]))
_ = int(compiler.ReadUint8(v.curInsts[ip+1:]))
v.curFrame.ip += 1
2019-01-09 10:17:42 +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--
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-09 10:17:42 +03:00
//v.sp = lastFrame.basePointer - 1
v.sp = lastFrame.basePointer
2019-01-09 10:17:42 +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
}
v.stack[v.sp-1] = retVal
//v.sp++
2019-01-09 10:17:42 +03:00
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
2019-01-09 10:17:42 +03:00
v.sp = lastFrame.basePointer - 1
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = undefinedPtr
v.sp++
case compiler.OpDefineLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
sp := v.curFrame.basePointer + int(localIndex)
// local variables can be mutated by other actions
// so always store the copy of popped value
val := *v.stack[v.sp-1]
v.sp--
v.stack[sp] = &val
2019-01-09 10:17:42 +03:00
case compiler.OpSetLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
2019-01-09 10:17:42 +03:00
sp := v.curFrame.basePointer + int(localIndex)
// 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.
val := v.stack[v.sp-1]
v.sp--
*v.stack[sp] = *val // also use a copy of popped value
2019-01-09 10:17:42 +03:00
case compiler.OpSetSelLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
numSelectors := int(compiler.ReadUint8(v.curInsts[ip+2:]))
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
// pop selector outcomes (left to right)
selectors := make([]interface{}, numSelectors, numSelectors)
for i := 0; i < numSelectors; i++ {
sel := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
switch sel := (*sel).(type) {
case *objects.String: // map key
selectors[i] = sel.Value
case *objects.Int: // array index
selectors[i] = int(sel.Value)
default:
return fmt.Errorf("invalid selector type: %s", sel.TypeName())
}
}
// RHS value
val := v.stack[v.sp-1] // no need to copy value here; selectorAssign uses copy of value
v.sp--
2019-01-09 10:17:42 +03:00
sp := v.curFrame.basePointer + int(localIndex)
2019-01-09 10:17:42 +03:00
if err := selectorAssign(v.stack[sp], val, selectors); err != nil {
return err
}
2019-01-09 10:17:42 +03:00
case compiler.OpGetLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
2019-01-09 10:17:42 +03:00
val := v.stack[v.curFrame.basePointer+int(localIndex)]
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = val
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpGetBuiltin:
builtinIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &builtinFuncs[builtinIndex]
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpClosure:
constIndex := compiler.ReadUint16(v.curInsts[ip+1:])
numFree := compiler.ReadUint8(v.curInsts[ip+3:])
v.curFrame.ip += 3
2019-01-09 10:17:42 +03:00
if err := v.pushClosure(int(constIndex), int(numFree)); err != nil {
return err
}
2019-01-09 10:17:42 +03:00
case compiler.OpGetFree:
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
2019-01-09 10:17:42 +03:00
val := v.curFrame.freeVars[freeIndex]
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = val
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpSetSelFree:
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
numSelectors := int(compiler.ReadUint8(v.curInsts[ip+2:]))
v.curFrame.ip += 2
2019-01-09 10:17:42 +03:00
// pop selector outcomes (left to right)
selectors := make([]interface{}, numSelectors, numSelectors)
for i := 0; i < numSelectors; i++ {
sel := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
switch sel := (*sel).(type) {
case *objects.String: // map key
selectors[i] = sel.Value
case *objects.Int: // array index
selectors[i] = int(sel.Value)
default:
return fmt.Errorf("invalid selector type: %s", sel.TypeName())
}
}
// RHS value
val := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
if err := selectorAssign(v.curFrame.freeVars[freeIndex], val, selectors); err != nil {
2019-01-09 10:17:42 +03:00
return err
}
2019-01-09 10:17:42 +03:00
case compiler.OpSetFree:
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
2019-01-09 10:17:42 +03:00
val := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
*v.curFrame.freeVars[freeIndex] = *val
2019-01-09 10:17:42 +03:00
case compiler.OpIteratorInit:
var iterator objects.Object
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)
case *objects.String:
iterator = objects.NewStringIterator(dst)
default:
return fmt.Errorf("non-iterable type: %s", dst.TypeName())
}
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &iterator
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpIteratorNext:
iterator := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
b := (*iterator).(objects.Iterator).Next()
if v.sp >= StackSize {
return ErrStackOverflow
}
2019-01-09 10:17:42 +03:00
if b {
v.stack[v.sp] = truePtr
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = falsePtr
2019-01-09 10:17:42 +03:00
}
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpIteratorKey:
iterator := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
val := (*iterator).(objects.Iterator).Key()
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &val
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpIteratorValue:
iterator := v.stack[v.sp-1]
v.sp--
2019-01-09 10:17:42 +03:00
val := (*iterator).(objects.Iterator).Value()
if v.sp >= StackSize {
return ErrStackOverflow
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = &val
v.sp++
2019-01-09 10:17:42 +03:00
default:
return fmt.Errorf("unknown opcode: %d", v.curInsts[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
if v.sp > 0 && !v.aborting {
return fmt.Errorf("non empty stack after execution")
}
2019-01-09 10:17:42 +03:00
return nil
}
2019-01-09 23:39:40 +03:00
func (v *VM) Globals() []*objects.Object {
return v.globals
}
2019-01-12 08:50:23 +03:00
func (v *VM) FrameInfo() (frameIndex int, 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
if v.sp >= StackSize {
return ErrStackOverflow
}
2019-01-09 10:17:42 +03:00
var cl objects.Object = &objects.Closure{
Fn: fn,
Free: free,
}
v.stack[v.sp] = &cl
v.sp++
2019-01-09 10:17:42 +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)
}
// check if this is a tail-call (recursive call right before return)
if fn == v.curFrame.fn { // recursion
nextOp := compiler.Opcode(v.curInsts[v.curFrame.ip+1])
2019-01-11 11:53:04 +03:00
if nextOp == compiler.OpReturnValue || // tail call
(nextOp == compiler.OpPop &&
compiler.OpReturn == compiler.Opcode(v.curInsts[v.curFrame.ip+2])) {
// stack before tail-call
//
// |--------|
// | | <- SP current
// |--------|
// | *ARG2 | for next function (tail-call)
// |--------|
// | *ARG1 | for next function (tail-call)
// |--------|
// | FUNC | function itself
// |--------|
// | LOCAL3 | for current function
// |--------|
// | LOCAL2 | for current function
// |--------|
// | ARG2 | for current function
// |--------|
// | ARG1 | <- BP for current function
// |--------|
for p := 0; p < numArgs; p++ {
v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
}
v.sp -= numArgs + 1
v.curFrame.ip = -1 // reset IP to beginning of the frame
// stack after tail-call
//
// |--------|
// | |
// |--------|
// | *ARG2 |
// |--------|
// | *ARG1 |
// |--------|
// | FUNC | <- SP current
// |--------|
// | LOCAL3 | for current function
// |--------|
// | LOCAL2 | for current function
// |--------|
// | *ARG2 | (copied)
// |--------|
// | *ARG1 | <- BP (copied)
// |--------|
return nil
}
}
v.curFrame = &(v.frames[v.framesIndex])
v.curFrame.fn = fn
v.curFrame.freeVars = freeVars
v.curFrame.ip = -1
v.curFrame.basePointer = v.sp - numArgs
v.curInsts = fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
2019-01-09 10:17:42 +03:00
v.framesIndex++
v.sp = v.sp - numArgs + fn.NumLocals
// 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
}
func selectorAssign(dst, src *objects.Object, selectors []interface{}) error {
numSel := len(selectors)
for idx := 0; idx < numSel; idx++ {
switch sel := selectors[idx].(type) {
case string:
map_, isMap := (*dst).(*objects.Map)
if !isMap {
return fmt.Errorf("invalid map object for selector '%s'", sel)
}
if idx == numSel-1 {
map_.Set(sel, *src)
return nil
}
nxt, found := map_.Get(sel)
if !found {
return fmt.Errorf("key not found '%s'", sel)
}
dst = &nxt
case int:
arr, isArray := (*dst).(*objects.Array)
if !isArray {
return fmt.Errorf("invalid array object for select '[%d]'", sel)
}
if idx == numSel-1 {
return arr.Set(sel, *src)
}
nxt, err := arr.Get(sel)
if err != nil {
return err
}
dst = &nxt
default:
panic(fmt.Errorf("invalid selector term: %T", sel))
}
}
return nil
}
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
}
}