xgo/runtime/vm.go

1295 lines
26 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"
"sync/atomic"
2019-01-09 10:17:42 +03:00
"github.com/d5/tengo/compiler"
"github.com/d5/tengo/compiler/source"
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 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
// MaxFrames is the maximum number of function frames.
MaxFrames = 1024
2019-01-09 10:17:42 +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
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
maxAllocs int64
allocs int64
err error
errOffset int
2019-01-09 10:17:42 +03:00
}
// NewVM creates a VM.
func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, builtinFuncs []objects.Object, builtinModules map[string]objects.Object, maxAllocs int64) *VM {
2019-01-09 10:17:42 +03:00
if globals == nil {
globals = make([]objects.Object, GlobalsSize)
2019-01-09 10:17:42 +03:00
}
if builtinModules == nil {
builtinModules = make(map[string]objects.Object)
}
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,
}
}
}
2019-01-09 10:17:42 +03:00
frames := make([]Frame, MaxFrames)
frames[0].fn = bytecode.MainFunction
2019-01-09 10:17:42 +03:00
frames[0].ip = -1
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,
maxAllocs: maxAllocs,
2019-01-09 10:17:42 +03:00
}
}
// Abort aborts the execution.
2019-01-09 10:17:42 +03:00
func (v *VM) Abort() {
atomic.StoreInt64(&v.aborting, 1)
2019-01-09 10:17:42 +03:00
}
// Run starts the execution.
func (v *VM) Run() (err error) {
// reset VM states
v.sp = 0
v.curFrame = &(v.frames[0])
v.curInsts = v.curFrame.fn.Instructions
v.curIPLimit = len(v.curInsts) - 1
v.framesIndex = 1
v.ip = -1
v.allocs = v.maxAllocs + 1
atomic.StoreInt64(&v.aborting, 0)
v.run()
err = v.err
if err != nil {
filePos := v.fileSet.Position(v.curFrame.fn.SourceMap[v.ip-v.errOffset])
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])
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() {
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
switch 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
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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 {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = objects.UndefinedValue
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpAdd:
v.binaryOp(token.Add)
2019-01-09 10:17:42 +03:00
case compiler.OpSub:
v.binaryOp(token.Sub)
2019-01-09 10:17:42 +03:00
case compiler.OpMul:
v.binaryOp(token.Mul)
2019-01-09 10:17:42 +03:00
case compiler.OpDiv:
v.binaryOp(token.Quo)
2019-01-09 10:17:42 +03:00
case compiler.OpRem:
v.binaryOp(token.Rem)
2019-01-09 10:17:42 +03:00
case compiler.OpBAnd:
v.binaryOp(token.And)
2019-01-09 10:17:42 +03:00
case compiler.OpBOr:
v.binaryOp(token.Or)
2019-01-09 10:17:42 +03:00
case compiler.OpBXor:
v.binaryOp(token.Xor)
2019-01-09 10:17:42 +03:00
case compiler.OpBAndNot:
v.binaryOp(token.AndNot)
2019-01-09 10:17:42 +03:00
case compiler.OpBShiftLeft:
v.binaryOp(token.Shl)
2019-01-09 10:17:42 +03:00
case compiler.OpBShiftRight:
v.binaryOp(token.Shr)
case compiler.OpGreaterThan:
v.binaryOp(token.Greater)
case compiler.OpGreaterThanEqual:
v.binaryOp(token.GreaterEq)
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 {
v.err = ErrStackOverflow
return
}
2019-01-09 10:17:42 +03:00
if left.Equals(right) {
v.stack[v.sp] = objects.TrueValue
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = objects.FalseValue
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 {
v.err = ErrStackOverflow
return
}
2019-01-09 10:17:42 +03:00
if left.Equals(right) {
v.stack[v.sp] = objects.FalseValue
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = objects.TrueValue
2019-01-09 10:17:42 +03:00
}
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 {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = objects.TrueValue
v.sp++
2019-01-09 10:17:42 +03:00
case compiler.OpFalse:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = objects.FalseValue
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 {
v.err = ErrStackOverflow
return
}
2019-01-09 10:17:42 +03:00
if operand.IsFalsy() {
v.stack[v.sp] = objects.TrueValue
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = objects.FalseValue
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) {
2019-01-09 10:17:42 +03:00
case *objects.Int:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
var res objects.Object = &objects.Int{Value: ^x.Value}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = res
v.sp++
2019-01-09 10:17:42 +03:00
default:
v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName())
return
2019-01-09 10:17:42 +03:00
}
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) {
2019-01-09 10:17:42 +03:00
case *objects.Int:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
var res objects.Object = &objects.Int{Value: -x.Value}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = res
v.sp++
2019-01-09 10:17:42 +03:00
case *objects.Float:
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
var res objects.Object = &objects.Float{Value: -x.Value}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = res
v.sp++
2019-01-09 10:17:42 +03:00
default:
v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName())
return
2019-01-09 10:17:42 +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
condition := v.stack[v.sp-1]
v.sp--
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-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]
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
} else {
v.sp--
2019-01-09 10:17:42 +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]
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
} else {
v.sp--
2019-01-09 10:17:42 +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-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
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
// selectors and RHS value
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
2019-01-09 10:17:42 +03:00
if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil {
v.errOffset = 3
v.err = e
return
2019-01-09 10:17:42 +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]
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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:
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])
2019-01-09 10:17:42 +03:00
}
v.sp -= numElements
var arr objects.Object = &objects.Array{Value: elements}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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:
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]
2019-01-09 10:17:42 +03:00
kv[key.(*objects.String).Value] = value
}
v.sp -= numElements
var m objects.Object = &objects.Map{Value: kv}
2019-01-09 10:17:42 +03:00
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = m
v.sp++
case compiler.OpError:
value := v.stack[v.sp-1]
var e objects.Object = &objects.Error{
Value: value,
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-1] = e
2019-01-25 06:31:17 +03:00
case compiler.OpImmutable:
2019-01-26 01:54:58 +03:00
value := v.stack[v.sp-1]
switch value := value.(type) {
2019-01-26 01:54:58 +03:00
case *objects.Array:
var immutableArray objects.Object = &objects.ImmutableArray{
Value: value.Value,
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-1] = immutableArray
2019-01-26 01:54:58 +03:00
case *objects.Map:
var immutableMap objects.Object = &objects.ImmutableMap{
Value: value.Value,
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-1] = immutableMap
2019-01-26 01:54:58 +03:00
}
2019-01-25 06:31:17 +03:00
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.Indexable:
val, e := left.IndexGet(index)
if e != nil {
if e == objects.ErrInvalidIndexType {
v.err = fmt.Errorf("invalid index type: %s", index.TypeName())
return
}
v.err = e
return
2019-01-18 12:43:46 +03:00
}
if val == nil {
val = objects.UndefinedValue
2019-01-18 12:43:46 +03:00
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-18 12:43:46 +03:00
}
v.stack[v.sp] = val
2019-01-18 12:43:46 +03:00
v.sp++
case *objects.Error: // e.value
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.sp++
2019-01-09 10:17:42 +03:00
default:
v.err = fmt.Errorf("not indexable: %s", left.TypeName())
return
2019-01-09 10:17:42 +03:00
}
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 int64
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())
return
}
2019-01-09 10:17:42 +03:00
}
switch left := left.(type) {
2019-01-09 10:17:42 +03:00
case *objects.Array:
numElements := int64(len(left.Value))
var highIdx int64
if high == objects.UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
return
}
if lowIdx > highIdx {
v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
return
}
if lowIdx < 0 {
lowIdx = 0
} else if lowIdx > numElements {
lowIdx = numElements
}
if highIdx < 0 {
highIdx = 0
} else if highIdx > numElements {
highIdx = numElements
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = val
v.sp++
2019-01-26 01:54:58 +03:00
case *objects.ImmutableArray:
numElements := int64(len(left.Value))
var highIdx int64
if high == objects.UndefinedValue {
2019-01-26 01:54:58 +03:00
highIdx = numElements
} else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
return
2019-01-26 01:54:58 +03:00
}
if lowIdx > highIdx {
v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
return
2019-01-26 01:54:58 +03:00
}
if lowIdx < 0 {
lowIdx = 0
} else if lowIdx > numElements {
lowIdx = numElements
}
if highIdx < 0 {
highIdx = 0
} else if highIdx > numElements {
highIdx = numElements
}
2019-01-26 01:54:58 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-26 01:54:58 +03:00
}
var val objects.Object = &objects.Array{Value: left.Value[lowIdx:highIdx]}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = val
2019-01-26 01:54:58 +03:00
v.sp++
2019-01-09 10:17:42 +03:00
case *objects.String:
numElements := int64(len(left.Value))
var highIdx int64
if high == objects.UndefinedValue {
highIdx = numElements
} else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
return
2019-01-09 10:17:42 +03:00
}
if lowIdx > highIdx {
v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
return
}
if lowIdx < 0 {
lowIdx = 0
} else if lowIdx > numElements {
lowIdx = numElements
}
if highIdx < 0 {
highIdx = 0
} else if highIdx > numElements {
highIdx = numElements
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
var val objects.Object = &objects.String{Value: left.Value[lowIdx:highIdx]}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = val
v.sp++
2019-01-18 12:43:46 +03:00
case *objects.Bytes:
numElements := int64(len(left.Value))
var highIdx int64
if high == objects.UndefinedValue {
2019-01-18 12:43:46 +03:00
highIdx = numElements
} else if high, ok := high.(*objects.Int); ok {
highIdx = high.Value
} else {
v.err = fmt.Errorf("invalid slice index type: %s", high.TypeName())
return
2019-01-18 12:43:46 +03:00
}
if lowIdx > highIdx {
v.err = fmt.Errorf("invalid slice index: %d > %d", lowIdx, highIdx)
return
2019-01-18 12:43:46 +03:00
}
if lowIdx < 0 {
lowIdx = 0
} else if lowIdx > numElements {
lowIdx = numElements
}
if highIdx < 0 {
highIdx = 0
} else if highIdx > numElements {
highIdx = numElements
}
2019-01-18 12:43:46 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-18 12:43:46 +03:00
}
var val objects.Object = &objects.Bytes{Value: left.Value[lowIdx:highIdx]}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = val
2019-01-18 12:43:46 +03:00
v.sp++
2019-01-09 10:17:42 +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
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
}
// test if it's tail-call
if callee.Fn == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
if nextOp == compiler.OpReturnValue ||
(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
}
}
// update call frame
v.curFrame.ip = v.ip // store current ip before call
v.curFrame = &(v.frames[v.framesIndex])
v.curFrame.fn = callee.Fn
v.curFrame.freeVars = callee.Free
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
}
// test if it's tail-call
if callee == v.curFrame.fn { // recursion
nextOp := v.curInsts[v.ip+1]
if nextOp == compiler.OpReturnValue ||
(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
}
}
// update call frame
v.curFrame.ip = v.ip // store current ip before call
v.curFrame = &(v.frames[v.framesIndex])
v.curFrame.fn = callee
v.curFrame.freeVars = nil
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)
}
ret, e := callee.Call(args...)
v.sp -= numArgs + 1
// 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())
return
}
if e, ok := e.(objects.ErrInvalidArgumentType); ok {
v.err = fmt.Errorf("invalid type for argument '%s' in call to '%s': expected %s, found %s",
e.Name, value.TypeName(), e.Expected, e.Found)
return
}
v.err = e
return
}
// nil return -> undefined
if ret == nil {
ret = objects.UndefinedValue
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = ret
v.sp++
default:
v.errOffset = 1
v.err = fmt.Errorf("not callable: %s", callee.TypeName())
return
2019-01-09 10:17:42 +03:00
}
2019-01-09 10:17:42 +03:00
case compiler.OpReturnValue:
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-20 18:41:57 +03:00
v.ip = v.curFrame.ip
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
// skip stack overflow check because (newSP) <= (oldSP)
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-20 18:41:57 +03:00
v.ip = v.curFrame.ip
2019-01-09 10:17:42 +03:00
2019-02-02 10:27:29 +03:00
//v.sp = lastFrame.basePointer - 1
v.sp = lastFrame.basePointer
2019-01-09 10:17:42 +03:00
// skip stack overflow check because (newSP) <= (oldSP)
v.stack[v.sp-1] = objects.UndefinedValue
2019-02-02 10:27:29 +03:00
//v.sp++
case compiler.OpDefineLocal:
2019-01-20 18:41:57 +03:00
localIndex := int(v.curInsts[v.ip+1])
v.ip++
2019-01-20 18:41:57 +03:00
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]
v.sp--
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
// 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--
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
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
// selectors and RHS value
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
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
if e := indexAssign(v.stack[sp], val, selectors); e != nil {
v.errOffset = 2
v.err = e
return
2019-01-09 10:17:42 +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
if obj, ok := val.(*objects.ObjectPtr); ok {
val = *obj.Value
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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:
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
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
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.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
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.ObjectPtr, numFree)
for i := 0; i < 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
}
var cl = &objects.Closure{
Fn: fn,
Free: free,
2019-01-09 10:17:42 +03:00
}
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp] = cl
v.sp++
case compiler.OpGetFreePtr:
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
val := v.curFrame.freeVars[freeIndex]
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = val
v.sp++
case compiler.OpGetFree:
freeIndex := int(v.curInsts[v.ip+1])
v.ip++
val := *v.curFrame.freeVars[freeIndex].Value
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = val
v.sp++
case compiler.OpSetFree:
freeIndex := int(v.curInsts[v.ip+1])
v.ip++
*v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
v.sp--
case compiler.OpGetLocalPtr:
localIndex := int(v.curInsts[v.ip+1])
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
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
v.stack[v.sp] = freeVar
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
// selectors and RHS value
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
2019-01-09 10:17:42 +03:00
if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil {
v.errOffset = 2
v.err = e
return
2019-01-09 10:17:42 +03:00
}
2019-01-09 10:17:42 +03:00
case compiler.OpIteratorInit:
var iterator objects.Object
dst := v.stack[v.sp-1]
v.sp--
iterable, ok := dst.(objects.Iterable)
2019-01-24 00:36:03 +03:00
if !ok {
v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
return
2019-01-09 10:17:42 +03:00
}
2019-01-24 00:36:03 +03:00
iterator = iterable.Iterate()
v.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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--
hasMore := iterator.(objects.Iterator).Next()
2019-01-24 00:36:03 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
}
2019-01-24 00:36:03 +03:00
if hasMore {
v.stack[v.sp] = objects.TrueValue
2019-01-09 10:17:42 +03:00
} else {
v.stack[v.sp] = objects.FalseValue
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--
val := iterator.(objects.Iterator).Key()
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
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--
val := iterator.(objects.Iterator).Value()
2019-01-09 10:17:42 +03:00
if v.sp >= StackSize {
v.err = ErrStackOverflow
return
2019-01-09 10:17:42 +03:00
}
v.stack[v.sp] = val
v.sp++
2019-01-09 10:17:42 +03:00
default:
panic(fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip]))
2019-01-09 10:17:42 +03:00
}
}
}
// Globals returns the global variables.
func (v *VM) Globals() []objects.Object {
2019-01-09 23:39:40 +03:00
return v.globals
}
func indexAssign(dst, src objects.Object, selectors []objects.Object) error {
2019-01-09 10:17:42 +03:00
numSel := len(selectors)
for sidx := numSel - 1; sidx > 0; sidx-- {
indexable, ok := dst.(objects.Indexable)
if !ok {
return fmt.Errorf("not indexable: %s", dst.TypeName())
}
2019-01-09 10:17:42 +03:00
next, err := indexable.IndexGet(selectors[sidx])
if err != nil {
if err == objects.ErrInvalidIndexType {
return fmt.Errorf("invalid index type: %s", selectors[sidx].TypeName())
}
return err
}
2019-01-09 10:17:42 +03:00
dst = next
}
2019-01-09 10:17:42 +03:00
indexAssignable, ok := dst.(objects.IndexAssignable)
if !ok {
return fmt.Errorf("not index-assignable: %s", dst.TypeName())
2019-01-09 10:17:42 +03:00
}
if err := indexAssignable.IndexSet(selectors[0], src); err != nil {
if err == objects.ErrInvalidIndexValueType {
return fmt.Errorf("invaid index value type: %s", src.TypeName())
}
return err
}
return nil
2019-01-09 10:17:42 +03:00
}
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.allocs--
if v.allocs == 0 {
v.err = ErrObjectAllocLimit
return
}
v.stack[v.sp-2] = res
v.sp--
}