src.dualinventive.com/go/devsim/vendor/github.com/dop251/goja/vm.go

2515 lines
42 KiB
Go

package goja
import (
"fmt"
"log"
"math"
"strconv"
"sync"
"sync/atomic"
)
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
interrupted uint32
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)
}
if _, exists := s.names[name]; !exists {
s.names[name] = uint32(len(s.names))
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
interrupted := false
for !vm.halt {
if interrupted = atomic.LoadUint32(&vm.interrupted) != 0; interrupted {
break
}
vm.prg.code[vm.pc].exec(vm)
}
if interrupted {
vm.interruptLock.Lock()
v := &InterruptedError{
iface: vm.interruptVal,
}
atomic.StoreUint32(&vm.interrupted, 0)
vm.interruptVal = nil
vm.interruptLock.Unlock()
panic(v)
}
}
func (vm *vm) Interrupt(v interface{}) {
vm.interruptLock.Lock()
vm.interruptVal = v
atomic.StoreUint32(&vm.interrupted, 1)
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 := propertyDescr{
Getter: val,
Configurable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
}
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 := propertyDescr{
Setter: val,
Configurable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
}
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++
}
func (vm *vm) clearStack() {
stackTail := vm.stack[vm.sp:]
for i := range stackTail {
stackTail[i] = nil
}
vm.stack = vm.stack[:vm.sp]
}
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++
}