123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908 |
- package tengo
- import (
- "fmt"
- "sync/atomic"
- "github.com/d5/tengo/v2/parser"
- "github.com/d5/tengo/v2/token"
- )
- // frame represents a function call frame.
- type frame struct {
- fn *CompiledFunction
- freeVars []*ObjectPtr
- ip int
- basePointer int
- }
- // VM is a virtual machine that executes the bytecode compiled by Compiler.
- type VM struct {
- constants []Object
- stack [StackSize]Object
- sp int
- globals []Object
- fileSet *parser.SourceFileSet
- 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 *Bytecode,
- globals []Object,
- maxAllocs int64,
- ) *VM {
- if globals == nil {
- globals = make([]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: %w\n\tat %s",
- err, 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("%w\n\tat %s", err, filePos)
- }
- return err
- }
- return nil
- }
- func (v *VM) run() {
- for atomic.LoadInt64(&v.aborting) == 0 {
- v.ip++
- switch v.curInsts[v.ip] {
- case parser.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 parser.OpNull:
- v.stack[v.sp] = UndefinedValue
- v.sp++
- case parser.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 == 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 parser.OpEqual:
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
- if left.Equals(right) {
- v.stack[v.sp] = TrueValue
- } else {
- v.stack[v.sp] = FalseValue
- }
- v.sp++
- case parser.OpNotEqual:
- right := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
- if left.Equals(right) {
- v.stack[v.sp] = FalseValue
- } else {
- v.stack[v.sp] = TrueValue
- }
- v.sp++
- case parser.OpPop:
- v.sp--
- case parser.OpTrue:
- v.stack[v.sp] = TrueValue
- v.sp++
- case parser.OpFalse:
- v.stack[v.sp] = FalseValue
- v.sp++
- case parser.OpLNot:
- operand := v.stack[v.sp-1]
- v.sp--
- if operand.IsFalsy() {
- v.stack[v.sp] = TrueValue
- } else {
- v.stack[v.sp] = FalseValue
- }
- v.sp++
- case parser.OpBComplement:
- operand := v.stack[v.sp-1]
- v.sp--
- switch x := operand.(type) {
- case *Int:
- var res Object = &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 parser.OpMinus:
- operand := v.stack[v.sp-1]
- v.sp--
- switch x := operand.(type) {
- case *Int:
- var res Object = &Int{Value: -x.Value}
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = res
- v.sp++
- case *Float:
- var res Object = &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 parser.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 parser.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 parser.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 parser.OpJump:
- pos := int(v.curInsts[v.ip+2]) | int(v.curInsts[v.ip+1])<<8
- v.ip = pos - 1
- case parser.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 parser.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([]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
- e := indexAssign(v.globals[globalIndex], val, selectors)
- if e != nil {
- v.err = e
- return
- }
- case parser.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 parser.OpArray:
- v.ip += 2
- numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- var elements []Object
- for i := v.sp - numElements; i < v.sp; i++ {
- elements = append(elements, v.stack[i])
- }
- v.sp -= numElements
- var arr Object = &Array{Value: elements}
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = arr
- v.sp++
- case parser.OpMap:
- v.ip += 2
- numElements := int(v.curInsts[v.ip]) | int(v.curInsts[v.ip-1])<<8
- kv := make(map[string]Object, numElements)
- for i := v.sp - numElements; i < v.sp; i += 2 {
- key := v.stack[i]
- value := v.stack[i+1]
- kv[key.(*String).Value] = value
- }
- v.sp -= numElements
- var m Object = &Map{Value: kv}
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = m
- v.sp++
- case parser.OpError:
- value := v.stack[v.sp-1]
- var e Object = &Error{
- Value: value,
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp-1] = e
- case parser.OpImmutable:
- value := v.stack[v.sp-1]
- switch value := value.(type) {
- case *Array:
- var immutableArray Object = &ImmutableArray{
- Value: value.Value,
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp-1] = immutableArray
- case *Map:
- var immutableMap Object = &ImmutableMap{
- Value: value.Value,
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp-1] = immutableMap
- }
- case parser.OpIndex:
- index := v.stack[v.sp-1]
- left := v.stack[v.sp-2]
- v.sp -= 2
- val, err := left.IndexGet(index)
- if err != nil {
- if err == ErrNotIndexable {
- v.err = fmt.Errorf("not indexable: %s", index.TypeName())
- return
- }
- if err == ErrInvalidIndexType {
- v.err = fmt.Errorf("invalid index type: %s",
- index.TypeName())
- return
- }
- v.err = err
- return
- }
- if val == nil {
- val = UndefinedValue
- }
- v.stack[v.sp] = val
- v.sp++
- case parser.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 != UndefinedValue {
- if lowInt, ok := low.(*Int); ok {
- lowIdx = lowInt.Value
- } else {
- v.err = fmt.Errorf("invalid slice index type: %s",
- low.TypeName())
- return
- }
- }
- switch left := left.(type) {
- case *Array:
- numElements := int64(len(left.Value))
- var highIdx int64
- if high == UndefinedValue {
- highIdx = numElements
- } else if highInt, ok := high.(*Int); ok {
- highIdx = highInt.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 Object = &Array{
- Value: left.Value[lowIdx:highIdx],
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = val
- v.sp++
- case *ImmutableArray:
- numElements := int64(len(left.Value))
- var highIdx int64
- if high == UndefinedValue {
- highIdx = numElements
- } else if highInt, ok := high.(*Int); ok {
- highIdx = highInt.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 Object = &Array{
- Value: left.Value[lowIdx:highIdx],
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = val
- v.sp++
- case *String:
- numElements := int64(len(left.Value))
- var highIdx int64
- if high == UndefinedValue {
- highIdx = numElements
- } else if highInt, ok := high.(*Int); ok {
- highIdx = highInt.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 Object = &String{
- Value: left.Value[lowIdx:highIdx],
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = val
- v.sp++
- case *Bytes:
- numElements := int64(len(left.Value))
- var highIdx int64
- if high == UndefinedValue {
- highIdx = numElements
- } else if highInt, ok := high.(*Int); ok {
- highIdx = highInt.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 Object = &Bytes{
- Value: left.Value[lowIdx:highIdx],
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = val
- v.sp++
- }
- case parser.OpCall:
- numArgs := int(v.curInsts[v.ip+1])
- spread := int(v.curInsts[v.ip+2])
- v.ip += 2
- value := v.stack[v.sp-1-numArgs]
- if !value.CanCall() {
- v.err = fmt.Errorf("not callable: %s", value.TypeName())
- return
- }
- if spread == 1 {
- v.sp--
- switch arr := v.stack[v.sp].(type) {
- case *Array:
- for _, item := range arr.Value {
- v.stack[v.sp] = item
- v.sp++
- }
- numArgs += len(arr.Value) - 1
- case *ImmutableArray:
- for _, item := range arr.Value {
- v.stack[v.sp] = item
- v.sp++
- }
- numArgs += len(arr.Value) - 1
- default:
- v.err = fmt.Errorf("not an array: %s", arr.TypeName())
- return
- }
- }
- if callee, ok := value.(*CompiledFunction); ok {
- 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([]Object, varArgs)
- spStart := v.sp - varArgs
- for i := spStart; i < v.sp; i++ {
- args[i-spStart] = v.stack[i]
- }
- v.stack[spStart] = &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 == parser.OpReturn ||
- (nextOp == parser.OpPop &&
- parser.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
- }
- }
- if v.framesIndex >= MaxFrames {
- v.err = ErrStackOverflow
- return
- }
- // 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 = callee.Free
- v.curFrame.basePointer = v.sp - numArgs
- v.curInsts = callee.Instructions
- v.ip = -1
- v.framesIndex++
- v.sp = v.sp - numArgs + callee.NumLocals
- } else {
- var args []Object
- args = append(args, v.stack[v.sp-numArgs:v.sp]...)
- ret, e := value.Call(args...)
- v.sp -= numArgs + 1
- // runtime error
- if e != nil {
- if e == ErrWrongNumArguments {
- v.err = fmt.Errorf(
- "wrong number of arguments in call to '%s'",
- value.TypeName())
- return
- }
- if e, ok := e.(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 = UndefinedValue
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = ret
- v.sp++
- }
- case parser.OpReturn:
- v.ip++
- var retVal Object
- if int(v.curInsts[v.ip]) == 1 {
- retVal = v.stack[v.sp-1]
- } else {
- retVal = 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 parser.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 parser.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].(*ObjectPtr); ok {
- *obj.Value = val
- val = obj
- }
- v.stack[sp] = val // also use a copy of popped value
- case parser.OpSetSelLocal:
- localIndex := int(v.curInsts[v.ip+1])
- numSelectors := int(v.curInsts[v.ip+2])
- v.ip += 2
- // selectors and RHS value
- selectors := make([]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.(*ObjectPtr); ok {
- dst = *obj.Value
- }
- if e := indexAssign(dst, val, selectors); e != nil {
- v.err = e
- return
- }
- case parser.OpGetLocal:
- v.ip++
- localIndex := int(v.curInsts[v.ip])
- val := v.stack[v.curFrame.basePointer+localIndex]
- if obj, ok := val.(*ObjectPtr); ok {
- val = *obj.Value
- }
- v.stack[v.sp] = val
- v.sp++
- case parser.OpGetBuiltin:
- v.ip++
- builtinIndex := int(v.curInsts[v.ip])
- v.stack[v.sp] = builtinFuncs[builtinIndex]
- v.sp++
- case parser.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].(*CompiledFunction)
- if !ok {
- v.err = fmt.Errorf("not function: %s", fn.TypeName())
- return
- }
- free := make([]*ObjectPtr, numFree)
- for i := 0; i < numFree; i++ {
- switch freeVar := (v.stack[v.sp-numFree+i]).(type) {
- case *ObjectPtr:
- free[i] = freeVar
- default:
- free[i] = &ObjectPtr{
- Value: &v.stack[v.sp-numFree+i],
- }
- }
- }
- v.sp -= numFree
- cl := &CompiledFunction{
- Instructions: fn.Instructions,
- NumLocals: fn.NumLocals,
- NumParameters: fn.NumParameters,
- VarArgs: fn.VarArgs,
- SourceMap: fn.SourceMap,
- Free: free,
- }
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = cl
- v.sp++
- case parser.OpGetFreePtr:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
- val := v.curFrame.freeVars[freeIndex]
- v.stack[v.sp] = val
- v.sp++
- case parser.OpGetFree:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
- val := *v.curFrame.freeVars[freeIndex].Value
- v.stack[v.sp] = val
- v.sp++
- case parser.OpSetFree:
- v.ip++
- freeIndex := int(v.curInsts[v.ip])
- *v.curFrame.freeVars[freeIndex].Value = v.stack[v.sp-1]
- v.sp--
- case parser.OpGetLocalPtr:
- v.ip++
- localIndex := int(v.curInsts[v.ip])
- sp := v.curFrame.basePointer + localIndex
- val := v.stack[sp]
- var freeVar *ObjectPtr
- if obj, ok := val.(*ObjectPtr); ok {
- freeVar = obj
- } else {
- freeVar = &ObjectPtr{Value: &val}
- v.stack[sp] = freeVar
- }
- v.stack[v.sp] = freeVar
- v.sp++
- case parser.OpSetSelFree:
- v.ip += 2
- freeIndex := int(v.curInsts[v.ip-1])
- numSelectors := int(v.curInsts[v.ip])
- // selectors and RHS value
- selectors := make([]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
- e := indexAssign(*v.curFrame.freeVars[freeIndex].Value,
- val, selectors)
- if e != nil {
- v.err = e
- return
- }
- case parser.OpIteratorInit:
- var iterator Object
- dst := v.stack[v.sp-1]
- v.sp--
- if !dst.CanIterate() {
- v.err = fmt.Errorf("not iterable: %s", dst.TypeName())
- return
- }
- iterator = dst.Iterate()
- v.allocs--
- if v.allocs == 0 {
- v.err = ErrObjectAllocLimit
- return
- }
- v.stack[v.sp] = iterator
- v.sp++
- case parser.OpIteratorNext:
- iterator := v.stack[v.sp-1]
- v.sp--
- hasMore := iterator.(Iterator).Next()
- if hasMore {
- v.stack[v.sp] = TrueValue
- } else {
- v.stack[v.sp] = FalseValue
- }
- v.sp++
- case parser.OpIteratorKey:
- iterator := v.stack[v.sp-1]
- v.sp--
- val := iterator.(Iterator).Key()
- v.stack[v.sp] = val
- v.sp++
- case parser.OpIteratorValue:
- iterator := v.stack[v.sp-1]
- v.sp--
- val := iterator.(Iterator).Value()
- v.stack[v.sp] = val
- v.sp++
- case parser.OpSuspend:
- return
- 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 Object, selectors []Object) error {
- numSel := len(selectors)
- for sidx := numSel - 1; sidx > 0; sidx-- {
- next, err := dst.IndexGet(selectors[sidx])
- if err != nil {
- if err == ErrNotIndexable {
- return fmt.Errorf("not indexable: %s", dst.TypeName())
- }
- if err == ErrInvalidIndexType {
- return fmt.Errorf("invalid index type: %s",
- selectors[sidx].TypeName())
- }
- return err
- }
- dst = next
- }
- if err := dst.IndexSet(selectors[0], src); err != nil {
- if err == ErrNotIndexAssignable {
- return fmt.Errorf("not index-assignable: %s", dst.TypeName())
- }
- if err == ErrInvalidIndexValueType {
- return fmt.Errorf("invaid index value type: %s", src.TypeName())
- }
- return err
- }
- return nil
- }
|