summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/d5/tengo/runtime/vm.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/d5/tengo/runtime/vm.go')
-rw-r--r--vendor/github.com/d5/tengo/runtime/vm.go1092
1 files changed, 0 insertions, 1092 deletions
diff --git a/vendor/github.com/d5/tengo/runtime/vm.go b/vendor/github.com/d5/tengo/runtime/vm.go
deleted file mode 100644
index 07f6e530..00000000
--- a/vendor/github.com/d5/tengo/runtime/vm.go
+++ /dev/null
@@ -1,1092 +0,0 @@
-package runtime
-
-import (
- "fmt"
- "sync/atomic"
-
- "github.com/d5/tengo/compiler"
- "github.com/d5/tengo/compiler/source"
- "github.com/d5/tengo/compiler/token"
- "github.com/d5/tengo/objects"
-)
-
-const (
- // StackSize is the maximum stack size.
- StackSize = 2048
-
- // GlobalsSize is the maximum number of global variables.
- GlobalsSize = 1024
-
- // MaxFrames is the maximum number of function frames.
- MaxFrames = 1024
-)
-
-// VM is a virtual machine that executes the bytecode compiled by Compiler.
-type VM struct {
- constants []objects.Object
- stack [StackSize]objects.Object
- sp int
- globals []objects.Object
- fileSet *source.FileSet
- frames [MaxFrames]Frame
- framesIndex int
- curFrame *Frame
- curInsts []byte
- ip int
- aborting int64
- maxAllocs int64
- allocs int64
- err error
-}
-
-// NewVM creates a VM.
-func NewVM(bytecode *compiler.Bytecode, globals []objects.Object, maxAllocs int64) *VM {
- if globals == nil {
- globals = make([]objects.Object, GlobalsSize)
- }
-
- v := &VM{
- constants: bytecode.Constants,
- sp: 0,
- globals: globals,
- fileSet: bytecode.FileSet,
- framesIndex: 1,
- ip: -1,
- maxAllocs: maxAllocs,
- }
-
- v.frames[0].fn = bytecode.MainFunction
- v.frames[0].ip = -1
- v.curFrame = &v.frames[0]
- v.curInsts = v.curFrame.fn.Instructions
-
- return v
-}
-
-// Abort aborts the execution.
-func (v *VM) Abort() {
- atomic.StoreInt64(&v.aborting, 1)
-}
-
-// 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.framesIndex = 1
- v.ip = -1
- v.allocs = v.maxAllocs + 1
-
- v.run()
-
- atomic.StoreInt64(&v.aborting, 0)
-
- err = v.err
- if err != nil {
- filePos := v.fileSet.Position(v.curFrame.fn.SourcePos(v.ip - 1))
- err = fmt.Errorf("Runtime Error: %s\n\tat %s", err.Error(), filePos)
- for v.framesIndex > 1 {
- v.framesIndex--
- v.curFrame = &v.frames[v.framesIndex-1]
-
- filePos = v.fileSet.Position(v.curFrame.fn.SourcePos(v.curFrame.ip - 1))
- err = fmt.Errorf("%s\n\tat %s", err.Error(), filePos)
- }
- return err
- }
-
- return nil
-}
-
-func (v *VM) run() {
- defer func() {
- if r := recover(); r != nil {
- if v.sp >= StackSize || v.framesIndex >= MaxFrames {
- v.err = ErrStackOverflow
- return
- }
-
- if v.ip < len(v.curInsts)-1 {
- if err, ok := r.(error); ok {
- v.err = err
- } else {
- v.err = fmt.Errorf("panic: %v", r)
- }
- }
- }
- }()
-
- for atomic.LoadInt64(&v.aborting) == 0 {
- v.ip++
-
- switch v.curInsts[v.ip] {
- case compiler.OpConstant:
- v.ip += 2
- cidx := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
-
- v.stack[v.sp] = v.constants[cidx]
- v.sp++
-
- case compiler.OpNull:
- v.stack[v.sp] = objects.UndefinedValue
- v.sp++
-
- case compiler.OpBinaryOp:
- v.ip++
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
-
- tok := token.Token(v.curInsts[v.ip])
- res, e := left.BinaryOp(tok, right)
- if e != nil {
- v.sp -= 2
-
- if e == objects.ErrInvalidOperator {
- v.err = fmt.Errorf("invalid operation: %s %s %s",
- left.TypeName(), tok.String(), 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--
-
- case compiler.OpEqual:
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
-
- if left.Equals(right) {
- v.stack[v.sp] = objects.TrueValue
- } else {
- v.stack[v.sp] = objects.FalseValue
- }
- v.sp++
-
- case compiler.OpNotEqual:
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
-
- if left.Equals(right) {
- v.stack[v.sp] = objects.FalseValue
- } else {
- v.stack[v.sp] = objects.TrueValue
- }
- v.sp++
-
- case compiler.OpPop:
- v.sp--
-
- case compiler.OpTrue:
- v.stack[v.sp] = objects.TrueValue
- v.sp++
-
- case compiler.OpFalse:
- v.stack[v.sp] = objects.FalseValue
- v.sp++
-
- case compiler.OpLNot:
- operand := v.stack[v.sp-1]
- v.sp--
-
- if operand.IsFalsy() {
- v.stack[v.sp] = objects.TrueValue
- } else {
- v.stack[v.sp] = objects.FalseValue
- }
- v.sp++
-
- case compiler.OpBComplement:
- operand := v.stack[v.sp-1]
- v.sp--
-
- switch x := operand.(type) {
- case *objects.Int:
- 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++
- default:
- v.err = fmt.Errorf("invalid operation: ^%s", operand.TypeName())
- return
- }
-
- case compiler.OpMinus:
- operand := v.stack[v.sp-1]
- v.sp--
-
- switch x := operand.(type) {
- case *objects.Int:
- 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++
- case *objects.Float:
- 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++
- default:
- v.err = fmt.Errorf("invalid operation: -%s", operand.TypeName())
- return
- }
-
- case compiler.OpJumpFalsy:
- v.ip += 2
- v.sp--
- if v.stack[v.sp].IsFalsy() {
- pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- v.ip = pos - 1
- }
-
- case compiler.OpAndJump:
- v.ip += 2
-
- if v.stack[v.sp-1].IsFalsy() {
- pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- v.ip = pos - 1
- } else {
- v.sp--
- }
-
- case compiler.OpOrJump:
- v.ip += 2
-
- if v.stack[v.sp-1].IsFalsy() {
- v.sp--
- } else {
- pos := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- v.ip = pos - 1
- }
-
- case compiler.OpJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- v.ip = pos - 1
-
- case compiler.OpSetGlobal:
- v.ip += 2
- v.sp--
-
- globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- v.globals[globalIndex] = v.stack[v.sp]
-
- case compiler.OpSetSelGlobal:
- v.ip += 3
- globalIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
- numSelectors := int(v.curInsts[v.ip])
-
- // selectors and RHS value
- selectors := make([]objects.Object, numSelectors)
- for i := 0; i < numSelectors; i++ {
- selectors[i] = v.stack[v.sp-numSelectors+i]
- }
-
- val := v.stack[v.sp-numSelectors-1]
- v.sp -= numSelectors + 1
-
- if e := indexAssign(v.globals[globalIndex], val, selectors); e != nil {
- v.err = e
- return
- }
-
- case compiler.OpGetGlobal:
- v.ip += 2
- globalIndex := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
-
- val := v.globals[globalIndex]
-
- v.stack[v.sp] = val
- v.sp++
-
- case compiler.OpArray:
- v.ip += 2
- numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
-
- var elements []objects.Object
- for i := v.sp - numElements; i < v.sp; i++ {
- elements = append(elements, v.stack[i])
- }
- v.sp -= numElements
-
- var arr objects.Object = &objects.Array{Value: elements}
-
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
-
- v.stack[v.sp] = arr
- v.sp++
-
- case compiler.OpMap:
- v.ip += 2
- numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
-
- kv := make(map[string]objects.Object)
- for i := v.sp - numElements; i < v.sp; i += 2 {
- key := v.stack[i]
- value := v.stack[i+1]
- kv[key.(*objects.String).Value] = value
- }
- v.sp -= numElements
-
- var m objects.Object = &objects.Map{Value: kv}
-
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
-
- 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
-
- case compiler.OpImmutable:
- value := v.stack[v.sp-1]
-
- switch value := value.(type) {
- 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
- 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
- }
-
- case compiler.OpIndex:
- index := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
-
- 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
- }
- if val == nil {
- val = objects.UndefinedValue
- }
-
- v.stack[v.sp] = val
- 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
- }
-
- v.stack[v.sp] = left.Value
- v.sp++
-
- default:
- v.err = fmt.Errorf("not indexable: %s", left.TypeName())
- return
- }
-
- 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
- }
- }
-
- switch left := left.(type) {
- 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
- }
-
- 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++
-
- case *objects.ImmutableArray:
- 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
- }
-
- 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++
-
- 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
- }
-
- 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
- }
-
- 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++
-
- case *objects.Bytes:
- 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
- }
-
- 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
- v.sp++
- }
-
- case compiler.OpCall:
- numArgs := int(v.curInsts[v.ip+1])
- v.ip++
-
- value := v.stack[v.sp-1-numArgs]
-
- switch callee := value.(type) {
- case *objects.Closure:
- if callee.Fn.VarArgs {
- // if the closure is variadic,
- // roll up all variadic parameters into an array
- realArgs := callee.Fn.NumParameters - 1
- varArgs := numArgs - realArgs
- if varArgs >= 0 {
- numArgs = realArgs + 1
- args := make([]objects.Object, varArgs)
- spStart := v.sp - varArgs
- for i := spStart; i < v.sp; i++ {
- args[i-spStart] = v.stack[i]
- }
- v.stack[spStart] = &objects.Array{Value: args}
- v.sp = spStart + 1
- }
- }
-
- if numArgs != callee.Fn.NumParameters {
- if callee.Fn.VarArgs {
- v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d",
- callee.Fn.NumParameters-1, numArgs)
- } else {
- 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.OpReturn ||
- (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
- for p := 0; p < numArgs; p++ {
- v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
- }
- v.sp -= numArgs + 1
- v.ip = -1 // reset IP to beginning of the frame
- continue
- }
- }
-
- // 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.framesIndex++
- v.sp = v.sp - numArgs + callee.Fn.NumLocals
-
- case *objects.CompiledFunction:
- if callee.VarArgs {
- // if the closure is variadic,
- // roll up all variadic parameters into an array
- realArgs := callee.NumParameters - 1
- varArgs := numArgs - realArgs
- if varArgs >= 0 {
- numArgs = realArgs + 1
- args := make([]objects.Object, varArgs)
- spStart := v.sp - varArgs
- for i := spStart; i < v.sp; i++ {
- args[i-spStart] = v.stack[i]
- }
- v.stack[spStart] = &objects.Array{Value: args}
- v.sp = spStart + 1
- }
- }
-
- if numArgs != callee.NumParameters {
- if callee.VarArgs {
- v.err = fmt.Errorf("wrong number of arguments: want>=%d, got=%d",
- callee.NumParameters-1, numArgs)
- } else {
- 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.OpReturn ||
- (nextOp == compiler.OpPop && compiler.OpReturn == v.curInsts[v.ip+2]) {
- for p := 0; p < numArgs; p++ {
- v.stack[v.curFrame.basePointer+p] = v.stack[v.sp-numArgs+p]
- }
- v.sp -= numArgs + 1
- v.ip = -1 // reset IP to beginning of the frame
- continue
- }
- }
-
- // 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.framesIndex++
- v.sp = v.sp - numArgs + callee.NumLocals
-
- case objects.Callable:
- var args []objects.Object
- args = append(args, v.stack[v.sp-numArgs:v.sp]...)
-
- ret, e := callee.Call(args...)
- v.sp -= numArgs + 1
-
- // runtime error
- if e != nil {
- 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
- }
-
- v.stack[v.sp] = ret
- v.sp++
-
- default:
- v.err = fmt.Errorf("not callable: %s", callee.TypeName())
- return
- }
-
- case compiler.OpReturn:
- v.ip++
- var retVal objects.Object
- if int(v.curInsts[v.ip]) == 1 {
- retVal = v.stack[v.sp-1]
- } else {
- retVal = objects.UndefinedValue
- }
- //v.sp--
-
- v.framesIndex--
- v.curFrame = &v.frames[v.framesIndex-1]
- v.curInsts = v.curFrame.fn.Instructions
- v.ip = v.curFrame.ip
-
- //v.sp = lastFrame.basePointer - 1
- v.sp = v.frames[v.framesIndex].basePointer
-
- // skip stack overflow check because (newSP) <= (oldSP)
- v.stack[v.sp-1] = retVal
- //v.sp++
-
- case compiler.OpDefineLocal:
- v.ip++
- localIndex := int(v.curInsts[v.ip])
-
- sp := v.curFrame.basePointer + localIndex
-
- // local variables can be mutated by other actions
- // so always store the copy of popped value
- val := v.stack[v.sp-1]
- v.sp--
-
- v.stack[sp] = val
-
- case compiler.OpSetLocal:
- localIndex := int(v.curInsts[v.ip+1])
- v.ip++
-
- 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
-
- case compiler.OpSetSelLocal:
- localIndex := int(v.curInsts[v.ip+1])
- numSelectors := int(v.curInsts[v.ip+2])
- v.ip += 2
-
- // 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
-
- dst := v.stack[v.curFrame.basePointer+localIndex]
- if obj, ok := dst.(*objects.ObjectPtr); ok {
- dst = *obj.Value
- }
-
- if e := indexAssign(dst, val, selectors); e != nil {
- v.err = e
- return
- }
-
- case compiler.OpGetLocal:
- v.ip++
- localIndex := int(v.curInsts[v.ip])
-
- val := v.stack[v.curFrame.basePointer+localIndex]
-
- if obj, ok := val.(*objects.ObjectPtr); ok {
- val = *obj.Value
- }
-
- v.stack[v.sp] = val
- v.sp++
-
- case compiler.OpGetBuiltin:
- v.ip++
- builtinIndex := int(v.curInsts[v.ip])
-
- v.stack[v.sp] = objects.Builtins[builtinIndex]
- v.sp++
-
- case compiler.OpClosure:
- v.ip += 3
- constIndex := int(v.curInsts[v.ip-1]) | int(v.curInsts[v.ip-2])<<8
- numFree := int(v.curInsts[v.ip])
-
- fn, ok := v.constants[constIndex].(*objects.CompiledFunction)
- if !ok {
- v.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
-
- var cl = &objects.Closure{
- Fn: fn,
- Free: free,
- }
-
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
-
- v.stack[v.sp] = cl
- v.sp++
-
- case compiler.OpGetFreePtr:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
-
- val := v.curFrame.freeVars[freeIndex]
-
- v.stack[v.sp] = val
- v.sp++
-
- case compiler.OpGetFree:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
-
- val := *v.curFrame.freeVars[freeIndex].Value
-
- v.stack[v.sp] = val
- v.sp++
-
- case compiler.OpSetFree:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
-
- *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
-
- v.sp--
-
- case compiler.OpGetLocalPtr:
- v.ip++
- localIndex := int(v.curInsts[v.ip])
-
- sp := v.curFrame.basePointer + localIndex
- val := v.stack[sp]
-
- var freeVar *objects.ObjectPtr
- if obj, ok := val.(*objects.ObjectPtr); ok {
- freeVar = obj
- } else {
- freeVar = &objects.ObjectPtr{Value: &val}
- v.stack[sp] = freeVar
- }
-
- v.stack[v.sp] = freeVar
- v.sp++
-
- case compiler.OpSetSelFree:
- v.ip += 2
- freeIndex := int(v.curInsts[v.ip-1])
- numSelectors := int(v.curInsts[v.ip])
-
- // 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
-
- if e := indexAssign(*v.curFrame.freeVars[freeIndex].Value, val, selectors); e != nil {
- v.err = e
- return
- }
-
- case compiler.OpIteratorInit:
- var iterator objects.Object
-
- dst := v.stack[v.sp-1]
- v.sp--
-
- iterable, ok := dst.(objects.Iterable)
- if !ok {
- v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
- return
- }
-
- iterator = iterable.Iterate()
-
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
-
- v.stack[v.sp] = iterator
- v.sp++
-
- case compiler.OpIteratorNext:
- iterator := v.stack[v.sp-1]
- v.sp--
-
- hasMore := iterator.(objects.Iterator).Next()
-
- if hasMore {
- v.stack[v.sp] = objects.TrueValue
- } else {
- v.stack[v.sp] = objects.FalseValue
- }
- v.sp++
-
- case compiler.OpIteratorKey:
- iterator := v.stack[v.sp-1]
- v.sp--
-
- val := iterator.(objects.Iterator).Key()
-
- v.stack[v.sp] = val
- v.sp++
-
- case compiler.OpIteratorValue:
- iterator := v.stack[v.sp-1]
- v.sp--
-
- val := iterator.(objects.Iterator).Value()
-
- v.stack[v.sp] = val
- v.sp++
-
- default:
- v.err = fmt.Errorf("unknown opcode: %d", v.curInsts[v.ip])
- return
- }
- }
-}
-
-// IsStackEmpty tests if the stack is empty or not.
-func (v *VM) IsStackEmpty() bool {
- return v.sp == 0
-}
-
-func indexAssign(dst, src objects.Object, selectors []objects.Object) error {
- 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())
- }
-
- 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
- }
-
- dst = next
- }
-
- indexAssignable, ok := dst.(objects.IndexAssignable)
- if !ok {
- return fmt.Errorf("not index-assignable: %s", dst.TypeName())
- }
-
- 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
-}