|
|
@@ -0,0 +1,2497 @@
|
|
|
+package goja
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "math"
|
|
|
+ "strconv"
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ maxInt = 1 << 53
|
|
|
+)
|
|
|
+
|
|
|
+type valueStack []Value
|
|
|
+
|
|
|
+type stash struct {
|
|
|
+ values valueStack
|
|
|
+ extraArgs valueStack
|
|
|
+ names map[string]uint32
|
|
|
+ obj objectImpl
|
|
|
+
|
|
|
+ outer *stash
|
|
|
+}
|
|
|
+
|
|
|
+type context struct {
|
|
|
+ prg *Program
|
|
|
+ funcName string
|
|
|
+ stash *stash
|
|
|
+ pc, sb int
|
|
|
+ args int
|
|
|
+}
|
|
|
+
|
|
|
+type iterStackItem struct {
|
|
|
+ val Value
|
|
|
+ f iterNextFunc
|
|
|
+}
|
|
|
+
|
|
|
+type ref interface {
|
|
|
+ get() Value
|
|
|
+ set(Value)
|
|
|
+ refname() string
|
|
|
+}
|
|
|
+
|
|
|
+type stashRef struct {
|
|
|
+ v *Value
|
|
|
+ n string
|
|
|
+}
|
|
|
+
|
|
|
+func (r stashRef) get() Value {
|
|
|
+ return *r.v
|
|
|
+}
|
|
|
+
|
|
|
+func (r *stashRef) set(v Value) {
|
|
|
+ *r.v = v
|
|
|
+}
|
|
|
+
|
|
|
+func (r *stashRef) refname() string {
|
|
|
+ return r.n
|
|
|
+}
|
|
|
+
|
|
|
+type objRef struct {
|
|
|
+ base objectImpl
|
|
|
+ name string
|
|
|
+ strict bool
|
|
|
+}
|
|
|
+
|
|
|
+func (r *objRef) get() Value {
|
|
|
+ return r.base.getStr(r.name)
|
|
|
+}
|
|
|
+
|
|
|
+func (r *objRef) set(v Value) {
|
|
|
+ r.base.putStr(r.name, v, r.strict)
|
|
|
+}
|
|
|
+
|
|
|
+func (r *objRef) refname() string {
|
|
|
+ return r.name
|
|
|
+}
|
|
|
+
|
|
|
+type unresolvedRef struct {
|
|
|
+ runtime *Runtime
|
|
|
+ name string
|
|
|
+}
|
|
|
+
|
|
|
+func (r *unresolvedRef) get() Value {
|
|
|
+ r.runtime.throwReferenceError(r.name)
|
|
|
+ panic("Unreachable")
|
|
|
+}
|
|
|
+
|
|
|
+func (r *unresolvedRef) set(v Value) {
|
|
|
+ r.get()
|
|
|
+}
|
|
|
+
|
|
|
+func (r *unresolvedRef) refname() string {
|
|
|
+ return r.name
|
|
|
+}
|
|
|
+
|
|
|
+type vm struct {
|
|
|
+ r *Runtime
|
|
|
+ prg *Program
|
|
|
+ funcName string
|
|
|
+ pc int
|
|
|
+ stack valueStack
|
|
|
+ sp, sb, args int
|
|
|
+
|
|
|
+ stash *stash
|
|
|
+ callStack []context
|
|
|
+ iterStack []iterStackItem
|
|
|
+ refStack []ref
|
|
|
+
|
|
|
+ stashAllocs int
|
|
|
+ halt bool
|
|
|
+
|
|
|
+ interrupt bool
|
|
|
+ interruptVal interface{}
|
|
|
+ interruptLock sync.Mutex
|
|
|
+}
|
|
|
+
|
|
|
+type instruction interface {
|
|
|
+ exec(*vm)
|
|
|
+}
|
|
|
+
|
|
|
+func intToValue(i int64) Value {
|
|
|
+ if i >= -maxInt && i <= maxInt {
|
|
|
+ if i >= -128 && i <= 127 {
|
|
|
+ return intCache[i+128]
|
|
|
+ }
|
|
|
+ return valueInt(i)
|
|
|
+ }
|
|
|
+ return valueFloat(float64(i))
|
|
|
+}
|
|
|
+
|
|
|
+func floatToInt(f float64) (result int64, ok bool) {
|
|
|
+ if (f != 0 || !math.Signbit(f)) && !math.IsInf(f, 0) && f == math.Trunc(f) && f >= -maxInt && f <= maxInt {
|
|
|
+ return int64(f), true
|
|
|
+ }
|
|
|
+ return 0, false
|
|
|
+}
|
|
|
+
|
|
|
+func floatToValue(f float64) (result Value) {
|
|
|
+ if i, ok := floatToInt(f); ok {
|
|
|
+ return intToValue(i)
|
|
|
+ }
|
|
|
+ switch {
|
|
|
+ case f == 0:
|
|
|
+ return _negativeZero
|
|
|
+ case math.IsNaN(f):
|
|
|
+ return _NaN
|
|
|
+ case math.IsInf(f, 1):
|
|
|
+ return _positiveInf
|
|
|
+ case math.IsInf(f, -1):
|
|
|
+ return _negativeInf
|
|
|
+ }
|
|
|
+ return valueFloat(f)
|
|
|
+}
|
|
|
+
|
|
|
+func toInt(v Value) (int64, bool) {
|
|
|
+ num := v.ToNumber()
|
|
|
+ if i, ok := num.assertInt(); ok {
|
|
|
+ return i, true
|
|
|
+ }
|
|
|
+ if f, ok := num.assertFloat(); ok {
|
|
|
+ if i, ok := floatToInt(f); ok {
|
|
|
+ return i, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0, false
|
|
|
+}
|
|
|
+
|
|
|
+func toIntIgnoreNegZero(v Value) (int64, bool) {
|
|
|
+ num := v.ToNumber()
|
|
|
+ if i, ok := num.assertInt(); ok {
|
|
|
+ return i, true
|
|
|
+ }
|
|
|
+ if f, ok := num.assertFloat(); ok {
|
|
|
+ if v == _negativeZero {
|
|
|
+ return 0, true
|
|
|
+ }
|
|
|
+ if i, ok := floatToInt(f); ok {
|
|
|
+ return i, true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0, false
|
|
|
+}
|
|
|
+
|
|
|
+func (s *valueStack) expand(idx int) {
|
|
|
+ if idx < len(*s) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if idx < cap(*s) {
|
|
|
+ *s = (*s)[:idx+1]
|
|
|
+ } else {
|
|
|
+ n := make([]Value, idx+1, (idx+1)<<1)
|
|
|
+ copy(n, *s)
|
|
|
+ *s = n
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) put(name string, v Value) bool {
|
|
|
+ if s.obj != nil {
|
|
|
+ if found := s.obj.getStr(name); found != nil {
|
|
|
+ s.obj.putStr(name, v, false)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ } else {
|
|
|
+ if idx, found := s.names[name]; found {
|
|
|
+ s.values.expand(int(idx))
|
|
|
+ s.values[idx] = v
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) putByIdx(idx uint32, v Value) {
|
|
|
+ if s.obj != nil {
|
|
|
+ panic("Attempt to put by idx into an object scope")
|
|
|
+ }
|
|
|
+ s.values.expand(int(idx))
|
|
|
+ s.values[idx] = v
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) getByIdx(idx uint32) Value {
|
|
|
+ if int(idx) < len(s.values) {
|
|
|
+ return s.values[idx]
|
|
|
+ }
|
|
|
+ return _undefined
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) getByName(name string, vm *vm) (v Value, exists bool) {
|
|
|
+ if s.obj != nil {
|
|
|
+ v = s.obj.getStr(name)
|
|
|
+ if v == nil {
|
|
|
+ return nil, false
|
|
|
+ //return valueUnresolved{r: vm.r, ref: name}, false
|
|
|
+ }
|
|
|
+ return v, true
|
|
|
+ }
|
|
|
+ if idx, exists := s.names[name]; exists {
|
|
|
+ return s.values[idx], true
|
|
|
+ }
|
|
|
+ return nil, false
|
|
|
+ //return valueUnresolved{r: vm.r, ref: name}, false
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) createBinding(name string) {
|
|
|
+ if s.names == nil {
|
|
|
+ s.names = make(map[string]uint32)
|
|
|
+ }
|
|
|
+ s.names[name] = uint32(len(s.values))
|
|
|
+ s.values = append(s.values, _undefined)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *stash) deleteBinding(name string) bool {
|
|
|
+ if s.obj != nil {
|
|
|
+ return s.obj.deleteStr(name, false)
|
|
|
+ }
|
|
|
+ if idx, found := s.names[name]; found {
|
|
|
+ s.values[idx] = nil
|
|
|
+ delete(s.names, name)
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) newStash() {
|
|
|
+ vm.stash = &stash{
|
|
|
+ outer: vm.stash,
|
|
|
+ }
|
|
|
+ vm.stashAllocs++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) init() {
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) run() {
|
|
|
+ vm.halt = false
|
|
|
+ for !vm.halt && !vm.interrupt {
|
|
|
+ vm.prg.code[vm.pc].exec(vm)
|
|
|
+ }
|
|
|
+
|
|
|
+ if vm.interrupt {
|
|
|
+ vm.interruptLock.Lock()
|
|
|
+ v := &InterruptedError{
|
|
|
+ iface: vm.interruptVal,
|
|
|
+ }
|
|
|
+ vm.interrupt = false
|
|
|
+ vm.interruptVal = nil
|
|
|
+ vm.interruptLock.Unlock()
|
|
|
+ panic(v)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) Interrupt(v interface{}) {
|
|
|
+ vm.interruptLock.Lock()
|
|
|
+ vm.interruptVal = v
|
|
|
+ vm.interrupt = true
|
|
|
+ vm.interruptLock.Unlock()
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) captureStack(stack []stackFrame, ctxOffset int) []stackFrame {
|
|
|
+ // Unroll the context stack
|
|
|
+ stack = append(stack, stackFrame{prg: vm.prg, pc: vm.pc, funcName: vm.funcName})
|
|
|
+ for i := len(vm.callStack) - 1; i > ctxOffset-1; i-- {
|
|
|
+ if vm.callStack[i].pc != -1 {
|
|
|
+ stack = append(stack, stackFrame{prg: vm.callStack[i].prg, pc: vm.callStack[i].pc - 1, funcName: vm.callStack[i].funcName})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return stack
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) try(f func()) (ex *Exception) {
|
|
|
+ var ctx context
|
|
|
+ vm.saveCtx(&ctx)
|
|
|
+
|
|
|
+ ctxOffset := len(vm.callStack)
|
|
|
+ sp := vm.sp
|
|
|
+ iterLen := len(vm.iterStack)
|
|
|
+ refLen := len(vm.refStack)
|
|
|
+
|
|
|
+ defer func() {
|
|
|
+ if x := recover(); x != nil {
|
|
|
+ defer func() {
|
|
|
+ vm.callStack = vm.callStack[:ctxOffset]
|
|
|
+ vm.restoreCtx(&ctx)
|
|
|
+ vm.sp = sp
|
|
|
+
|
|
|
+ // Restore other stacks
|
|
|
+ iterTail := vm.iterStack[iterLen:]
|
|
|
+ for i, _ := range iterTail {
|
|
|
+ iterTail[i] = iterStackItem{}
|
|
|
+ }
|
|
|
+ vm.iterStack = vm.iterStack[:iterLen]
|
|
|
+ refTail := vm.refStack[refLen:]
|
|
|
+ for i, _ := range refTail {
|
|
|
+ refTail[i] = nil
|
|
|
+ }
|
|
|
+ vm.refStack = vm.refStack[:refLen]
|
|
|
+ }()
|
|
|
+ switch x1 := x.(type) {
|
|
|
+ case Value:
|
|
|
+ ex = &Exception{
|
|
|
+ val: x1,
|
|
|
+ }
|
|
|
+ case *InterruptedError:
|
|
|
+ x1.stack = vm.captureStack(x1.stack, ctxOffset)
|
|
|
+ panic(x1)
|
|
|
+ case *Exception:
|
|
|
+ ex = x1
|
|
|
+ default:
|
|
|
+ if vm.prg != nil {
|
|
|
+ vm.prg.dumpCode(log.Printf)
|
|
|
+ }
|
|
|
+ //log.Print("Stack: ", string(debug.Stack()))
|
|
|
+ panic(fmt.Errorf("Panic at %d: %v", vm.pc, x))
|
|
|
+ }
|
|
|
+ ex.stack = vm.captureStack(ex.stack, ctxOffset)
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ f()
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) runTry() (ex *Exception) {
|
|
|
+ return vm.try(vm.run)
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) push(v Value) {
|
|
|
+ vm.stack.expand(vm.sp)
|
|
|
+ vm.stack[vm.sp] = v
|
|
|
+ vm.sp++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) pop() Value {
|
|
|
+ vm.sp--
|
|
|
+ return vm.stack[vm.sp]
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) peek() Value {
|
|
|
+ return vm.stack[vm.sp-1]
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) saveCtx(ctx *context) {
|
|
|
+ ctx.prg = vm.prg
|
|
|
+ ctx.funcName = vm.funcName
|
|
|
+ ctx.stash = vm.stash
|
|
|
+ ctx.pc = vm.pc
|
|
|
+ ctx.sb = vm.sb
|
|
|
+ ctx.args = vm.args
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) pushCtx() {
|
|
|
+ /*
|
|
|
+ vm.ctxStack = append(vm.ctxStack, context{
|
|
|
+ prg: vm.prg,
|
|
|
+ stash: vm.stash,
|
|
|
+ pc: vm.pc,
|
|
|
+ sb: vm.sb,
|
|
|
+ args: vm.args,
|
|
|
+ })*/
|
|
|
+ vm.callStack = append(vm.callStack, context{})
|
|
|
+ vm.saveCtx(&vm.callStack[len(vm.callStack)-1])
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) restoreCtx(ctx *context) {
|
|
|
+ vm.prg = ctx.prg
|
|
|
+ vm.funcName = ctx.funcName
|
|
|
+ vm.pc = ctx.pc
|
|
|
+ vm.stash = ctx.stash
|
|
|
+ vm.sb = ctx.sb
|
|
|
+ vm.args = ctx.args
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) popCtx() {
|
|
|
+ l := len(vm.callStack) - 1
|
|
|
+ vm.prg = vm.callStack[l].prg
|
|
|
+ vm.callStack[l].prg = nil
|
|
|
+ vm.funcName = vm.callStack[l].funcName
|
|
|
+ vm.pc = vm.callStack[l].pc
|
|
|
+ vm.stash = vm.callStack[l].stash
|
|
|
+ vm.callStack[l].stash = nil
|
|
|
+ vm.sb = vm.callStack[l].sb
|
|
|
+ vm.args = vm.callStack[l].args
|
|
|
+
|
|
|
+ vm.callStack = vm.callStack[:l]
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Runtime) toObject(v Value, args ...interface{}) *Object {
|
|
|
+ //r.checkResolveable(v)
|
|
|
+ if obj, ok := v.(*Object); ok {
|
|
|
+ return obj
|
|
|
+ }
|
|
|
+ if len(args) > 0 {
|
|
|
+ r.typeErrorResult(true, args)
|
|
|
+ } else {
|
|
|
+ r.typeErrorResult(true, "Value is not an object: %s", v.ToString())
|
|
|
+ }
|
|
|
+ panic("Unreachable")
|
|
|
+}
|
|
|
+
|
|
|
+func (r *Runtime) toCallee(v Value) *Object {
|
|
|
+ if obj, ok := v.(*Object); ok {
|
|
|
+ return obj
|
|
|
+ }
|
|
|
+ switch unresolved := v.(type) {
|
|
|
+ case valueUnresolved:
|
|
|
+ unresolved.throw()
|
|
|
+ panic("Unreachable")
|
|
|
+ case memberUnresolved:
|
|
|
+ r.typeErrorResult(true, "Object has no member '%s'", unresolved.ref)
|
|
|
+ panic("Unreachable")
|
|
|
+ }
|
|
|
+ r.typeErrorResult(true, "Value is not an object: %s", v.ToString())
|
|
|
+ panic("Unreachable")
|
|
|
+}
|
|
|
+
|
|
|
+type _newStash struct{}
|
|
|
+
|
|
|
+var newStash _newStash
|
|
|
+
|
|
|
+func (_newStash) exec(vm *vm) {
|
|
|
+ vm.newStash()
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _noop struct{}
|
|
|
+
|
|
|
+var noop _noop
|
|
|
+
|
|
|
+func (_noop) exec(vm *vm) {
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type loadVal uint32
|
|
|
+
|
|
|
+func (l loadVal) exec(vm *vm) {
|
|
|
+ vm.push(vm.prg.values[l])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type loadVal1 uint32
|
|
|
+
|
|
|
+func (l *loadVal1) exec(vm *vm) {
|
|
|
+ vm.push(vm.prg.values[*l])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _loadUndef struct{}
|
|
|
+
|
|
|
+var loadUndef _loadUndef
|
|
|
+
|
|
|
+func (_loadUndef) exec(vm *vm) {
|
|
|
+ vm.push(_undefined)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _loadNil struct{}
|
|
|
+
|
|
|
+var loadNil _loadNil
|
|
|
+
|
|
|
+func (_loadNil) exec(vm *vm) {
|
|
|
+ vm.push(nil)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _loadGlobalObject struct{}
|
|
|
+
|
|
|
+var loadGlobalObject _loadGlobalObject
|
|
|
+
|
|
|
+func (_loadGlobalObject) exec(vm *vm) {
|
|
|
+ vm.push(vm.r.globalObject)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type loadStack int
|
|
|
+
|
|
|
+func (l loadStack) exec(vm *vm) {
|
|
|
+ // l < 0 -- arg<-l-1>
|
|
|
+ // l > 0 -- var<l-1>
|
|
|
+ // l == 0 -- this
|
|
|
+
|
|
|
+ if l < 0 {
|
|
|
+ arg := int(-l)
|
|
|
+ if arg > vm.args {
|
|
|
+ vm.push(_undefined)
|
|
|
+ } else {
|
|
|
+ vm.push(vm.stack[vm.sb+arg])
|
|
|
+ }
|
|
|
+ } else if l > 0 {
|
|
|
+ vm.push(vm.stack[vm.sb+vm.args+int(l)])
|
|
|
+ } else {
|
|
|
+ vm.push(vm.stack[vm.sb])
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _loadCallee struct{}
|
|
|
+
|
|
|
+var loadCallee _loadCallee
|
|
|
+
|
|
|
+func (_loadCallee) exec(vm *vm) {
|
|
|
+ vm.push(vm.stack[vm.sb-1])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) storeStack(s int) {
|
|
|
+ // l < 0 -- arg<-l-1>
|
|
|
+ // l > 0 -- var<l-1>
|
|
|
+ // l == 0 -- this
|
|
|
+
|
|
|
+ if s < 0 {
|
|
|
+ vm.stack[vm.sb-s] = vm.stack[vm.sp-1]
|
|
|
+ } else if s > 0 {
|
|
|
+ vm.stack[vm.sb+vm.args+s] = vm.stack[vm.sp-1]
|
|
|
+ } else {
|
|
|
+ panic("Attempt to modify this")
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type storeStack int
|
|
|
+
|
|
|
+func (s storeStack) exec(vm *vm) {
|
|
|
+ vm.storeStack(int(s))
|
|
|
+}
|
|
|
+
|
|
|
+type storeStackP int
|
|
|
+
|
|
|
+func (s storeStackP) exec(vm *vm) {
|
|
|
+ vm.storeStack(int(s))
|
|
|
+ vm.sp--
|
|
|
+}
|
|
|
+
|
|
|
+type _toNumber struct{}
|
|
|
+
|
|
|
+var toNumber _toNumber
|
|
|
+
|
|
|
+func (_toNumber) exec(vm *vm) {
|
|
|
+ vm.stack[vm.sp-1] = vm.stack[vm.sp-1].ToNumber()
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _add struct{}
|
|
|
+
|
|
|
+var add _add
|
|
|
+
|
|
|
+func (_add) exec(vm *vm) {
|
|
|
+ right := vm.stack[vm.sp-1]
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+
|
|
|
+ if o, ok := left.(*Object); ok {
|
|
|
+ left = o.self.toPrimitive()
|
|
|
+ }
|
|
|
+
|
|
|
+ if o, ok := right.(*Object); ok {
|
|
|
+ right = o.self.toPrimitive()
|
|
|
+ }
|
|
|
+
|
|
|
+ var ret Value
|
|
|
+
|
|
|
+ leftString, isLeftString := left.assertString()
|
|
|
+ rightString, isRightString := right.assertString()
|
|
|
+
|
|
|
+ if isLeftString || isRightString {
|
|
|
+ if !isLeftString {
|
|
|
+ leftString = left.ToString()
|
|
|
+ }
|
|
|
+ if !isRightString {
|
|
|
+ rightString = right.ToString()
|
|
|
+ }
|
|
|
+ ret = leftString.concat(rightString)
|
|
|
+ } else {
|
|
|
+ if leftInt, ok := left.assertInt(); ok {
|
|
|
+ if rightInt, ok := right.assertInt(); ok {
|
|
|
+ ret = intToValue(int64(leftInt) + int64(rightInt))
|
|
|
+ } else {
|
|
|
+ ret = floatToValue(float64(leftInt) + right.ToFloat())
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ret = floatToValue(left.ToFloat() + right.ToFloat())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.stack[vm.sp-2] = ret
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _sub struct{}
|
|
|
+
|
|
|
+var sub _sub
|
|
|
+
|
|
|
+func (_sub) exec(vm *vm) {
|
|
|
+ right := vm.stack[vm.sp-1]
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+
|
|
|
+ var result Value
|
|
|
+
|
|
|
+ if left, ok := left.assertInt(); ok {
|
|
|
+ if right, ok := right.assertInt(); ok {
|
|
|
+ result = intToValue(left - right)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result = floatToValue(left.ToFloat() - right.ToFloat())
|
|
|
+end:
|
|
|
+ vm.sp--
|
|
|
+ vm.stack[vm.sp-1] = result
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _mul struct{}
|
|
|
+
|
|
|
+var mul _mul
|
|
|
+
|
|
|
+func (_mul) exec(vm *vm) {
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+ right := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ var result Value
|
|
|
+
|
|
|
+ if left, ok := toInt(left); ok {
|
|
|
+ if right, ok := toInt(right); ok {
|
|
|
+ if left == 0 && right == -1 || left == -1 && right == 0 {
|
|
|
+ result = _negativeZero
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ res := left * right
|
|
|
+ // check for overflow
|
|
|
+ if left == 0 || right == 0 || res/left == right {
|
|
|
+ result = intToValue(res)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result = floatToValue(left.ToFloat() * right.ToFloat())
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.sp--
|
|
|
+ vm.stack[vm.sp-1] = result
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _div struct{}
|
|
|
+
|
|
|
+var div _div
|
|
|
+
|
|
|
+func (_div) exec(vm *vm) {
|
|
|
+ left := vm.stack[vm.sp-2].ToFloat()
|
|
|
+ right := vm.stack[vm.sp-1].ToFloat()
|
|
|
+
|
|
|
+ var result Value
|
|
|
+
|
|
|
+ if math.IsNaN(left) || math.IsNaN(right) {
|
|
|
+ result = _NaN
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ if math.IsInf(left, 0) && math.IsInf(right, 0) {
|
|
|
+ result = _NaN
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ if left == 0 && right == 0 {
|
|
|
+ result = _NaN
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+
|
|
|
+ if math.IsInf(left, 0) {
|
|
|
+ if math.Signbit(left) == math.Signbit(right) {
|
|
|
+ result = _positiveInf
|
|
|
+ goto end
|
|
|
+ } else {
|
|
|
+ result = _negativeInf
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if math.IsInf(right, 0) {
|
|
|
+ if math.Signbit(left) == math.Signbit(right) {
|
|
|
+ result = _positiveZero
|
|
|
+ goto end
|
|
|
+ } else {
|
|
|
+ result = _negativeZero
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if right == 0 {
|
|
|
+ if math.Signbit(left) == math.Signbit(right) {
|
|
|
+ result = _positiveInf
|
|
|
+ goto end
|
|
|
+ } else {
|
|
|
+ result = _negativeInf
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result = floatToValue(left / right)
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.sp--
|
|
|
+ vm.stack[vm.sp-1] = result
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _mod struct{}
|
|
|
+
|
|
|
+var mod _mod
|
|
|
+
|
|
|
+func (_mod) exec(vm *vm) {
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+ right := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ var result Value
|
|
|
+
|
|
|
+ if leftInt, ok := toInt(left); ok {
|
|
|
+ if rightInt, ok := toInt(right); ok {
|
|
|
+ if rightInt == 0 {
|
|
|
+ result = _NaN
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ r := leftInt % rightInt
|
|
|
+ if r == 0 && leftInt < 0 {
|
|
|
+ result = _negativeZero
|
|
|
+ } else {
|
|
|
+ result = intToValue(leftInt % rightInt)
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result = floatToValue(math.Mod(left.ToFloat(), right.ToFloat()))
|
|
|
+end:
|
|
|
+ vm.sp--
|
|
|
+ vm.stack[vm.sp-1] = result
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _neg struct{}
|
|
|
+
|
|
|
+var neg _neg
|
|
|
+
|
|
|
+func (_neg) exec(vm *vm) {
|
|
|
+ operand := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ var result Value
|
|
|
+
|
|
|
+ if i, ok := toInt(operand); ok {
|
|
|
+ if i == 0 {
|
|
|
+ result = _negativeZero
|
|
|
+ } else {
|
|
|
+ result = valueInt(-i)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ f := operand.ToFloat()
|
|
|
+ if !math.IsNaN(f) {
|
|
|
+ f = -f
|
|
|
+ }
|
|
|
+ result = valueFloat(f)
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.stack[vm.sp-1] = result
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _plus struct{}
|
|
|
+
|
|
|
+var plus _plus
|
|
|
+
|
|
|
+func (_plus) exec(vm *vm) {
|
|
|
+ vm.stack[vm.sp-1] = vm.stack[vm.sp-1].ToNumber()
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _inc struct{}
|
|
|
+
|
|
|
+var inc _inc
|
|
|
+
|
|
|
+func (_inc) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ if i, ok := toInt(v); ok {
|
|
|
+ v = intToValue(i + 1)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+
|
|
|
+ v = valueFloat(v.ToFloat() + 1)
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.stack[vm.sp-1] = v
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _dec struct{}
|
|
|
+
|
|
|
+var dec _dec
|
|
|
+
|
|
|
+func (_dec) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ if i, ok := toInt(v); ok {
|
|
|
+ v = intToValue(i - 1)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+
|
|
|
+ v = valueFloat(v.ToFloat() - 1)
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.stack[vm.sp-1] = v
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _and struct{}
|
|
|
+
|
|
|
+var and _and
|
|
|
+
|
|
|
+func (_and) exec(vm *vm) {
|
|
|
+ left := toInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left & right))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _or struct{}
|
|
|
+
|
|
|
+var or _or
|
|
|
+
|
|
|
+func (_or) exec(vm *vm) {
|
|
|
+ left := toInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left | right))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _xor struct{}
|
|
|
+
|
|
|
+var xor _xor
|
|
|
+
|
|
|
+func (_xor) exec(vm *vm) {
|
|
|
+ left := toInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left ^ right))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _bnot struct{}
|
|
|
+
|
|
|
+var bnot _bnot
|
|
|
+
|
|
|
+func (_bnot) exec(vm *vm) {
|
|
|
+ op := toInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-1] = intToValue(int64(^op))
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _sal struct{}
|
|
|
+
|
|
|
+var sal _sal
|
|
|
+
|
|
|
+func (_sal) exec(vm *vm) {
|
|
|
+ left := toInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toUInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left << (right & 0x1F)))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _sar struct{}
|
|
|
+
|
|
|
+var sar _sar
|
|
|
+
|
|
|
+func (_sar) exec(vm *vm) {
|
|
|
+ left := toInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toUInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _shr struct{}
|
|
|
+
|
|
|
+var shr _shr
|
|
|
+
|
|
|
+func (_shr) exec(vm *vm) {
|
|
|
+ left := toUInt32(vm.stack[vm.sp-2])
|
|
|
+ right := toUInt32(vm.stack[vm.sp-1])
|
|
|
+ vm.stack[vm.sp-2] = intToValue(int64(left >> (right & 0x1F)))
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _halt struct{}
|
|
|
+
|
|
|
+var halt _halt
|
|
|
+
|
|
|
+func (_halt) exec(vm *vm) {
|
|
|
+ vm.halt = true
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type jump int32
|
|
|
+
|
|
|
+func (j jump) exec(vm *vm) {
|
|
|
+ vm.pc += int(j)
|
|
|
+}
|
|
|
+
|
|
|
+type _setElem struct{}
|
|
|
+
|
|
|
+var setElem _setElem
|
|
|
+
|
|
|
+func (_setElem) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-3])
|
|
|
+ propName := vm.stack[vm.sp-2]
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ obj.self.put(propName, val, false)
|
|
|
+
|
|
|
+ vm.sp -= 2
|
|
|
+ vm.stack[vm.sp-1] = val
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _setElemStrict struct{}
|
|
|
+
|
|
|
+var setElemStrict _setElemStrict
|
|
|
+
|
|
|
+func (_setElemStrict) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-3])
|
|
|
+ propName := vm.stack[vm.sp-2]
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ obj.self.put(propName, val, true)
|
|
|
+
|
|
|
+ vm.sp -= 2
|
|
|
+ vm.stack[vm.sp-1] = val
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _deleteElem struct{}
|
|
|
+
|
|
|
+var deleteElem _deleteElem
|
|
|
+
|
|
|
+func (_deleteElem) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-2])
|
|
|
+ propName := vm.stack[vm.sp-1]
|
|
|
+ if !obj.self.hasProperty(propName) || obj.self.delete(propName, false) {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _deleteElemStrict struct{}
|
|
|
+
|
|
|
+var deleteElemStrict _deleteElemStrict
|
|
|
+
|
|
|
+func (_deleteElemStrict) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-2])
|
|
|
+ propName := vm.stack[vm.sp-1]
|
|
|
+ obj.self.delete(propName, true)
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type deleteProp string
|
|
|
+
|
|
|
+func (d deleteProp) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-1])
|
|
|
+ if !obj.self.hasPropertyStr(string(d)) || obj.self.deleteStr(string(d), false) {
|
|
|
+ vm.stack[vm.sp-1] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-1] = valueFalse
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type deletePropStrict string
|
|
|
+
|
|
|
+func (d deletePropStrict) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-1])
|
|
|
+ obj.self.deleteStr(string(d), true)
|
|
|
+ vm.stack[vm.sp-1] = valueTrue
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setProp string
|
|
|
+
|
|
|
+func (p setProp) exec(vm *vm) {
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ vm.r.toObject(vm.stack[vm.sp-2]).self.putStr(string(p), val, false)
|
|
|
+ vm.stack[vm.sp-2] = val
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setPropStrict string
|
|
|
+
|
|
|
+func (p setPropStrict) exec(vm *vm) {
|
|
|
+ obj := vm.stack[vm.sp-2]
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ obj1 := vm.r.toObject(obj)
|
|
|
+ obj1.self.putStr(string(p), val, true)
|
|
|
+ vm.stack[vm.sp-2] = val
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setProp1 string
|
|
|
+
|
|
|
+func (p setProp1) exec(vm *vm) {
|
|
|
+ vm.r.toObject(vm.stack[vm.sp-2]).self._putProp(string(p), vm.stack[vm.sp-1], true, true, true)
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _setProto struct{}
|
|
|
+
|
|
|
+var setProto _setProto
|
|
|
+
|
|
|
+func (_setProto) exec(vm *vm) {
|
|
|
+ vm.r.toObject(vm.stack[vm.sp-2]).self.putStr("__proto__", vm.stack[vm.sp-1], true)
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setPropGetter string
|
|
|
+
|
|
|
+func (s setPropGetter) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-2])
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ descr := vm.r.NewObject().self
|
|
|
+ descr.putStr("get", val, false)
|
|
|
+ descr.putStr("configurable", valueTrue, false)
|
|
|
+ descr.putStr("enumerable", valueTrue, false)
|
|
|
+
|
|
|
+ obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setPropSetter string
|
|
|
+
|
|
|
+func (s setPropSetter) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-2])
|
|
|
+ val := vm.stack[vm.sp-1]
|
|
|
+
|
|
|
+ descr := vm.r.NewObject().self
|
|
|
+ descr.putStr("set", val, false)
|
|
|
+ descr.putStr("configurable", valueTrue, false)
|
|
|
+ descr.putStr("enumerable", valueTrue, false)
|
|
|
+
|
|
|
+ obj.self.defineOwnProperty(newStringValue(string(s)), descr, false)
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getProp string
|
|
|
+
|
|
|
+func (g getProp) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+ obj := v.baseObject(vm.r)
|
|
|
+ if obj == nil {
|
|
|
+ vm.r.typeErrorResult(true, "Cannot read property '%s' of undefined", g)
|
|
|
+ }
|
|
|
+ prop := obj.self.getPropStr(string(g))
|
|
|
+ if prop1, ok := prop.(*valueProperty); ok {
|
|
|
+ vm.stack[vm.sp-1] = prop1.get(v)
|
|
|
+ } else {
|
|
|
+ if prop == nil {
|
|
|
+ prop = _undefined
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-1] = prop
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getPropCallee string
|
|
|
+
|
|
|
+func (g getPropCallee) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+ obj := v.baseObject(vm.r)
|
|
|
+ if obj == nil {
|
|
|
+ vm.r.typeErrorResult(true, "Cannot read property '%s' of undefined", g)
|
|
|
+ }
|
|
|
+ prop := obj.self.getPropStr(string(g))
|
|
|
+ if prop1, ok := prop.(*valueProperty); ok {
|
|
|
+ vm.stack[vm.sp-1] = prop1.get(v)
|
|
|
+ } else {
|
|
|
+ if prop == nil {
|
|
|
+ prop = memberUnresolved{valueUnresolved{r: vm.r, ref: string(g)}}
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-1] = prop
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _getElem struct{}
|
|
|
+
|
|
|
+var getElem _getElem
|
|
|
+
|
|
|
+func (_getElem) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-2]
|
|
|
+ obj := v.baseObject(vm.r)
|
|
|
+ propName := vm.stack[vm.sp-1]
|
|
|
+ if obj == nil {
|
|
|
+ vm.r.typeErrorResult(true, "Cannot read property '%s' of undefined", propName.String())
|
|
|
+ }
|
|
|
+
|
|
|
+ prop := obj.self.getProp(propName)
|
|
|
+ if prop1, ok := prop.(*valueProperty); ok {
|
|
|
+ vm.stack[vm.sp-2] = prop1.get(v)
|
|
|
+ } else {
|
|
|
+ if prop == nil {
|
|
|
+ prop = _undefined
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-2] = prop
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _getElemCallee struct{}
|
|
|
+
|
|
|
+var getElemCallee _getElemCallee
|
|
|
+
|
|
|
+func (_getElemCallee) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-2]
|
|
|
+ obj := v.baseObject(vm.r)
|
|
|
+ propName := vm.stack[vm.sp-1]
|
|
|
+ if obj == nil {
|
|
|
+ vm.r.typeErrorResult(true, "Cannot read property '%s' of undefined", propName.String())
|
|
|
+ panic("Unreachable")
|
|
|
+ }
|
|
|
+
|
|
|
+ prop := obj.self.getProp(propName)
|
|
|
+ if prop1, ok := prop.(*valueProperty); ok {
|
|
|
+ vm.stack[vm.sp-2] = prop1.get(v)
|
|
|
+ } else {
|
|
|
+ if prop == nil {
|
|
|
+ prop = memberUnresolved{valueUnresolved{r: vm.r, ref: propName.String()}}
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-2] = prop
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _dup struct{}
|
|
|
+
|
|
|
+var dup _dup
|
|
|
+
|
|
|
+func (_dup) exec(vm *vm) {
|
|
|
+ vm.push(vm.stack[vm.sp-1])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type dupN uint32
|
|
|
+
|
|
|
+func (d dupN) exec(vm *vm) {
|
|
|
+ vm.push(vm.stack[vm.sp-1-int(d)])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type rdupN uint32
|
|
|
+
|
|
|
+func (d rdupN) exec(vm *vm) {
|
|
|
+ vm.stack[vm.sp-1-int(d)] = vm.stack[vm.sp-1]
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _newObject struct{}
|
|
|
+
|
|
|
+var newObject _newObject
|
|
|
+
|
|
|
+func (_newObject) exec(vm *vm) {
|
|
|
+ vm.push(vm.r.NewObject())
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type newArray uint32
|
|
|
+
|
|
|
+func (l newArray) exec(vm *vm) {
|
|
|
+ values := make([]Value, l)
|
|
|
+ if l > 0 {
|
|
|
+ copy(values, vm.stack[vm.sp-int(l):vm.sp])
|
|
|
+ }
|
|
|
+ obj := vm.r.newArrayValues(values)
|
|
|
+ if l > 0 {
|
|
|
+ vm.sp -= int(l) - 1
|
|
|
+ vm.stack[vm.sp-1] = obj
|
|
|
+ } else {
|
|
|
+ vm.push(obj)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type newRegexp struct {
|
|
|
+ pattern regexpPattern
|
|
|
+ src valueString
|
|
|
+
|
|
|
+ global, ignoreCase, multiline bool
|
|
|
+}
|
|
|
+
|
|
|
+func (n *newRegexp) exec(vm *vm) {
|
|
|
+ vm.push(vm.r.newRegExpp(n.pattern, n.src, n.global, n.ignoreCase, n.multiline, vm.r.global.RegExpPrototype))
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) setLocal(s int) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+ level := s >> 24
|
|
|
+ idx := uint32(s & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+ stash.putByIdx(idx, v)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setLocal uint32
|
|
|
+
|
|
|
+func (s setLocal) exec(vm *vm) {
|
|
|
+ vm.setLocal(int(s))
|
|
|
+}
|
|
|
+
|
|
|
+type setLocalP uint32
|
|
|
+
|
|
|
+func (s setLocalP) exec(vm *vm) {
|
|
|
+ vm.setLocal(int(s))
|
|
|
+ vm.sp--
|
|
|
+}
|
|
|
+
|
|
|
+type setVar struct {
|
|
|
+ name string
|
|
|
+ idx uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (s setVar) exec(vm *vm) {
|
|
|
+ v := vm.peek()
|
|
|
+
|
|
|
+ level := int(s.idx >> 24)
|
|
|
+ idx := uint32(s.idx & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ name := s.name
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ if stash.put(name, v) {
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+
|
|
|
+ if stash != nil {
|
|
|
+ stash.putByIdx(idx, v)
|
|
|
+ } else {
|
|
|
+ vm.r.globalObject.self.putStr(name, v, false)
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type resolveVar1 string
|
|
|
+
|
|
|
+func (s resolveVar1) exec(vm *vm) {
|
|
|
+ name := string(s)
|
|
|
+ var ref ref
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if stash.obj != nil {
|
|
|
+ if stash.obj.hasPropertyStr(name) {
|
|
|
+ ref = &objRef{
|
|
|
+ base: stash.obj,
|
|
|
+ name: name,
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if idx, exists := stash.names[name]; exists {
|
|
|
+ ref = &stashRef{
|
|
|
+ v: &stash.values[idx],
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ref = &objRef{
|
|
|
+ base: vm.r.globalObject.self,
|
|
|
+ name: name,
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.refStack = append(vm.refStack, ref)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type deleteVar string
|
|
|
+
|
|
|
+func (d deleteVar) exec(vm *vm) {
|
|
|
+ name := string(d)
|
|
|
+ ret := true
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if stash.obj != nil {
|
|
|
+ if stash.obj.hasPropertyStr(name) {
|
|
|
+ ret = stash.obj.deleteStr(name, false)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if _, exists := stash.names[name]; exists {
|
|
|
+ ret = false
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if vm.r.globalObject.self.hasPropertyStr(name) {
|
|
|
+ ret = vm.r.globalObject.self.deleteStr(name, false)
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ if ret {
|
|
|
+ vm.push(valueTrue)
|
|
|
+ } else {
|
|
|
+ vm.push(valueFalse)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type deleteGlobal string
|
|
|
+
|
|
|
+func (d deleteGlobal) exec(vm *vm) {
|
|
|
+ name := string(d)
|
|
|
+ var ret bool
|
|
|
+ if vm.r.globalObject.self.hasPropertyStr(name) {
|
|
|
+ ret = vm.r.globalObject.self.deleteStr(name, false)
|
|
|
+ } else {
|
|
|
+ ret = true
|
|
|
+ }
|
|
|
+ if ret {
|
|
|
+ vm.push(valueTrue)
|
|
|
+ } else {
|
|
|
+ vm.push(valueFalse)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type resolveVar1Strict string
|
|
|
+
|
|
|
+func (s resolveVar1Strict) exec(vm *vm) {
|
|
|
+ name := string(s)
|
|
|
+ var ref ref
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if stash.obj != nil {
|
|
|
+ if stash.obj.hasPropertyStr(name) {
|
|
|
+ ref = &objRef{
|
|
|
+ base: stash.obj,
|
|
|
+ name: name,
|
|
|
+ strict: true,
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if idx, exists := stash.names[name]; exists {
|
|
|
+ ref = &stashRef{
|
|
|
+ v: &stash.values[idx],
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if vm.r.globalObject.self.hasPropertyStr(name) {
|
|
|
+ ref = &objRef{
|
|
|
+ base: vm.r.globalObject.self,
|
|
|
+ name: name,
|
|
|
+ strict: true,
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+
|
|
|
+ ref = &unresolvedRef{
|
|
|
+ runtime: vm.r,
|
|
|
+ name: string(s),
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.refStack = append(vm.refStack, ref)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setGlobal string
|
|
|
+
|
|
|
+func (s setGlobal) exec(vm *vm) {
|
|
|
+ v := vm.peek()
|
|
|
+
|
|
|
+ vm.r.globalObject.self.putStr(string(s), v, false)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setVarStrict struct {
|
|
|
+ name string
|
|
|
+ idx uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (s setVarStrict) exec(vm *vm) {
|
|
|
+ v := vm.peek()
|
|
|
+
|
|
|
+ level := int(s.idx >> 24)
|
|
|
+ idx := uint32(s.idx & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ name := s.name
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ if stash.put(name, v) {
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+
|
|
|
+ if stash != nil {
|
|
|
+ stash.putByIdx(idx, v)
|
|
|
+ } else {
|
|
|
+ o := vm.r.globalObject.self
|
|
|
+ if o.hasOwnPropertyStr(name) {
|
|
|
+ o.putStr(name, v, true)
|
|
|
+ } else {
|
|
|
+ vm.r.throwReferenceError(name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setVar1Strict string
|
|
|
+
|
|
|
+func (s setVar1Strict) exec(vm *vm) {
|
|
|
+ v := vm.peek()
|
|
|
+ var o objectImpl
|
|
|
+
|
|
|
+ name := string(s)
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if stash.put(name, v) {
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ o = vm.r.globalObject.self
|
|
|
+ if o.hasOwnPropertyStr(name) {
|
|
|
+ o.putStr(name, v, true)
|
|
|
+ } else {
|
|
|
+ vm.r.throwReferenceError(name)
|
|
|
+ }
|
|
|
+end:
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type setGlobalStrict string
|
|
|
+
|
|
|
+func (s setGlobalStrict) exec(vm *vm) {
|
|
|
+ v := vm.peek()
|
|
|
+
|
|
|
+ name := string(s)
|
|
|
+ o := vm.r.globalObject.self
|
|
|
+ if o.hasOwnPropertyStr(name) {
|
|
|
+ o.putStr(name, v, true)
|
|
|
+ } else {
|
|
|
+ vm.r.throwReferenceError(name)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getLocal uint32
|
|
|
+
|
|
|
+func (g getLocal) exec(vm *vm) {
|
|
|
+ level := int(g >> 24)
|
|
|
+ idx := uint32(g & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.push(stash.getByIdx(idx))
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getVar struct {
|
|
|
+ name string
|
|
|
+ idx uint32
|
|
|
+ ref bool
|
|
|
+}
|
|
|
+
|
|
|
+func (g getVar) exec(vm *vm) {
|
|
|
+ level := int(g.idx >> 24)
|
|
|
+ idx := uint32(g.idx & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ name := g.name
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ if v, found := stash.getByName(name, vm); found {
|
|
|
+ vm.push(v)
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+ if stash != nil {
|
|
|
+ vm.push(stash.getByIdx(idx))
|
|
|
+ } else {
|
|
|
+ v := vm.r.globalObject.self.getStr(name)
|
|
|
+ if v == nil {
|
|
|
+ if g.ref {
|
|
|
+ v = valueUnresolved{r: vm.r, ref: name}
|
|
|
+ } else {
|
|
|
+ vm.r.throwReferenceError(name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vm.push(v)
|
|
|
+ }
|
|
|
+end:
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type resolveVar struct {
|
|
|
+ name string
|
|
|
+ idx uint32
|
|
|
+ strict bool
|
|
|
+}
|
|
|
+
|
|
|
+func (r resolveVar) exec(vm *vm) {
|
|
|
+ level := int(r.idx >> 24)
|
|
|
+ idx := uint32(r.idx & 0x00FFFFFF)
|
|
|
+ stash := vm.stash
|
|
|
+ var ref ref
|
|
|
+ for i := 0; i < level; i++ {
|
|
|
+ if stash.obj != nil {
|
|
|
+ if stash.obj.hasPropertyStr(r.name) {
|
|
|
+ ref = &objRef{
|
|
|
+ base: stash.obj,
|
|
|
+ name: r.name,
|
|
|
+ strict: r.strict,
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if idx, exists := stash.names[r.name]; exists {
|
|
|
+ ref = &stashRef{
|
|
|
+ v: &stash.values[idx],
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stash = stash.outer
|
|
|
+ }
|
|
|
+
|
|
|
+ if stash != nil {
|
|
|
+ ref = &stashRef{
|
|
|
+ v: &stash.values[idx],
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ } /*else {
|
|
|
+ if vm.r.globalObject.self.hasProperty(nameVal) {
|
|
|
+ ref = &objRef{
|
|
|
+ base: vm.r.globalObject.self,
|
|
|
+ name: r.name,
|
|
|
+ }
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ } */
|
|
|
+
|
|
|
+ ref = &unresolvedRef{
|
|
|
+ runtime: vm.r,
|
|
|
+ name: r.name,
|
|
|
+ }
|
|
|
+
|
|
|
+end:
|
|
|
+ vm.refStack = append(vm.refStack, ref)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _getValue struct{}
|
|
|
+
|
|
|
+var getValue _getValue
|
|
|
+
|
|
|
+func (_getValue) exec(vm *vm) {
|
|
|
+ ref := vm.refStack[len(vm.refStack)-1]
|
|
|
+ if v := ref.get(); v != nil {
|
|
|
+ vm.push(v)
|
|
|
+ } else {
|
|
|
+ vm.r.throwReferenceError(ref.refname())
|
|
|
+ panic("Unreachable")
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _putValue struct{}
|
|
|
+
|
|
|
+var putValue _putValue
|
|
|
+
|
|
|
+func (_putValue) exec(vm *vm) {
|
|
|
+ l := len(vm.refStack) - 1
|
|
|
+ ref := vm.refStack[l]
|
|
|
+ vm.refStack[l] = nil
|
|
|
+ vm.refStack = vm.refStack[:l]
|
|
|
+ ref.set(vm.stack[vm.sp-1])
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getVar1 string
|
|
|
+
|
|
|
+func (n getVar1) exec(vm *vm) {
|
|
|
+ name := string(n)
|
|
|
+ var val Value
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if v, exists := stash.getByName(name, vm); exists {
|
|
|
+ val = v
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if val == nil {
|
|
|
+ val = vm.r.globalObject.self.getStr(name)
|
|
|
+ if val == nil {
|
|
|
+ vm.r.throwReferenceError(name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vm.push(val)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type getVar1Callee string
|
|
|
+
|
|
|
+func (n getVar1Callee) exec(vm *vm) {
|
|
|
+ name := string(n)
|
|
|
+ var val Value
|
|
|
+ for stash := vm.stash; stash != nil; stash = stash.outer {
|
|
|
+ if v, exists := stash.getByName(name, vm); exists {
|
|
|
+ val = v
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if val == nil {
|
|
|
+ val = vm.r.globalObject.self.getStr(name)
|
|
|
+ if val == nil {
|
|
|
+ val = valueUnresolved{r: vm.r, ref: name}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vm.push(val)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _pop struct{}
|
|
|
+
|
|
|
+var pop _pop
|
|
|
+
|
|
|
+func (_pop) exec(vm *vm) {
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _swap struct{}
|
|
|
+
|
|
|
+var swap _swap
|
|
|
+
|
|
|
+func (_swap) exec(vm *vm) {
|
|
|
+ vm.stack[vm.sp-1], vm.stack[vm.sp-2] = vm.stack[vm.sp-2], vm.stack[vm.sp-1]
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) callEval(n int, strict bool) {
|
|
|
+ if vm.r.toObject(vm.stack[vm.sp-n-1]) == vm.r.global.Eval {
|
|
|
+ if n > 0 {
|
|
|
+ srcVal := vm.stack[vm.sp-n]
|
|
|
+ if src, ok := srcVal.assertString(); ok {
|
|
|
+ var this Value
|
|
|
+ if vm.sb != 0 {
|
|
|
+ this = vm.stack[vm.sb]
|
|
|
+ } else {
|
|
|
+ this = vm.r.globalObject
|
|
|
+ }
|
|
|
+ ret := vm.r.eval(src.String(), true, strict, this)
|
|
|
+ vm.stack[vm.sp-n-2] = ret
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-n-2] = srcVal
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-n-2] = _undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp -= n + 1
|
|
|
+ vm.pc++
|
|
|
+ } else {
|
|
|
+ call(n).exec(vm)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type callEval uint32
|
|
|
+
|
|
|
+func (numargs callEval) exec(vm *vm) {
|
|
|
+ vm.callEval(int(numargs), false)
|
|
|
+}
|
|
|
+
|
|
|
+type callEvalStrict uint32
|
|
|
+
|
|
|
+func (numargs callEvalStrict) exec(vm *vm) {
|
|
|
+ vm.callEval(int(numargs), true)
|
|
|
+}
|
|
|
+
|
|
|
+type _boxThis struct{}
|
|
|
+
|
|
|
+var boxThis _boxThis
|
|
|
+
|
|
|
+func (_boxThis) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sb]
|
|
|
+ if v == _undefined || v == _null {
|
|
|
+ vm.stack[vm.sb] = vm.r.globalObject
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sb] = v.ToObject(vm.r)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type call uint32
|
|
|
+
|
|
|
+func (numargs call) exec(vm *vm) {
|
|
|
+ // this
|
|
|
+ // callee
|
|
|
+ // arg0
|
|
|
+ // ...
|
|
|
+ // arg<numargs-1>
|
|
|
+ n := int(numargs)
|
|
|
+ v := vm.stack[vm.sp-n-1] // callee
|
|
|
+ obj := vm.r.toCallee(v)
|
|
|
+repeat:
|
|
|
+ switch f := obj.self.(type) {
|
|
|
+ case *funcObject:
|
|
|
+ vm.pc++
|
|
|
+ vm.pushCtx()
|
|
|
+ vm.args = n
|
|
|
+ vm.prg = f.prg
|
|
|
+ vm.stash = f.stash
|
|
|
+ vm.pc = 0
|
|
|
+ vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = vm.stack[vm.sp-n-2], vm.stack[vm.sp-n-1]
|
|
|
+ return
|
|
|
+ case *nativeFuncObject:
|
|
|
+ vm._nativeCall(f, n)
|
|
|
+ case *boundFuncObject:
|
|
|
+ vm._nativeCall(&f.nativeFuncObject, n)
|
|
|
+ case *lazyObject:
|
|
|
+ obj.self = f.create(obj)
|
|
|
+ goto repeat
|
|
|
+ default:
|
|
|
+ vm.r.typeErrorResult(true, "Not a function: %s", obj.ToString())
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) _nativeCall(f *nativeFuncObject, n int) {
|
|
|
+ if f.f != nil {
|
|
|
+ vm.pushCtx()
|
|
|
+ vm.prg = nil
|
|
|
+ vm.funcName = f.nameProp.get(nil).String()
|
|
|
+ ret := f.f(FunctionCall{
|
|
|
+ Arguments: vm.stack[vm.sp-n : vm.sp],
|
|
|
+ This: vm.stack[vm.sp-n-2],
|
|
|
+ })
|
|
|
+ if ret == nil {
|
|
|
+ ret = _undefined
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-n-2] = ret
|
|
|
+ vm.popCtx()
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-n-2] = _undefined
|
|
|
+ }
|
|
|
+ vm.sp -= n + 1
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type enterFunc uint32
|
|
|
+
|
|
|
+func (e enterFunc) exec(vm *vm) {
|
|
|
+ // Input stack:
|
|
|
+ //
|
|
|
+ // callee
|
|
|
+ // this
|
|
|
+ // arg0
|
|
|
+ // ...
|
|
|
+ // argN
|
|
|
+ // <- sp
|
|
|
+
|
|
|
+ // Output stack:
|
|
|
+ //
|
|
|
+ // this <- sb
|
|
|
+ // <- sp
|
|
|
+
|
|
|
+ vm.newStash()
|
|
|
+ offset := vm.args - int(e)
|
|
|
+ vm.stash.values = make([]Value, e)
|
|
|
+ if offset > 0 {
|
|
|
+ copy(vm.stash.values, vm.stack[vm.sp-vm.args:])
|
|
|
+ vm.stash.extraArgs = make([]Value, offset)
|
|
|
+ copy(vm.stash.extraArgs, vm.stack[vm.sp-offset:])
|
|
|
+ } else {
|
|
|
+ copy(vm.stash.values, vm.stack[vm.sp-vm.args:])
|
|
|
+ vv := vm.stash.values[vm.args:]
|
|
|
+ for i, _ := range vv {
|
|
|
+ vv[i] = _undefined
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vm.sp -= vm.args
|
|
|
+ vm.sb = vm.sp - 1
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _ret struct{}
|
|
|
+
|
|
|
+var ret _ret
|
|
|
+
|
|
|
+func (_ret) exec(vm *vm) {
|
|
|
+ // callee -3
|
|
|
+ // this -2
|
|
|
+ // retval -1
|
|
|
+
|
|
|
+ vm.stack[vm.sp-3] = vm.stack[vm.sp-1]
|
|
|
+ vm.sp -= 2
|
|
|
+ vm.popCtx()
|
|
|
+ if vm.pc < 0 {
|
|
|
+ vm.halt = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type enterFuncStashless struct {
|
|
|
+ stackSize uint32
|
|
|
+ args uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (e enterFuncStashless) exec(vm *vm) {
|
|
|
+ vm.sb = vm.sp - vm.args - 1
|
|
|
+ var ss int
|
|
|
+ d := int(e.args) - vm.args
|
|
|
+ if d > 0 {
|
|
|
+ ss = int(e.stackSize) + d
|
|
|
+ vm.args = int(e.args)
|
|
|
+ } else {
|
|
|
+ ss = int(e.stackSize)
|
|
|
+ }
|
|
|
+ sp := vm.sp
|
|
|
+ if ss > 0 {
|
|
|
+ vm.sp += int(ss)
|
|
|
+ vm.stack.expand(vm.sp)
|
|
|
+ s := vm.stack[sp:vm.sp]
|
|
|
+ for i, _ := range s {
|
|
|
+ s[i] = _undefined
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _retStashless struct{}
|
|
|
+
|
|
|
+var retStashless _retStashless
|
|
|
+
|
|
|
+func (_retStashless) exec(vm *vm) {
|
|
|
+ retval := vm.stack[vm.sp-1]
|
|
|
+ vm.sp = vm.sb
|
|
|
+ vm.stack[vm.sp-1] = retval
|
|
|
+ vm.popCtx()
|
|
|
+ if vm.pc < 0 {
|
|
|
+ vm.halt = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type newFunc struct {
|
|
|
+ prg *Program
|
|
|
+ name string
|
|
|
+ length uint32
|
|
|
+ strict bool
|
|
|
+
|
|
|
+ srcStart, srcEnd uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (n *newFunc) exec(vm *vm) {
|
|
|
+ obj := vm.r.newFunc(n.name, int(n.length), n.strict)
|
|
|
+ obj.prg = n.prg
|
|
|
+ obj.stash = vm.stash
|
|
|
+ obj.src = n.prg.src.src[n.srcStart:n.srcEnd]
|
|
|
+ vm.push(obj.val)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type bindName string
|
|
|
+
|
|
|
+func (d bindName) exec(vm *vm) {
|
|
|
+ if vm.stash != nil {
|
|
|
+ vm.stash.createBinding(string(d))
|
|
|
+ } else {
|
|
|
+ vm.r.globalObject.self._putProp(string(d), _undefined, true, true, false)
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type jne int32
|
|
|
+
|
|
|
+func (j jne) exec(vm *vm) {
|
|
|
+ vm.sp--
|
|
|
+ if !vm.stack[vm.sp].ToBoolean() {
|
|
|
+ vm.pc += int(j)
|
|
|
+ } else {
|
|
|
+ vm.pc++
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type jeq int32
|
|
|
+
|
|
|
+func (j jeq) exec(vm *vm) {
|
|
|
+ vm.sp--
|
|
|
+ if vm.stack[vm.sp].ToBoolean() {
|
|
|
+ vm.pc += int(j)
|
|
|
+ } else {
|
|
|
+ vm.pc++
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type jeq1 int32
|
|
|
+
|
|
|
+func (j jeq1) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-1].ToBoolean() {
|
|
|
+ vm.pc += int(j)
|
|
|
+ } else {
|
|
|
+ vm.pc++
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type jneq1 int32
|
|
|
+
|
|
|
+func (j jneq1) exec(vm *vm) {
|
|
|
+ if !vm.stack[vm.sp-1].ToBoolean() {
|
|
|
+ vm.pc += int(j)
|
|
|
+ } else {
|
|
|
+ vm.pc++
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type _not struct{}
|
|
|
+
|
|
|
+var not _not
|
|
|
+
|
|
|
+func (_not) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-1].ToBoolean() {
|
|
|
+ vm.stack[vm.sp-1] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-1] = valueTrue
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func toPrimitiveNumber(v Value) Value {
|
|
|
+ if o, ok := v.(*Object); ok {
|
|
|
+ return o.self.toPrimitiveNumber()
|
|
|
+ }
|
|
|
+ return v
|
|
|
+}
|
|
|
+
|
|
|
+func cmp(px, py Value) Value {
|
|
|
+ var ret bool
|
|
|
+ var nx, ny float64
|
|
|
+
|
|
|
+ if xs, ok := px.assertString(); ok {
|
|
|
+ if ys, ok := py.assertString(); ok {
|
|
|
+ ret = xs.compareTo(ys) < 0
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if xi, ok := px.assertInt(); ok {
|
|
|
+ if yi, ok := py.assertInt(); ok {
|
|
|
+ ret = xi < yi
|
|
|
+ goto end
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ nx = px.ToFloat()
|
|
|
+ ny = py.ToFloat()
|
|
|
+
|
|
|
+ if math.IsNaN(nx) || math.IsNaN(ny) {
|
|
|
+ return _undefined
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = nx < ny
|
|
|
+
|
|
|
+end:
|
|
|
+ if ret {
|
|
|
+ return valueTrue
|
|
|
+ }
|
|
|
+ return valueFalse
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+type _op_lt struct{}
|
|
|
+
|
|
|
+var op_lt _op_lt
|
|
|
+
|
|
|
+func (_op_lt) exec(vm *vm) {
|
|
|
+ left := toPrimitiveNumber(vm.stack[vm.sp-2])
|
|
|
+ right := toPrimitiveNumber(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ r := cmp(left, right)
|
|
|
+ if r == _undefined {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = r
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_lte struct{}
|
|
|
+
|
|
|
+var op_lte _op_lte
|
|
|
+
|
|
|
+func (_op_lte) exec(vm *vm) {
|
|
|
+ left := toPrimitiveNumber(vm.stack[vm.sp-2])
|
|
|
+ right := toPrimitiveNumber(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ r := cmp(right, left)
|
|
|
+ if r == _undefined || r == valueTrue {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_gt struct{}
|
|
|
+
|
|
|
+var op_gt _op_gt
|
|
|
+
|
|
|
+func (_op_gt) exec(vm *vm) {
|
|
|
+ left := toPrimitiveNumber(vm.stack[vm.sp-2])
|
|
|
+ right := toPrimitiveNumber(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ r := cmp(right, left)
|
|
|
+ if r == _undefined {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = r
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_gte struct{}
|
|
|
+
|
|
|
+var op_gte _op_gte
|
|
|
+
|
|
|
+func (_op_gte) exec(vm *vm) {
|
|
|
+ left := toPrimitiveNumber(vm.stack[vm.sp-2])
|
|
|
+ right := toPrimitiveNumber(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ r := cmp(left, right)
|
|
|
+ if r == _undefined || r == valueTrue {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_eq struct{}
|
|
|
+
|
|
|
+var op_eq _op_eq
|
|
|
+
|
|
|
+func (_op_eq) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-2].Equals(vm.stack[vm.sp-1]) {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_neq struct{}
|
|
|
+
|
|
|
+var op_neq _op_neq
|
|
|
+
|
|
|
+func (_op_neq) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-2].Equals(vm.stack[vm.sp-1]) {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_strict_eq struct{}
|
|
|
+
|
|
|
+var op_strict_eq _op_strict_eq
|
|
|
+
|
|
|
+func (_op_strict_eq) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-2].StrictEquals(vm.stack[vm.sp-1]) {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_strict_neq struct{}
|
|
|
+
|
|
|
+var op_strict_neq _op_strict_neq
|
|
|
+
|
|
|
+func (_op_strict_neq) exec(vm *vm) {
|
|
|
+ if vm.stack[vm.sp-2].StrictEquals(vm.stack[vm.sp-1]) {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_instanceof struct{}
|
|
|
+
|
|
|
+var op_instanceof _op_instanceof
|
|
|
+
|
|
|
+func (_op_instanceof) exec(vm *vm) {
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+ right := vm.r.toObject(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ if right.self.hasInstance(left) {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _op_in struct{}
|
|
|
+
|
|
|
+var op_in _op_in
|
|
|
+
|
|
|
+func (_op_in) exec(vm *vm) {
|
|
|
+ left := vm.stack[vm.sp-2]
|
|
|
+ right := vm.r.toObject(vm.stack[vm.sp-1])
|
|
|
+
|
|
|
+ if right.self.hasProperty(left) {
|
|
|
+ vm.stack[vm.sp-2] = valueTrue
|
|
|
+ } else {
|
|
|
+ vm.stack[vm.sp-2] = valueFalse
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type try struct {
|
|
|
+ catchOffset int32
|
|
|
+ finallyOffset int32
|
|
|
+ dynamic bool
|
|
|
+}
|
|
|
+
|
|
|
+func (t try) exec(vm *vm) {
|
|
|
+ o := vm.pc
|
|
|
+ vm.pc++
|
|
|
+ ex := vm.runTry()
|
|
|
+ if ex != nil && t.catchOffset > 0 {
|
|
|
+ // run the catch block (in try)
|
|
|
+ vm.pc = o + int(t.catchOffset)
|
|
|
+ // TODO: if ex.val is an Error, set the stack property
|
|
|
+ if t.dynamic {
|
|
|
+ vm.newStash()
|
|
|
+ vm.stash.putByIdx(0, ex.val)
|
|
|
+ } else {
|
|
|
+ vm.push(ex.val)
|
|
|
+ }
|
|
|
+ ex = vm.runTry()
|
|
|
+ if t.dynamic {
|
|
|
+ vm.stash = vm.stash.outer
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if t.finallyOffset > 0 {
|
|
|
+ pc := vm.pc
|
|
|
+ // Run finally
|
|
|
+ vm.pc = o + int(t.finallyOffset)
|
|
|
+ vm.run()
|
|
|
+ if vm.prg.code[vm.pc] == retFinally {
|
|
|
+ vm.pc = pc
|
|
|
+ } else {
|
|
|
+ // break or continue out of finally, dropping exception
|
|
|
+ ex = nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.halt = false
|
|
|
+
|
|
|
+ if ex != nil {
|
|
|
+ panic(ex)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type _retFinally struct{}
|
|
|
+
|
|
|
+var retFinally _retFinally
|
|
|
+
|
|
|
+func (_retFinally) exec(vm *vm) {
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type enterCatch string
|
|
|
+
|
|
|
+func (varName enterCatch) exec(vm *vm) {
|
|
|
+ vm.stash.names = map[string]uint32{
|
|
|
+ string(varName): 0,
|
|
|
+ }
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _throw struct{}
|
|
|
+
|
|
|
+var throw _throw
|
|
|
+
|
|
|
+func (_throw) exec(vm *vm) {
|
|
|
+ panic(vm.stack[vm.sp-1])
|
|
|
+}
|
|
|
+
|
|
|
+type _new uint32
|
|
|
+
|
|
|
+func (n _new) exec(vm *vm) {
|
|
|
+ obj := vm.r.toObject(vm.stack[vm.sp-1-int(n)])
|
|
|
+repeat:
|
|
|
+ switch f := obj.self.(type) {
|
|
|
+ case *funcObject:
|
|
|
+ args := make([]Value, n)
|
|
|
+ copy(args, vm.stack[vm.sp-int(n):])
|
|
|
+ vm.sp -= int(n)
|
|
|
+ vm.stack[vm.sp-1] = f.construct(args)
|
|
|
+ case *nativeFuncObject:
|
|
|
+ vm._nativeNew(f, int(n))
|
|
|
+ case *boundFuncObject:
|
|
|
+ vm._nativeNew(&f.nativeFuncObject, int(n))
|
|
|
+ case *lazyObject:
|
|
|
+ obj.self = f.create(obj)
|
|
|
+ goto repeat
|
|
|
+ default:
|
|
|
+ vm.r.typeErrorResult(true, "Not a constructor")
|
|
|
+ }
|
|
|
+
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func (vm *vm) _nativeNew(f *nativeFuncObject, n int) {
|
|
|
+ if f.construct != nil {
|
|
|
+ args := make([]Value, n)
|
|
|
+ copy(args, vm.stack[vm.sp-n:])
|
|
|
+ vm.sp -= n
|
|
|
+ vm.stack[vm.sp-1] = f.construct(args)
|
|
|
+ } else {
|
|
|
+ vm.r.typeErrorResult(true, "Not a constructor")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type _typeof struct{}
|
|
|
+
|
|
|
+var typeof _typeof
|
|
|
+
|
|
|
+func (_typeof) exec(vm *vm) {
|
|
|
+ var r Value
|
|
|
+ switch v := vm.stack[vm.sp-1].(type) {
|
|
|
+ case valueUndefined, valueUnresolved:
|
|
|
+ r = stringUndefined
|
|
|
+ case valueNull:
|
|
|
+ r = stringObjectC
|
|
|
+ case *Object:
|
|
|
+ repeat:
|
|
|
+ switch s := v.self.(type) {
|
|
|
+ case *funcObject, *nativeFuncObject, *boundFuncObject:
|
|
|
+ r = stringFunction
|
|
|
+ case *lazyObject:
|
|
|
+ v.self = s.create(v)
|
|
|
+ goto repeat
|
|
|
+ default:
|
|
|
+ r = stringObjectC
|
|
|
+ }
|
|
|
+ case valueBool:
|
|
|
+ r = stringBoolean
|
|
|
+ case valueString:
|
|
|
+ r = stringString
|
|
|
+ case valueInt, valueFloat:
|
|
|
+ r = stringNumber
|
|
|
+ default:
|
|
|
+ panic(fmt.Errorf("Unknown type: %T", v))
|
|
|
+ }
|
|
|
+ vm.stack[vm.sp-1] = r
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type createArgs uint32
|
|
|
+
|
|
|
+func (formalArgs createArgs) exec(vm *vm) {
|
|
|
+ v := &Object{runtime: vm.r}
|
|
|
+ args := &argumentsObject{}
|
|
|
+ args.extensible = true
|
|
|
+ args.prototype = vm.r.global.ObjectPrototype
|
|
|
+ args.class = "Arguments"
|
|
|
+ v.self = args
|
|
|
+ args.val = v
|
|
|
+ args.length = vm.args
|
|
|
+ args.init()
|
|
|
+ i := 0
|
|
|
+ c := int(formalArgs)
|
|
|
+ if vm.args < c {
|
|
|
+ c = vm.args
|
|
|
+ }
|
|
|
+ for ; i < c; i++ {
|
|
|
+ args._put(strconv.Itoa(i), &mappedProperty{
|
|
|
+ valueProperty: valueProperty{
|
|
|
+ writable: true,
|
|
|
+ configurable: true,
|
|
|
+ enumerable: true,
|
|
|
+ },
|
|
|
+ v: &vm.stash.values[i],
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, v := range vm.stash.extraArgs {
|
|
|
+ args._put(strconv.Itoa(i), v)
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ args._putProp("callee", vm.stack[vm.sb-1], true, false, true)
|
|
|
+ vm.push(v)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type createArgsStrict uint32
|
|
|
+
|
|
|
+func (formalArgs createArgsStrict) exec(vm *vm) {
|
|
|
+ args := vm.r.newBaseObject(vm.r.global.ObjectPrototype, "Arguments")
|
|
|
+ i := 0
|
|
|
+ c := int(formalArgs)
|
|
|
+ if vm.args < c {
|
|
|
+ c = vm.args
|
|
|
+ }
|
|
|
+ for _, v := range vm.stash.values[:c] {
|
|
|
+ args._put(strconv.Itoa(i), v)
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, v := range vm.stash.extraArgs {
|
|
|
+ args._put(strconv.Itoa(i), v)
|
|
|
+ i++
|
|
|
+ }
|
|
|
+
|
|
|
+ args._putProp("length", intToValue(int64(vm.args)), true, false, true)
|
|
|
+ args._put("callee", vm.r.global.throwerProperty)
|
|
|
+ args._put("caller", vm.r.global.throwerProperty)
|
|
|
+ vm.push(args.val)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _enterWith struct{}
|
|
|
+
|
|
|
+var enterWith _enterWith
|
|
|
+
|
|
|
+func (_enterWith) exec(vm *vm) {
|
|
|
+ vm.newStash()
|
|
|
+ vm.stash.obj = vm.stack[vm.sp-1].ToObject(vm.r).self
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _leaveWith struct{}
|
|
|
+
|
|
|
+var leaveWith _leaveWith
|
|
|
+
|
|
|
+func (_leaveWith) exec(vm *vm) {
|
|
|
+ vm.stash = vm.stash.outer
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+func emptyIter() (propIterItem, iterNextFunc) {
|
|
|
+ return propIterItem{}, nil
|
|
|
+}
|
|
|
+
|
|
|
+type _enumerate struct{}
|
|
|
+
|
|
|
+var enumerate _enumerate
|
|
|
+
|
|
|
+func (_enumerate) exec(vm *vm) {
|
|
|
+ v := vm.stack[vm.sp-1]
|
|
|
+ if v == _undefined || v == _null {
|
|
|
+ vm.iterStack = append(vm.iterStack, iterStackItem{f: emptyIter})
|
|
|
+ } else {
|
|
|
+ vm.iterStack = append(vm.iterStack, iterStackItem{f: v.ToObject(vm.r).self.enumerate(false, true)})
|
|
|
+ }
|
|
|
+ vm.sp--
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type enumNext int32
|
|
|
+
|
|
|
+func (jmp enumNext) exec(vm *vm) {
|
|
|
+ l := len(vm.iterStack) - 1
|
|
|
+ item, n := vm.iterStack[l].f()
|
|
|
+ if n != nil {
|
|
|
+ vm.iterStack[l].val = newStringValue(item.name)
|
|
|
+ vm.iterStack[l].f = n
|
|
|
+ vm.pc++
|
|
|
+ } else {
|
|
|
+ vm.pc += int(jmp)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+type _enumGet struct{}
|
|
|
+
|
|
|
+var enumGet _enumGet
|
|
|
+
|
|
|
+func (_enumGet) exec(vm *vm) {
|
|
|
+ l := len(vm.iterStack) - 1
|
|
|
+ vm.push(vm.iterStack[l].val)
|
|
|
+ vm.pc++
|
|
|
+}
|
|
|
+
|
|
|
+type _enumPop struct{}
|
|
|
+
|
|
|
+var enumPop _enumPop
|
|
|
+
|
|
|
+func (_enumPop) exec(vm *vm) {
|
|
|
+ l := len(vm.iterStack) - 1
|
|
|
+ vm.iterStack[l] = iterStackItem{}
|
|
|
+ vm.iterStack = vm.iterStack[:l]
|
|
|
+ vm.pc++
|
|
|
+}
|