ai_old/goja/vm.go
2024-09-20 16:50:35 +08:00

5918 lines
116 KiB
Go

package goja
import (
"fmt"
"math"
"math/big"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/dop251/goja/unistring"
)
const (
maxInt = 1 << 53
tryPanicMarker = -2
)
type valueStack []Value
type stash struct {
values []Value
extraArgs []Value
names map[unistring.String]uint32
obj *Object
outer *stash
// If this is a top-level function stash, sets the type of the function. If set, dynamic var declarations
// created by direct eval go here.
funcType funcType
}
type context struct {
prg *Program
stash *stash
privEnv *privateEnv
newTarget Value
result Value
pc, sb int
args int
}
type tryFrame struct {
// holds an uncaught exception for the 'finally' block
exception *Exception
callStackLen, iterLen, refLen uint32
sp int32
stash *stash
privEnv *privateEnv
catchPos, finallyPos, finallyRet int32
}
type execCtx struct {
context
stack []Value
tryStack []tryFrame
iterStack []iterStackItem
refStack []ref
}
func (vm *vm) suspend(ectx *execCtx, tryStackLen, iterStackLen, refStackLen uint32) {
vm.saveCtx(&ectx.context)
ectx.stack = append(ectx.stack[:0], vm.stack[vm.sb-1:vm.sp]...)
if len(vm.tryStack) > int(tryStackLen) {
ectx.tryStack = append(ectx.tryStack[:0], vm.tryStack[tryStackLen:]...)
vm.tryStack = vm.tryStack[:tryStackLen]
sp := int32(vm.sb - 1)
for i := range ectx.tryStack {
tf := &ectx.tryStack[i]
tf.iterLen -= iterStackLen
tf.refLen -= refStackLen
tf.sp -= sp
}
}
if len(vm.iterStack) > int(iterStackLen) {
ectx.iterStack = append(ectx.iterStack[:0], vm.iterStack[iterStackLen:]...)
vm.iterStack = vm.iterStack[:iterStackLen]
}
if len(vm.refStack) > int(refStackLen) {
ectx.refStack = append(ectx.refStack[:0], vm.refStack[refStackLen:]...)
vm.refStack = vm.refStack[:refStackLen]
}
}
func (vm *vm) resume(ctx *execCtx) {
vm.restoreCtx(&ctx.context)
sp := vm.sp
vm.sb = sp + 1
vm.stack.expand(sp + len(ctx.stack))
copy(vm.stack[sp:], ctx.stack)
vm.sp += len(ctx.stack)
for i := range ctx.tryStack {
tf := &ctx.tryStack[i]
tf.callStackLen = uint32(len(vm.callStack))
tf.iterLen += uint32(len(vm.iterStack))
tf.refLen += uint32(len(vm.refStack))
tf.sp += int32(sp)
}
vm.tryStack = append(vm.tryStack, ctx.tryStack...)
vm.iterStack = append(vm.iterStack, ctx.iterStack...)
vm.refStack = append(vm.refStack, ctx.refStack...)
}
type iterStackItem struct {
val Value
f iterNextFunc
iter *iteratorRecord
}
type ref interface {
get() Value
set(Value)
init(Value)
refname() unistring.String
}
type stashRef struct {
n unistring.String
v *[]Value
idx int
}
func (r *stashRef) get() Value {
return nilSafe((*r.v)[r.idx])
}
func (r *stashRef) set(v Value) {
(*r.v)[r.idx] = v
}
func (r *stashRef) init(v Value) {
r.set(v)
}
func (r *stashRef) refname() unistring.String {
return r.n
}
type thisRef struct {
v *[]Value
idx int
}
func (r *thisRef) get() Value {
v := (*r.v)[r.idx]
if v == nil {
panic(referenceError("Must call super constructor in derived class before accessing 'this'"))
}
return v
}
func (r *thisRef) set(v Value) {
ptr := &(*r.v)[r.idx]
if *ptr != nil {
panic(referenceError("Super constructor may only be called once"))
}
*ptr = v
}
func (r *thisRef) init(v Value) {
r.set(v)
}
func (r *thisRef) refname() unistring.String {
return thisBindingName
}
type stashRefLex struct {
stashRef
}
func (r *stashRefLex) get() Value {
v := (*r.v)[r.idx]
if v == nil {
panic(errAccessBeforeInit)
}
return v
}
func (r *stashRefLex) set(v Value) {
p := &(*r.v)[r.idx]
if *p == nil {
panic(errAccessBeforeInit)
}
*p = v
}
func (r *stashRefLex) init(v Value) {
(*r.v)[r.idx] = v
}
type stashRefConst struct {
stashRefLex
strictConst bool
}
func (r *stashRefConst) set(v Value) {
if r.strictConst {
panic(errAssignToConst)
}
}
type objRef struct {
base *Object
name Value
this Value
strict bool
nameConverted bool
}
func (r *objRef) getKey() Value {
if !r.nameConverted {
r.name = toPropertyKey(r.name)
r.nameConverted = true
}
return r.name
}
func (r *objRef) get() Value {
return r.base.get(r.getKey(), r.this)
}
func (r *objRef) set(v Value) {
key := r.getKey()
if r.this != nil {
r.base.set(key, v, r.this, r.strict)
} else {
r.base.setOwn(key, v, r.strict)
}
}
func (r *objRef) init(v Value) {
if r.this != nil {
r.base.set(r.getKey(), v, r.this, r.strict)
} else {
r.base.setOwn(r.getKey(), v, r.strict)
}
}
func (r *objRef) refname() unistring.String {
return r.getKey().string()
}
type objStrRef struct {
base *Object
name unistring.String
this Value
strict bool
binding bool
}
func (r *objStrRef) get() Value {
return r.base.self.getStr(r.name, r.this)
}
func (r *objStrRef) set(v Value) {
if r.strict && r.binding && !r.base.self.hasOwnPropertyStr(r.name) {
panic(referenceError(fmt.Sprintf("%s is not defined", r.name)))
}
if r.this != nil {
r.base.setStr(r.name, v, r.this, r.strict)
} else {
r.base.self.setOwnStr(r.name, v, r.strict)
}
}
func (r *objStrRef) init(v Value) {
if r.this != nil {
r.base.setStr(r.name, v, r.this, r.strict)
} else {
r.base.self.setOwnStr(r.name, v, r.strict)
}
}
func (r *objStrRef) refname() unistring.String {
return r.name
}
type privateRefRes struct {
base *Object
name *resolvedPrivateName
}
func (p *privateRefRes) get() Value {
return (*getPrivatePropRes)(p.name)._get(p.base, p.base.runtime.vm)
}
func (p *privateRefRes) set(value Value) {
(*setPrivatePropRes)(p.name)._set(p.base, value, p.base.runtime.vm)
}
func (p *privateRefRes) init(value Value) {
panic("not supported")
}
func (p *privateRefRes) refname() unistring.String {
return p.name.string()
}
type privateRefId struct {
base *Object
id *privateId
}
func (p *privateRefId) get() Value {
return p.base.runtime.vm.getPrivateProp(p.base, p.id.name, p.id.typ, p.id.idx, p.id.isMethod)
}
func (p *privateRefId) set(value Value) {
p.base.runtime.vm.setPrivateProp(p.base, p.id.name, p.id.typ, p.id.idx, p.id.isMethod, value)
}
func (p *privateRefId) init(value Value) {
panic("not supported")
}
func (p *privateRefId) refname() unistring.String {
return p.id.string()
}
type unresolvedRef struct {
runtime *Runtime
name unistring.String
}
func (r *unresolvedRef) get() Value {
r.runtime.throwReferenceError(r.name)
panic("Unreachable")
}
func (r *unresolvedRef) set(Value) {
r.get()
}
func (r *unresolvedRef) init(Value) {
r.get()
}
func (r *unresolvedRef) refname() unistring.String {
return r.name
}
type vm struct {
r *Runtime
prg *Program
pc int
stack valueStack
sp, sb, args int
stash *stash
privEnv *privateEnv
callStack []context
iterStack []iterStackItem
refStack []ref
tryStack []tryFrame
newTarget Value
result Value
maxCallStackSize int
stashAllocs int
interrupted uint32
interruptVal interface{}
interruptLock sync.Mutex
curAsyncRunner *asyncRunner
profTracker *profTracker
}
type instruction interface {
exec(*vm)
}
func intToValue(i int64) Value {
if idx := 256 + i; idx >= 0 && idx < 256 {
return intCache[idx]
}
if i >= -maxInt && i <= maxInt {
return valueInt(i)
}
return valueFloat(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 toNumeric(value Value) Value {
switch v := value.(type) {
case valueInt, *valueBigInt:
return v
case valueFloat:
return floatToValue(float64(v))
case *Object:
primValue := v.toPrimitiveNumber()
if bigint, ok := primValue.(*valueBigInt); ok {
return bigint
}
return primValue.ToNumber()
}
return value.ToNumber()
}
func (s *valueStack) expand(idx int) {
if idx < len(*s) {
return
}
idx++
if idx < cap(*s) {
*s = (*s)[:idx]
} else {
var newCap int
if idx < 1024 {
newCap = idx * 2
} else {
newCap = (idx + 1025) &^ 1023
}
n := make([]Value, idx, newCap)
copy(n, *s)
*s = n
}
}
func stashObjHas(obj *Object, name unistring.String) bool {
if obj.self.hasPropertyStr(name) {
if unscopables, ok := obj.self.getSym(SymUnscopables, nil).(*Object); ok {
if b := unscopables.self.getStr(name, nil); b != nil {
return !b.ToBoolean()
}
}
return true
}
return false
}
func (s *stash) isVariable() bool {
return s.funcType != funcNone
}
func (s *stash) initByIdx(idx uint32, v Value) {
if s.obj != nil {
panic("Attempt to init by idx into an object scope")
}
s.values[idx] = v
}
func (s *stash) initByName(name unistring.String, v Value) {
if idx, exists := s.names[name]; exists {
s.values[idx&^maskTyp] = v
} else {
panic(referenceError(fmt.Sprintf("%s is not defined", name)))
}
}
func (s *stash) getByIdx(idx uint32) Value {
return s.values[idx]
}
func (s *stash) getByName(name unistring.String) (v Value, exists bool) {
if s.obj != nil {
if stashObjHas(s.obj, name) {
return nilSafe(s.obj.self.getStr(name, nil)), true
}
return nil, false
}
if idx, exists := s.names[name]; exists {
v := s.values[idx&^maskTyp]
if v == nil {
if idx&maskVar == 0 {
panic(errAccessBeforeInit)
} else {
v = _undefined
}
}
return v, true
}
return nil, false
}
func (s *stash) getRefByName(name unistring.String, strict bool) ref {
if obj := s.obj; obj != nil {
if stashObjHas(obj, name) {
return &objStrRef{
base: obj,
name: name,
strict: strict,
binding: true,
}
}
} else {
if idx, exists := s.names[name]; exists {
if idx&maskVar == 0 {
if idx&maskConst == 0 {
return &stashRefLex{
stashRef: stashRef{
n: name,
v: &s.values,
idx: int(idx &^ maskTyp),
},
}
} else {
return &stashRefConst{
stashRefLex: stashRefLex{
stashRef: stashRef{
n: name,
v: &s.values,
idx: int(idx &^ maskTyp),
},
},
strictConst: strict || (idx&maskStrict != 0),
}
}
} else {
return &stashRef{
n: name,
v: &s.values,
idx: int(idx &^ maskTyp),
}
}
}
}
return nil
}
func (s *stash) createBinding(name unistring.String, deletable bool) {
if s.names == nil {
s.names = make(map[unistring.String]uint32)
}
if _, exists := s.names[name]; !exists {
idx := uint32(len(s.names)) | maskVar
if deletable {
idx |= maskDeletable
}
s.names[name] = idx
s.values = append(s.values, _undefined)
}
}
func (s *stash) createLexBinding(name unistring.String, isConst bool) {
if s.names == nil {
s.names = make(map[unistring.String]uint32)
}
if _, exists := s.names[name]; !exists {
idx := uint32(len(s.names))
if isConst {
idx |= maskConst | maskStrict
}
s.names[name] = idx
s.values = append(s.values, nil)
}
}
func (s *stash) deleteBinding(name unistring.String) {
delete(s.names, name)
}
func (vm *vm) newStash() {
vm.stash = &stash{
outer: vm.stash,
}
vm.stashAllocs++
}
func (vm *vm) init() {
vm.sb = -1
vm.stash = &vm.r.global.stash
vm.maxCallStackSize = math.MaxInt32
}
func (vm *vm) halted() bool {
pc := vm.pc
return pc < 0 || pc >= len(vm.prg.code)
}
func (vm *vm) run() {
if vm.profTracker != nil && !vm.runWithProfiler() {
return
}
count := 0
interrupted := false
for {
if count == 0 {
if atomic.LoadInt32(&globalProfiler.enabled) == 1 && !vm.runWithProfiler() {
return
}
count = 100
} else {
count--
}
if interrupted = atomic.LoadUint32(&vm.interrupted) != 0; interrupted {
break
}
pc := vm.pc
if pc < 0 || pc >= len(vm.prg.code) {
break
}
vm.prg.code[pc].exec(vm)
}
if interrupted {
vm.interruptLock.Lock()
v := &InterruptedError{
iface: vm.interruptVal,
}
v.stack = vm.captureStack(nil, 0)
vm.interruptLock.Unlock()
panic(v)
}
}
func (vm *vm) runWithProfiler() bool {
pt := vm.profTracker
if pt == nil {
pt = globalProfiler.p.registerVm()
vm.profTracker = pt
defer func() {
atomic.StoreInt32(&vm.profTracker.finished, 1)
vm.profTracker = nil
}()
}
interrupted := false
for {
if interrupted = atomic.LoadUint32(&vm.interrupted) != 0; interrupted {
return true
}
pc := vm.pc
if pc < 0 || pc >= len(vm.prg.code) {
break
}
vm.prg.code[pc].exec(vm)
req := atomic.LoadInt32(&pt.req)
if req == profReqStop {
return true
}
if req == profReqDoSample {
pt.stop = time.Now()
pt.numFrames = len(vm.r.CaptureCallStack(len(pt.frames), pt.frames[:0]))
pt.frames[0].pc = pc
atomic.StoreInt32(&pt.req, profReqSampleReady)
}
}
return false
}
func (vm *vm) Interrupt(v interface{}) {
vm.interruptLock.Lock()
vm.interruptVal = v
atomic.StoreUint32(&vm.interrupted, 1)
vm.interruptLock.Unlock()
}
func (vm *vm) ClearInterrupt() {
atomic.StoreUint32(&vm.interrupted, 0)
}
func getFuncName(stack []Value, sb int) unistring.String {
if sb > 0 {
if f, ok := stack[sb-1].(*Object); ok {
if _, isProxy := f.self.(*proxyObject); isProxy {
return "proxy"
}
return nilSafe(f.self.getStr("name", nil)).string()
}
}
return ""
}
func (vm *vm) captureStack(stack []StackFrame, ctxOffset int) []StackFrame {
// Unroll the context stack
if vm.prg != nil || vm.sb > 0 {
var funcName unistring.String
if vm.prg != nil {
funcName = vm.prg.funcName
} else {
funcName = getFuncName(vm.stack, vm.sb)
}
stack = append(stack, StackFrame{prg: vm.prg, pc: vm.pc, funcName: funcName})
}
for i := len(vm.callStack) - 1; i > ctxOffset-1; i-- {
frame := &vm.callStack[i]
if frame.prg != nil || frame.sb > 0 {
var funcName unistring.String
if prg := frame.prg; prg != nil {
funcName = prg.funcName
} else {
funcName = getFuncName(vm.stack, frame.sb)
}
stack = append(stack, StackFrame{prg: vm.callStack[i].prg, pc: frame.pc, funcName: funcName})
}
}
if ctxOffset == 0 && vm.curAsyncRunner != nil {
stack = vm.captureAsyncStack(stack, vm.curAsyncRunner)
}
return stack
}
func (vm *vm) captureAsyncStack(stack []StackFrame, runner *asyncRunner) []StackFrame {
if promise, _ := runner.promiseCap.promise.self.(*Promise); promise != nil {
if len(promise.fulfillReactions) == 1 {
if r := promise.fulfillReactions[0].asyncRunner; r != nil {
ctx := &r.gen.ctx
if ctx.prg != nil || ctx.sb > 0 {
var funcName unistring.String
if prg := ctx.prg; prg != nil {
funcName = prg.funcName
} else {
funcName = getFuncName(ctx.stack, 1)
}
stack = append(stack, StackFrame{prg: ctx.prg, pc: ctx.pc, funcName: funcName})
}
stack = vm.captureAsyncStack(stack, r)
}
}
}
return stack
}
func (vm *vm) pushTryFrame(catchPos, finallyPos int32) {
vm.tryStack = append(vm.tryStack, tryFrame{
callStackLen: uint32(len(vm.callStack)),
iterLen: uint32(len(vm.iterStack)),
refLen: uint32(len(vm.refStack)),
sp: int32(vm.sp),
stash: vm.stash,
privEnv: vm.privEnv,
catchPos: catchPos,
finallyPos: finallyPos,
finallyRet: -1,
})
}
func (vm *vm) popTryFrame() {
vm.tryStack = vm.tryStack[:len(vm.tryStack)-1]
}
func (vm *vm) restoreStacks(iterLen, refLen uint32) (ex *Exception) {
// Restore other stacks
iterTail := vm.iterStack[iterLen:]
for i := len(iterTail) - 1; i >= 0; i-- {
if iter := iterTail[i].iter; iter != nil {
ex1 := vm.try(func() {
iter.returnIter()
})
if ex1 != nil && ex == nil {
ex = ex1
}
}
iterTail[i] = iterStackItem{}
}
vm.iterStack = vm.iterStack[:iterLen]
refTail := vm.refStack[refLen:]
for i := range refTail {
refTail[i] = nil
}
vm.refStack = vm.refStack[:refLen]
return
}
func (vm *vm) handleThrow(arg interface{}) *Exception {
ex := vm.exceptionFromValue(arg)
for len(vm.tryStack) > 0 {
tf := &vm.tryStack[len(vm.tryStack)-1]
if tf.catchPos == -1 && tf.finallyPos == -1 || ex == nil && tf.catchPos != tryPanicMarker {
tf.exception = nil
vm.popTryFrame()
continue
}
if int(tf.callStackLen) < len(vm.callStack) {
ctx := &vm.callStack[tf.callStackLen]
vm.prg, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args =
ctx.prg, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args
vm.callStack = vm.callStack[:tf.callStackLen]
}
vm.sp = int(tf.sp)
vm.stash = tf.stash
vm.privEnv = tf.privEnv
_ = vm.restoreStacks(tf.iterLen, tf.refLen)
if tf.catchPos == tryPanicMarker {
break
}
if tf.catchPos >= 0 {
// exception is caught
vm.push(ex.val)
vm.pc = int(tf.catchPos)
tf.catchPos = -1
return nil
}
if tf.finallyPos >= 0 {
// no 'catch' block, but there is a 'finally' block
tf.exception = ex
vm.pc = int(tf.finallyPos)
tf.finallyPos = -1
tf.finallyRet = -1
return nil
}
}
if ex == nil {
panic(arg)
}
return ex
}
// Calls to this method must be made from the run() loop and must be the last statement before 'return'.
// In all other cases exceptions must be thrown using panic().
func (vm *vm) throw(v interface{}) {
if ex := vm.handleThrow(v); ex != nil {
panic(ex)
}
}
func (vm *vm) try(f func()) (ex *Exception) {
vm.pushTryFrame(tryPanicMarker, -1)
defer vm.popTryFrame()
defer func() {
if x := recover(); x != nil {
ex = vm.handleThrow(x)
}
}()
f()
return
}
func (vm *vm) runTry() (ex *Exception) {
vm.pushTryFrame(tryPanicMarker, -1)
defer vm.popTryFrame()
for {
ex = vm.runTryInner()
if ex != nil || vm.halted() {
return
}
}
}
func (vm *vm) runTryInner() (ex *Exception) {
defer func() {
if x := recover(); x != nil {
ex = vm.handleThrow(x)
}
}()
vm.run()
return
}
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, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args =
vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args
}
func (vm *vm) pushCtx() {
if len(vm.callStack) > vm.maxCallStackSize {
ex := &StackOverflowError{}
ex.stack = vm.captureStack(nil, 0)
panic(ex)
}
vm.callStack = append(vm.callStack, context{})
ctx := &vm.callStack[len(vm.callStack)-1]
vm.saveCtx(ctx)
}
func (vm *vm) restoreCtx(ctx *context) {
vm.prg, vm.stash, vm.privEnv, vm.newTarget, vm.result, vm.pc, vm.sb, vm.args =
ctx.prg, ctx.stash, ctx.privEnv, ctx.newTarget, ctx.result, ctx.pc, ctx.sb, ctx.args
}
func (vm *vm) popCtx() {
l := len(vm.callStack) - 1
ctx := &vm.callStack[l]
vm.restoreCtx(ctx)
if ctx.prg != nil {
*ctx = context{}
}
vm.callStack = vm.callStack[:l]
}
func (vm *vm) toCallee(v Value) *Object {
if obj, ok := v.(*Object); ok {
return obj
}
switch unresolved := v.(type) {
case valueUnresolved:
unresolved.throw()
panic("Unreachable")
case memberUnresolved:
panic(vm.r.NewTypeError("Object has no member '%s'", unresolved.ref))
}
panic(vm.r.NewTypeError("Value is not an object: %s", v.toString()))
}
type loadVal struct {
v Value
}
func (l loadVal) exec(vm *vm) {
vm.push(l.v)
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 _saveResult struct{}
var saveResult _saveResult
func (_saveResult) exec(vm *vm) {
vm.sp--
vm.result = vm.stack[vm.sp]
vm.pc++
}
type _loadResult struct{}
var loadResult _loadResult
func (_loadResult) exec(vm *vm) {
vm.push(vm.result)
vm.pc++
}
type _clearResult struct{}
var clearResult _clearResult
func (_clearResult) exec(vm *vm) {
vm.result = _undefined
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 -- var<l-1>
// l == 0 -- this
if l > 0 {
vm.push(nilSafe(vm.stack[vm.sb+vm.args+int(l)]))
} else {
vm.push(vm.stack[vm.sb])
}
vm.pc++
}
type loadStack1 int
func (l loadStack1) exec(vm *vm) {
// args are in stash
// l > 0 -- var<l-1>
// l == 0 -- this
if l > 0 {
vm.push(nilSafe(vm.stack[vm.sb+int(l)]))
} else {
vm.push(vm.stack[vm.sb])
}
vm.pc++
}
type loadStackLex int
func (l loadStackLex) exec(vm *vm) {
// l < 0 -- arg<-l-1>
// l > 0 -- var<l-1>
// l == 0 -- this
var p *Value
if l <= 0 {
arg := int(-l)
if arg > vm.args {
vm.push(_undefined)
vm.pc++
return
} else {
p = &vm.stack[vm.sb+arg]
}
} else {
p = &vm.stack[vm.sb+vm.args+int(l)]
}
if *p == nil {
vm.throw(errAccessBeforeInit)
return
}
vm.push(*p)
vm.pc++
}
type loadStack1Lex int
func (l loadStack1Lex) exec(vm *vm) {
p := &vm.stack[vm.sb+int(l)]
if *p == nil {
vm.throw(errAccessBeforeInit)
return
}
vm.push(*p)
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 -- var<l-1>
if s > 0 {
vm.stack[vm.sb+vm.args+s] = vm.stack[vm.sp-1]
} else {
panic("Illegal stack var index")
}
vm.pc++
}
func (vm *vm) storeStack1(s int) {
// args are in stash
// l > 0 -- var<l-1>
if s > 0 {
vm.stack[vm.sb+s] = vm.stack[vm.sp-1]
} else {
panic("Illegal stack var index")
}
vm.pc++
}
func (vm *vm) storeStackLex(s int) {
// l < 0 -- arg<-l-1>
// l > 0 -- var<l-1>
var p *Value
if s < 0 {
p = &vm.stack[vm.sb-s]
} else {
p = &vm.stack[vm.sb+vm.args+s]
}
if *p != nil {
*p = vm.stack[vm.sp-1]
} else {
panic(errAccessBeforeInit)
}
vm.pc++
}
func (vm *vm) storeStack1Lex(s int) {
// args are in stash
// s > 0 -- var<l-1>
if s <= 0 {
panic("Illegal stack var index")
}
p := &vm.stack[vm.sb+s]
if *p != nil {
*p = vm.stack[vm.sp-1]
} else {
panic(errAccessBeforeInit)
}
vm.pc++
}
func (vm *vm) initStack(s int) {
if s <= 0 {
vm.stack[vm.sb-s] = vm.stack[vm.sp-1]
} else {
vm.stack[vm.sb+vm.args+s] = vm.stack[vm.sp-1]
}
vm.pc++
}
func (vm *vm) initStack1(s int) {
if s <= 0 {
panic("Illegal stack var index")
}
vm.stack[vm.sb+s] = vm.stack[vm.sp-1]
vm.pc++
}
type storeStack int
func (s storeStack) exec(vm *vm) {
vm.storeStack(int(s))
}
type storeStack1 int
func (s storeStack1) exec(vm *vm) {
vm.storeStack1(int(s))
}
type storeStackLex int
func (s storeStackLex) exec(vm *vm) {
vm.storeStackLex(int(s))
}
type storeStack1Lex int
func (s storeStack1Lex) exec(vm *vm) {
vm.storeStack1Lex(int(s))
}
type initStack int
func (s initStack) exec(vm *vm) {
vm.initStack(int(s))
}
type initStackP int
func (s initStackP) exec(vm *vm) {
vm.initStack(int(s))
vm.sp--
}
type initStack1 int
func (s initStack1) exec(vm *vm) {
vm.initStack1(int(s))
}
type initStack1P int
func (s initStack1P) exec(vm *vm) {
vm.initStack1(int(s))
vm.sp--
}
type storeStackP int
func (s storeStackP) exec(vm *vm) {
vm.storeStack(int(s))
vm.sp--
}
type storeStack1P int
func (s storeStack1P) exec(vm *vm) {
vm.storeStack1(int(s))
vm.sp--
}
type storeStackLexP int
func (s storeStackLexP) exec(vm *vm) {
vm.storeStackLex(int(s))
vm.sp--
}
type storeStack1LexP int
func (s storeStack1LexP) exec(vm *vm) {
vm.storeStack1Lex(int(s))
vm.sp--
}
type _toNumber struct{}
var toNumber _toNumber
func (_toNumber) exec(vm *vm) {
vm.stack[vm.sp-1] = toNumeric(vm.stack[vm.sp-1])
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.toPrimitive()
}
if o, ok := right.(*Object); ok {
right = o.toPrimitive()
}
var ret Value
leftString, isLeftString := left.(String)
rightString, isRightString := right.(String)
if isLeftString || isRightString {
if !isLeftString {
leftString = left.toString()
}
if !isRightString {
rightString = right.toString()
}
ret = leftString.Concat(rightString)
} else {
switch left := left.(type) {
case valueInt:
switch right := right.(type) {
case valueInt:
ret = intToValue(int64(left) + int64(right))
case *valueBigInt:
panic(errMixBigIntType)
default:
ret = floatToValue(float64(left) + right.ToFloat())
}
case *valueBigInt:
if right, ok := right.(*valueBigInt); ok {
ret = (*valueBigInt)(new(big.Int).Add((*big.Int)(left), (*big.Int)(right)))
} else {
panic(errMixBigIntType)
}
default:
if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
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]
left = toNumeric(left)
right = toNumeric(right)
var result Value
switch left := left.(type) {
case valueInt:
switch right := right.(type) {
case valueInt:
result = intToValue(int64(left) - int64(right))
goto end
case *valueBigInt:
panic(errMixBigIntType)
}
case valueFloat:
if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
case *valueBigInt:
if right, ok := right.(*valueBigInt); ok {
result = (*valueBigInt)(new(big.Int).Sub((*big.Int)(left), (*big.Int)(right)))
goto end
}
panic(errMixBigIntType)
}
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 := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
switch left := left.(type) {
case valueInt:
switch right := right.(type) {
case valueInt:
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(int64(res))
goto end
}
case *valueBigInt:
panic(errMixBigIntType)
}
case valueFloat:
if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
case *valueBigInt:
if right, ok := right.(*valueBigInt); ok {
result = (*valueBigInt)(new(big.Int).Mul((*big.Int)(left), (*big.Int)(right)))
goto end
}
panic(errMixBigIntType)
}
result = floatToValue(left.ToFloat() * right.ToFloat())
end:
vm.sp--
vm.stack[vm.sp-1] = result
vm.pc++
}
type _exp struct{}
var exp _exp
func (_exp) exec(vm *vm) {
vm.sp--
x := vm.stack[vm.sp-1]
y := vm.stack[vm.sp]
x = toNumeric(x)
y = toNumeric(y)
var result Value
if x, ok := x.(*valueBigInt); ok {
if y, ok := y.(*valueBigInt); ok {
if (*big.Int)(y).Cmp(big.NewInt(0)) < 0 {
panic(vm.r.newError(vm.r.getRangeError(), "exponent must be positive"))
}
result = (*valueBigInt)(new(big.Int).Exp((*big.Int)(x), (*big.Int)(y), nil))
goto end
}
panic(errMixBigIntType)
} else if _, ok := y.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = pow(x, y)
end:
vm.stack[vm.sp-1] = result
vm.pc++
}
type _div struct{}
var div _div
func (_div) exec(vm *vm) {
leftValue := toNumeric(vm.stack[vm.sp-2])
rightValue := toNumeric(vm.stack[vm.sp-1])
var (
result Value
left, right float64
)
if left, ok := leftValue.(*valueBigInt); ok {
if right, ok := rightValue.(*valueBigInt); ok {
if (*big.Int)(right).Cmp(big.NewInt(0)) == 0 {
panic(vm.r.newError(vm.r.getRangeError(), "Division by zero"))
}
if (*big.Int)(left).CmpAbs((*big.Int)(right)) < 0 {
result = (*valueBigInt)(big.NewInt(0))
} else {
i, _ := new(big.Int).QuoRem((*big.Int)(left), (*big.Int)(right), big.NewInt(0))
result = (*valueBigInt)(i)
}
goto end
}
panic(errMixBigIntType)
} else if _, ok := rightValue.(*valueBigInt); ok {
panic(errMixBigIntType)
}
left, right = leftValue.ToFloat(), rightValue.ToFloat()
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 := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
switch left := left.(type) {
case valueInt:
switch right := right.(type) {
case valueInt:
if right == 0 {
result = _NaN
goto end
}
r := left % right
if r == 0 && left < 0 {
result = _negativeZero
} else {
result = intToValue(int64(left % right))
}
goto end
case *valueBigInt:
panic(errMixBigIntType)
}
case valueFloat:
if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
case *valueBigInt:
if right, ok := right.(*valueBigInt); ok {
switch {
case (*big.Int)(right).Cmp(big.NewInt(0)) == 0:
panic(vm.r.newError(vm.r.getRangeError(), "Division by zero"))
case (*big.Int)(left).Cmp(big.NewInt(0)) < 0:
abs := new(big.Int).Abs((*big.Int)(left))
v := new(big.Int).Mod(abs, (*big.Int)(right))
result = (*valueBigInt)(v.Neg(v))
default:
result = (*valueBigInt)(new(big.Int).Mod((*big.Int)(left), (*big.Int)(right)))
}
goto end
}
panic(errMixBigIntType)
}
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
switch n := toNumeric(operand).(type) {
case *valueBigInt:
result = (*valueBigInt)(new(big.Int).Neg((*big.Int)(n)))
case valueInt:
if n == 0 {
result = _negativeZero
} else {
result = -n
}
default:
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]
switch n := v.(type) {
case *valueBigInt:
v = (*valueBigInt)(new(big.Int).Add((*big.Int)(n), big.NewInt(1)))
case valueInt:
v = intToValue(int64(n + 1))
default:
v = valueFloat(n.ToFloat() + 1)
}
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]
switch n := v.(type) {
case *valueBigInt:
v = (*valueBigInt)(new(big.Int).Sub((*big.Int)(n), big.NewInt(1)))
case valueInt:
v = intToValue(int64(n - 1))
default:
v = valueFloat(n.ToFloat() - 1)
}
vm.stack[vm.sp-1] = v
vm.pc++
}
type _and struct{}
var and _and
func (_and) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
if left, ok := left.(*valueBigInt); ok {
if right, ok := right.(*valueBigInt); ok {
result = (*valueBigInt)(new(big.Int).And((*big.Int)(left), (*big.Int)(right)))
goto end
}
panic(errMixBigIntType)
} else if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = intToValue(int64(toInt32(left) & toInt32(right)))
end:
vm.stack[vm.sp-2] = result
vm.sp--
vm.pc++
}
type _or struct{}
var or _or
func (_or) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
if left, ok := left.(*valueBigInt); ok {
if right, ok := right.(*valueBigInt); ok {
result = (*valueBigInt)(new(big.Int).Or((*big.Int)(left), (*big.Int)(right)))
goto end
}
panic(errMixBigIntType)
} else if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = intToValue(int64(toInt32(left) | toInt32(right)))
end:
vm.stack[vm.sp-2] = result
vm.sp--
vm.pc++
}
type _xor struct{}
var xor _xor
func (_xor) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
if left, ok := left.(*valueBigInt); ok {
if right, ok := right.(*valueBigInt); ok {
result = (*valueBigInt)(new(big.Int).Xor((*big.Int)(left), (*big.Int)(right)))
goto end
}
panic(errMixBigIntType)
} else if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = intToValue(int64(toInt32(left) ^ toInt32(right)))
end:
vm.stack[vm.sp-2] = result
vm.sp--
vm.pc++
}
type _bnot struct{}
var bnot _bnot
func (_bnot) exec(vm *vm) {
v := vm.stack[vm.sp-1]
switch n := toNumeric(v).(type) {
case *valueBigInt:
v = (*valueBigInt)(new(big.Int).Not((*big.Int)(n)))
default:
v = intToValue(int64(^toInt32(n)))
}
vm.stack[vm.sp-1] = v
vm.pc++
}
type _sal struct{}
var sal _sal
func (_sal) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
if left, ok := left.(*valueBigInt); ok {
if right, ok := right.(*valueBigInt); ok {
n := uint((*big.Int)(right).Uint64())
if (*big.Int)(right).Sign() < 0 {
result = (*valueBigInt)(new(big.Int).Rsh((*big.Int)(left), n))
} else {
result = (*valueBigInt)(new(big.Int).Lsh((*big.Int)(left), n))
}
goto end
}
panic(errMixBigIntType)
} else if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = intToValue(int64(toInt32(left) << (toUint32(right) & 0x1F)))
end:
vm.stack[vm.sp-2] = result
vm.sp--
vm.pc++
}
type _sar struct{}
var sar _sar
func (_sar) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
var result Value
if left, ok := left.(*valueBigInt); ok {
if right, ok := right.(*valueBigInt); ok {
n := uint((*big.Int)(right).Uint64())
if (*big.Int)(right).Sign() < 0 {
result = (*valueBigInt)(new(big.Int).Lsh((*big.Int)(left), n))
} else {
result = (*valueBigInt)(new(big.Int).Rsh((*big.Int)(left), n))
}
goto end
}
panic(errMixBigIntType)
} else if _, ok := right.(*valueBigInt); ok {
panic(errMixBigIntType)
}
result = intToValue(int64(toInt32(left) >> (toUint32(right) & 0x1F)))
end:
vm.stack[vm.sp-2] = result
vm.sp--
vm.pc++
}
type _shr struct{}
var shr _shr
func (_shr) exec(vm *vm) {
left := toNumeric(vm.stack[vm.sp-2])
right := toNumeric(vm.stack[vm.sp-1])
if _, ok := left.(*valueBigInt); ok {
_ = toNumeric(right)
panic(vm.r.NewTypeError("BigInts have no unsigned right shift, use >> instead"))
} else if _, ok := right.(*valueBigInt); ok {
panic(vm.r.NewTypeError("BigInts have no unsigned right shift, use >> instead"))
}
vm.stack[vm.sp-2] = intToValue(int64(toUint32(left) >> (toUint32(right) & 0x1F)))
vm.sp--
vm.pc++
}
type jump int32
func (j jump) exec(vm *vm) {
vm.pc += int(j)
}
type _toPropertyKey struct{}
func (_toPropertyKey) exec(vm *vm) {
p := vm.sp - 1
vm.stack[p] = toPropertyKey(vm.stack[p])
vm.pc++
}
type _toString struct{}
func (_toString) exec(vm *vm) {
p := vm.sp - 1
vm.stack[p] = vm.stack[p].toString()
vm.pc++
}
type _getElemRef struct{}
var getElemRef _getElemRef
func (_getElemRef) exec(vm *vm) {
obj := vm.stack[vm.sp-2].ToObject(vm.r)
propName := vm.stack[vm.sp-1]
vm.refStack = append(vm.refStack, &objRef{
base: obj,
name: propName,
})
vm.sp -= 2
vm.pc++
}
type _getElemRefRecv struct{}
var getElemRefRecv _getElemRefRecv
func (_getElemRefRecv) exec(vm *vm) {
obj := vm.stack[vm.sp-1].ToObject(vm.r)
propName := vm.stack[vm.sp-2]
vm.refStack = append(vm.refStack, &objRef{
base: obj,
name: propName,
this: vm.stack[vm.sp-3],
})
vm.sp -= 3
vm.pc++
}
type _getElemRefStrict struct{}
var getElemRefStrict _getElemRefStrict
func (_getElemRefStrict) exec(vm *vm) {
obj := vm.stack[vm.sp-2].ToObject(vm.r)
propName := vm.stack[vm.sp-1]
vm.refStack = append(vm.refStack, &objRef{
base: obj,
name: propName,
strict: true,
})
vm.sp -= 2
vm.pc++
}
type _getElemRefRecvStrict struct{}
var getElemRefRecvStrict _getElemRefRecvStrict
func (_getElemRefRecvStrict) exec(vm *vm) {
obj := vm.stack[vm.sp-1].ToObject(vm.r)
propName := vm.stack[vm.sp-2]
vm.refStack = append(vm.refStack, &objRef{
base: obj,
name: propName,
this: vm.stack[vm.sp-3],
strict: true,
})
vm.sp -= 3
vm.pc++
}
type _setElem struct{}
var setElem _setElem
func (_setElem) exec(vm *vm) {
obj := vm.stack[vm.sp-3].ToObject(vm.r)
propName := toPropertyKey(vm.stack[vm.sp-2])
val := vm.stack[vm.sp-1]
obj.setOwn(propName, val, false)
vm.sp -= 2
vm.stack[vm.sp-1] = val
vm.pc++
}
type _setElem1 struct{}
var setElem1 _setElem1
func (_setElem1) exec(vm *vm) {
obj := vm.stack[vm.sp-3].ToObject(vm.r)
propName := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
obj.setOwn(propName, val, true)
vm.sp -= 2
vm.pc++
}
type _setElem1Named struct{}
var setElem1Named _setElem1Named
func (_setElem1Named) exec(vm *vm) {
receiver := vm.stack[vm.sp-3]
base := receiver.ToObject(vm.r)
propName := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
vm.r.toObject(val).self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: funcName("", propName),
Configurable: FLAG_TRUE,
}, true)
base.set(propName, val, receiver, true)
vm.sp -= 2
vm.pc++
}
type defineMethod struct {
enumerable bool
}
func (d *defineMethod) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-3])
propName := vm.stack[vm.sp-2]
method := vm.r.toObject(vm.stack[vm.sp-1])
method.self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: funcName("", propName),
Configurable: FLAG_TRUE,
}, true)
obj.defineOwnProperty(propName, PropertyDescriptor{
Value: method,
Writable: FLAG_TRUE,
Configurable: FLAG_TRUE,
Enumerable: ToFlag(d.enumerable),
}, true)
vm.sp -= 2
vm.pc++
}
type _setElemP struct{}
var setElemP _setElemP
func (_setElemP) exec(vm *vm) {
obj := vm.stack[vm.sp-3].ToObject(vm.r)
propName := toPropertyKey(vm.stack[vm.sp-2])
val := vm.stack[vm.sp-1]
obj.setOwn(propName, val, false)
vm.sp -= 3
vm.pc++
}
type _setElemStrict struct{}
var setElemStrict _setElemStrict
func (_setElemStrict) exec(vm *vm) {
propName := toPropertyKey(vm.stack[vm.sp-2])
receiver := vm.stack[vm.sp-3]
val := vm.stack[vm.sp-1]
if receiverObj, ok := receiver.(*Object); ok {
receiverObj.setOwn(propName, val, true)
} else {
base := receiver.ToObject(vm.r)
base.set(propName, val, receiver, true)
}
vm.sp -= 2
vm.stack[vm.sp-1] = val
vm.pc++
}
type _setElemRecv struct{}
var setElemRecv _setElemRecv
func (_setElemRecv) exec(vm *vm) {
receiver := vm.stack[vm.sp-4]
propName := toPropertyKey(vm.stack[vm.sp-3])
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
if obj, ok := o.(*Object); ok {
obj.set(propName, val, receiver, false)
} else {
base := o.ToObject(vm.r)
base.set(propName, val, receiver, false)
}
vm.sp -= 3
vm.stack[vm.sp-1] = val
vm.pc++
}
type _setElemRecvStrict struct{}
var setElemRecvStrict _setElemRecvStrict
func (_setElemRecvStrict) exec(vm *vm) {
receiver := vm.stack[vm.sp-4]
propName := toPropertyKey(vm.stack[vm.sp-3])
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
if obj, ok := o.(*Object); ok {
obj.set(propName, val, receiver, true)
} else {
base := o.ToObject(vm.r)
base.set(propName, val, receiver, true)
}
vm.sp -= 3
vm.stack[vm.sp-1] = val
vm.pc++
}
type _setElemStrictP struct{}
var setElemStrictP _setElemStrictP
func (_setElemStrictP) exec(vm *vm) {
propName := toPropertyKey(vm.stack[vm.sp-2])
receiver := vm.stack[vm.sp-3]
val := vm.stack[vm.sp-1]
if receiverObj, ok := receiver.(*Object); ok {
receiverObj.setOwn(propName, val, true)
} else {
base := receiver.ToObject(vm.r)
base.set(propName, val, receiver, true)
}
vm.sp -= 3
vm.pc++
}
type _setElemRecvP struct{}
var setElemRecvP _setElemRecvP
func (_setElemRecvP) exec(vm *vm) {
receiver := vm.stack[vm.sp-4]
propName := toPropertyKey(vm.stack[vm.sp-3])
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
if obj, ok := o.(*Object); ok {
obj.set(propName, val, receiver, false)
} else {
base := o.ToObject(vm.r)
base.set(propName, val, receiver, false)
}
vm.sp -= 4
vm.pc++
}
type _setElemRecvStrictP struct{}
var setElemRecvStrictP _setElemRecvStrictP
func (_setElemRecvStrictP) exec(vm *vm) {
receiver := vm.stack[vm.sp-4]
propName := toPropertyKey(vm.stack[vm.sp-3])
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
if obj, ok := o.(*Object); ok {
obj.set(propName, val, receiver, true)
} else {
base := o.ToObject(vm.r)
base.set(propName, val, receiver, true)
}
vm.sp -= 4
vm.pc++
}
type _deleteElem struct{}
var deleteElem _deleteElem
func (_deleteElem) exec(vm *vm) {
obj := vm.stack[vm.sp-2].ToObject(vm.r)
propName := toPropertyKey(vm.stack[vm.sp-1])
if obj.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.stack[vm.sp-2].ToObject(vm.r)
propName := toPropertyKey(vm.stack[vm.sp-1])
obj.delete(propName, true)
vm.stack[vm.sp-2] = valueTrue
vm.sp--
vm.pc++
}
type deleteProp unistring.String
func (d deleteProp) exec(vm *vm) {
obj := vm.stack[vm.sp-1].ToObject(vm.r)
if obj.self.deleteStr(unistring.String(d), false) {
vm.stack[vm.sp-1] = valueTrue
} else {
vm.stack[vm.sp-1] = valueFalse
}
vm.pc++
}
type deletePropStrict unistring.String
func (d deletePropStrict) exec(vm *vm) {
obj := vm.stack[vm.sp-1].ToObject(vm.r)
obj.self.deleteStr(unistring.String(d), true)
vm.stack[vm.sp-1] = valueTrue
vm.pc++
}
type getPropRef unistring.String
func (p getPropRef) exec(vm *vm) {
vm.refStack = append(vm.refStack, &objStrRef{
base: vm.stack[vm.sp-1].ToObject(vm.r),
name: unistring.String(p),
})
vm.sp--
vm.pc++
}
type getPropRefRecv unistring.String
func (p getPropRefRecv) exec(vm *vm) {
vm.refStack = append(vm.refStack, &objStrRef{
this: vm.stack[vm.sp-2],
base: vm.stack[vm.sp-1].ToObject(vm.r),
name: unistring.String(p),
})
vm.sp -= 2
vm.pc++
}
type getPropRefStrict unistring.String
func (p getPropRefStrict) exec(vm *vm) {
vm.refStack = append(vm.refStack, &objStrRef{
base: vm.stack[vm.sp-1].ToObject(vm.r),
name: unistring.String(p),
strict: true,
})
vm.sp--
vm.pc++
}
type getPropRefRecvStrict unistring.String
func (p getPropRefRecvStrict) exec(vm *vm) {
vm.refStack = append(vm.refStack, &objStrRef{
this: vm.stack[vm.sp-2],
base: vm.stack[vm.sp-1].ToObject(vm.r),
name: unistring.String(p),
strict: true,
})
vm.sp -= 2
vm.pc++
}
type setProp unistring.String
func (p setProp) exec(vm *vm) {
val := vm.stack[vm.sp-1]
vm.stack[vm.sp-2].ToObject(vm.r).self.setOwnStr(unistring.String(p), val, false)
vm.stack[vm.sp-2] = val
vm.sp--
vm.pc++
}
type setPropP unistring.String
func (p setPropP) exec(vm *vm) {
val := vm.stack[vm.sp-1]
vm.stack[vm.sp-2].ToObject(vm.r).self.setOwnStr(unistring.String(p), val, false)
vm.sp -= 2
vm.pc++
}
type setPropStrict unistring.String
func (p setPropStrict) exec(vm *vm) {
receiver := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if receiverObj, ok := receiver.(*Object); ok {
receiverObj.self.setOwnStr(propName, val, true)
} else {
base := receiver.ToObject(vm.r)
base.setStr(propName, val, receiver, true)
}
vm.stack[vm.sp-2] = val
vm.sp--
vm.pc++
}
type setPropRecv unistring.String
func (p setPropRecv) exec(vm *vm) {
receiver := vm.stack[vm.sp-3]
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if obj, ok := o.(*Object); ok {
obj.setStr(propName, val, receiver, false)
} else {
base := o.ToObject(vm.r)
base.setStr(propName, val, receiver, false)
}
vm.stack[vm.sp-3] = val
vm.sp -= 2
vm.pc++
}
type setPropRecvStrict unistring.String
func (p setPropRecvStrict) exec(vm *vm) {
receiver := vm.stack[vm.sp-3]
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if obj, ok := o.(*Object); ok {
obj.setStr(propName, val, receiver, true)
} else {
base := o.ToObject(vm.r)
base.setStr(propName, val, receiver, true)
}
vm.stack[vm.sp-3] = val
vm.sp -= 2
vm.pc++
}
type setPropRecvP unistring.String
func (p setPropRecvP) exec(vm *vm) {
receiver := vm.stack[vm.sp-3]
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if obj, ok := o.(*Object); ok {
obj.setStr(propName, val, receiver, false)
} else {
base := o.ToObject(vm.r)
base.setStr(propName, val, receiver, false)
}
vm.sp -= 3
vm.pc++
}
type setPropRecvStrictP unistring.String
func (p setPropRecvStrictP) exec(vm *vm) {
receiver := vm.stack[vm.sp-3]
o := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if obj, ok := o.(*Object); ok {
obj.setStr(propName, val, receiver, true)
} else {
base := o.ToObject(vm.r)
base.setStr(propName, val, receiver, true)
}
vm.sp -= 3
vm.pc++
}
type setPropStrictP unistring.String
func (p setPropStrictP) exec(vm *vm) {
receiver := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
propName := unistring.String(p)
if receiverObj, ok := receiver.(*Object); ok {
receiverObj.self.setOwnStr(propName, val, true)
} else {
base := receiver.ToObject(vm.r)
base.setStr(propName, val, receiver, true)
}
vm.sp -= 2
vm.pc++
}
type putProp unistring.String
func (p putProp) exec(vm *vm) {
vm.r.toObject(vm.stack[vm.sp-2]).self._putProp(unistring.String(p), vm.stack[vm.sp-1], true, true, true)
vm.sp--
vm.pc++
}
// used in class declarations instead of putProp because DefineProperty must be observable by Proxy
type definePropKeyed unistring.String
func (p definePropKeyed) exec(vm *vm) {
vm.r.toObject(vm.stack[vm.sp-2]).self.defineOwnPropertyStr(unistring.String(p), PropertyDescriptor{
Value: vm.stack[vm.sp-1],
Writable: FLAG_TRUE,
Configurable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
}, true)
vm.sp--
vm.pc++
}
type defineProp struct{}
func (defineProp) exec(vm *vm) {
vm.r.toObject(vm.stack[vm.sp-3]).defineOwnProperty(vm.stack[vm.sp-2], PropertyDescriptor{
Value: vm.stack[vm.sp-1],
Writable: FLAG_TRUE,
Configurable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
}, true)
vm.sp -= 2
vm.pc++
}
type defineMethodKeyed struct {
key unistring.String
enumerable bool
}
func (d *defineMethodKeyed) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-2])
method := vm.r.toObject(vm.stack[vm.sp-1])
obj.self.defineOwnPropertyStr(d.key, PropertyDescriptor{
Value: method,
Writable: FLAG_TRUE,
Configurable: FLAG_TRUE,
Enumerable: ToFlag(d.enumerable),
}, true)
vm.sp--
vm.pc++
}
type _setProto struct{}
var setProto _setProto
func (_setProto) exec(vm *vm) {
vm.r.setObjectProto(vm.stack[vm.sp-2], vm.stack[vm.sp-1])
vm.sp--
vm.pc++
}
type defineGetterKeyed struct {
key unistring.String
enumerable bool
}
func (s *defineGetterKeyed) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-2])
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
method.self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: asciiString("get ").Concat(stringValueFromRaw(s.key)),
Configurable: FLAG_TRUE,
}, true)
descr := PropertyDescriptor{
Getter: val,
Configurable: FLAG_TRUE,
Enumerable: ToFlag(s.enumerable),
}
obj.self.defineOwnPropertyStr(s.key, descr, true)
vm.sp--
vm.pc++
}
type defineSetterKeyed struct {
key unistring.String
enumerable bool
}
func (s *defineSetterKeyed) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-2])
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
method.self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: asciiString("set ").Concat(stringValueFromRaw(s.key)),
Configurable: FLAG_TRUE,
}, true)
descr := PropertyDescriptor{
Setter: val,
Configurable: FLAG_TRUE,
Enumerable: ToFlag(s.enumerable),
}
obj.self.defineOwnPropertyStr(s.key, descr, true)
vm.sp--
vm.pc++
}
type defineGetter struct {
enumerable bool
}
func (s *defineGetter) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-3])
propName := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
method.self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: funcName("get ", propName),
Configurable: FLAG_TRUE,
}, true)
descr := PropertyDescriptor{
Getter: val,
Configurable: FLAG_TRUE,
Enumerable: ToFlag(s.enumerable),
}
obj.defineOwnProperty(propName, descr, true)
vm.sp -= 2
vm.pc++
}
type defineSetter struct {
enumerable bool
}
func (s *defineSetter) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-3])
propName := vm.stack[vm.sp-2]
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
method.self.defineOwnPropertyStr("name", PropertyDescriptor{
Value: funcName("set ", propName),
Configurable: FLAG_TRUE,
}, true)
descr := PropertyDescriptor{
Setter: val,
Configurable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
}
obj.defineOwnProperty(propName, descr, true)
vm.sp -= 2
vm.pc++
}
type getProp unistring.String
func (g getProp) exec(vm *vm) {
v := vm.stack[vm.sp-1]
obj := v.baseObject(vm.r)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", g))
return
}
vm.stack[vm.sp-1] = nilSafe(obj.self.getStr(unistring.String(g), v))
vm.pc++
}
type getPropRecv unistring.String
func (g getPropRecv) exec(vm *vm) {
recv := vm.stack[vm.sp-2]
v := vm.stack[vm.sp-1]
obj := v.baseObject(vm.r)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", g))
return
}
vm.stack[vm.sp-2] = nilSafe(obj.self.getStr(unistring.String(g), recv))
vm.sp--
vm.pc++
}
type getPropRecvCallee unistring.String
func (g getPropRecvCallee) exec(vm *vm) {
recv := vm.stack[vm.sp-2]
v := vm.stack[vm.sp-1]
obj := v.baseObject(vm.r)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", g))
return
}
n := unistring.String(g)
prop := obj.self.getStr(n, recv)
if prop == nil {
prop = memberUnresolved{valueUnresolved{r: vm.r, ref: n}}
}
vm.stack[vm.sp-1] = prop
vm.pc++
}
type getPropCallee unistring.String
func (g getPropCallee) exec(vm *vm) {
v := vm.stack[vm.sp-1]
obj := v.baseObject(vm.r)
n := unistring.String(g)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined or null", n))
return
}
prop := obj.self.getStr(n, v)
if prop == nil {
prop = memberUnresolved{valueUnresolved{r: vm.r, ref: n}}
}
vm.push(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)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", vm.stack[vm.sp-1]))
return
}
propName := toPropertyKey(vm.stack[vm.sp-1])
vm.stack[vm.sp-2] = nilSafe(obj.get(propName, v))
vm.sp--
vm.pc++
}
type _getElemRecv struct{}
var getElemRecv _getElemRecv
func (_getElemRecv) exec(vm *vm) {
recv := vm.stack[vm.sp-3]
v := vm.stack[vm.sp-1]
obj := v.baseObject(vm.r)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", vm.stack[vm.sp-2]))
return
}
propName := toPropertyKey(vm.stack[vm.sp-2])
vm.stack[vm.sp-3] = nilSafe(obj.get(propName, recv))
vm.sp -= 2
vm.pc++
}
type _getKey struct{}
var getKey _getKey
func (_getKey) exec(vm *vm) {
v := vm.stack[vm.sp-2]
obj := v.baseObject(vm.r)
propName := vm.stack[vm.sp-1]
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", propName.String()))
return
}
vm.stack[vm.sp-2] = nilSafe(obj.get(propName, v))
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)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", vm.stack[vm.sp-1]))
return
}
propName := toPropertyKey(vm.stack[vm.sp-1])
prop := obj.get(propName, v)
if prop == nil {
prop = memberUnresolved{valueUnresolved{r: vm.r, ref: propName.string()}}
}
vm.stack[vm.sp-1] = prop
vm.pc++
}
type _getElemRecvCallee struct{}
var getElemRecvCallee _getElemRecvCallee
func (_getElemRecvCallee) exec(vm *vm) {
recv := vm.stack[vm.sp-3]
v := vm.stack[vm.sp-2]
obj := v.baseObject(vm.r)
if obj == nil {
vm.throw(vm.r.NewTypeError("Cannot read property '%s' of undefined", vm.stack[vm.sp-1]))
return
}
propName := toPropertyKey(vm.stack[vm.sp-1])
prop := obj.get(propName, recv)
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 dupLast uint32
func (d dupLast) exec(vm *vm) {
e := vm.sp + int(d)
vm.stack.expand(e)
copy(vm.stack[vm.sp:e], vm.stack[vm.sp-int(d):])
vm.sp = e
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, 0, l)
vm.push(vm.r.newArrayValues(values))
vm.pc++
}
type _pushArrayItem struct{}
var pushArrayItem _pushArrayItem
func (_pushArrayItem) exec(vm *vm) {
arr := vm.stack[vm.sp-2].(*Object).self.(*arrayObject)
if arr.length < math.MaxUint32 {
arr.length++
} else {
vm.throw(vm.r.newError(vm.r.getRangeError(), "Invalid array length"))
return
}
val := vm.stack[vm.sp-1]
arr.values = append(arr.values, val)
if val != nil {
arr.objCount++
}
vm.sp--
vm.pc++
}
type _pushArraySpread struct{}
var pushArraySpread _pushArraySpread
func (_pushArraySpread) exec(vm *vm) {
arr := vm.stack[vm.sp-2].(*Object).self.(*arrayObject)
vm.r.getIterator(vm.stack[vm.sp-1], nil).iterate(func(val Value) {
if arr.length < math.MaxUint32 {
arr.length++
} else {
vm.throw(vm.r.newError(vm.r.getRangeError(), "Invalid array length"))
return
}
arr.values = append(arr.values, val)
arr.objCount++
})
vm.sp--
vm.pc++
}
type _pushSpread struct{}
var pushSpread _pushSpread
func (_pushSpread) exec(vm *vm) {
vm.sp--
obj := vm.stack[vm.sp]
vm.r.getIterator(obj, nil).iterate(func(val Value) {
vm.push(val)
})
vm.pc++
}
type _newArrayFromIter struct{}
var newArrayFromIter _newArrayFromIter
func (_newArrayFromIter) exec(vm *vm) {
var values []Value
l := len(vm.iterStack) - 1
iter := vm.iterStack[l].iter
vm.iterStack[l] = iterStackItem{}
vm.iterStack = vm.iterStack[:l]
if iter.iterator != nil {
iter.iterate(func(val Value) {
values = append(values, val)
})
}
vm.push(vm.r.newArrayValues(values))
vm.pc++
}
type newRegexp struct {
pattern *regexpPattern
src String
}
func (n *newRegexp) exec(vm *vm) {
vm.push(vm.r.newRegExpp(n.pattern.clone(), n.src, vm.r.getRegExpPrototype()).val)
vm.pc++
}
func (vm *vm) setLocalLex(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
}
p := &stash.values[idx]
if *p == nil {
panic(errAccessBeforeInit)
}
*p = v
vm.pc++
}
func (vm *vm) initLocal(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.initByIdx(idx, v)
vm.pc++
}
type storeStash uint32
func (s storeStash) exec(vm *vm) {
vm.initLocal(int(s))
}
type storeStashP uint32
func (s storeStashP) exec(vm *vm) {
vm.initLocal(int(s))
vm.sp--
}
type storeStashLex uint32
func (s storeStashLex) exec(vm *vm) {
vm.setLocalLex(int(s))
}
type storeStashLexP uint32
func (s storeStashLexP) exec(vm *vm) {
vm.setLocalLex(int(s))
vm.sp--
}
type initStash uint32
func (s initStash) exec(vm *vm) {
vm.initLocal(int(s))
}
type initStashP uint32
func (s initStashP) exec(vm *vm) {
vm.initLocal(int(s))
vm.sp--
}
type initGlobalP unistring.String
func (s initGlobalP) exec(vm *vm) {
vm.sp--
vm.r.global.stash.initByName(unistring.String(s), vm.stack[vm.sp])
vm.pc++
}
type initGlobal unistring.String
func (s initGlobal) exec(vm *vm) {
vm.r.global.stash.initByName(unistring.String(s), vm.stack[vm.sp])
vm.pc++
}
type resolveVar1 unistring.String
func (s resolveVar1) exec(vm *vm) {
name := unistring.String(s)
var ref ref
for stash := vm.stash; stash != nil; stash = stash.outer {
ref = stash.getRefByName(name, false)
if ref != nil {
goto end
}
}
ref = &objStrRef{
base: vm.r.globalObject,
name: name,
binding: true,
}
end:
vm.refStack = append(vm.refStack, ref)
vm.pc++
}
type deleteVar unistring.String
func (d deleteVar) exec(vm *vm) {
name := unistring.String(d)
ret := true
for stash := vm.stash; stash != nil; stash = stash.outer {
if stash.obj != nil {
if stashObjHas(stash.obj, name) {
ret = stash.obj.self.deleteStr(name, false)
goto end
}
} else {
if idx, exists := stash.names[name]; exists {
if idx&(maskVar|maskDeletable) == maskVar|maskDeletable {
stash.deleteBinding(name)
} else {
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 unistring.String
func (d deleteGlobal) exec(vm *vm) {
name := unistring.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 unistring.String
func (s resolveVar1Strict) exec(vm *vm) {
name := unistring.String(s)
var ref ref
for stash := vm.stash; stash != nil; stash = stash.outer {
ref = stash.getRefByName(name, true)
if ref != nil {
goto end
}
}
if vm.r.globalObject.self.hasPropertyStr(name) {
ref = &objStrRef{
base: vm.r.globalObject,
name: name,
binding: true,
strict: true,
}
goto end
}
ref = &unresolvedRef{
runtime: vm.r,
name: name,
}
end:
vm.refStack = append(vm.refStack, ref)
vm.pc++
}
type setGlobal unistring.String
func (s setGlobal) exec(vm *vm) {
vm.r.setGlobal(unistring.String(s), vm.peek(), false)
vm.pc++
}
type setGlobalStrict unistring.String
func (s setGlobalStrict) exec(vm *vm) {
vm.r.setGlobal(unistring.String(s), vm.peek(), true)
vm.pc++
}
// Load a var from stash
type loadStash uint32
func (g loadStash) 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(nilSafe(stash.getByIdx(idx)))
vm.pc++
}
// Load a lexical binding from stash
type loadStashLex uint32
func (g loadStashLex) exec(vm *vm) {
level := int(g >> 24)
idx := uint32(g & 0x00FFFFFF)
stash := vm.stash
for i := 0; i < level; i++ {
stash = stash.outer
}
v := stash.getByIdx(idx)
if v == nil {
vm.throw(errAccessBeforeInit)
return
}
vm.push(v)
vm.pc++
}
// scan dynamic stashes up to the given level (encoded as 8 most significant bits of idx), if not found
// return the indexed var binding value from stash
type loadMixed struct {
name unistring.String
idx uint32
callee bool
}
func (g *loadMixed) exec(vm *vm) {
level := int(g.idx >> 24)
idx := g.idx & 0x00FFFFFF
stash := vm.stash
name := g.name
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
if stash != nil {
vm.push(nilSafe(stash.getByIdx(idx)))
}
end:
vm.pc++
}
// scan dynamic stashes up to the given level (encoded as 8 most significant bits of idx), if not found
// return the indexed lexical binding value from stash
type loadMixedLex loadMixed
func (g *loadMixedLex) exec(vm *vm) {
level := int(g.idx >> 24)
idx := g.idx & 0x00FFFFFF
stash := vm.stash
name := g.name
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
if stash != nil {
v := stash.getByIdx(idx)
if v == nil {
vm.throw(errAccessBeforeInit)
return
}
vm.push(v)
}
end:
vm.pc++
}
// scan dynamic stashes up to the given level (encoded as 8 most significant bits of idx), if not found
// return the indexed var binding value from stack
type loadMixedStack struct {
name unistring.String
idx int
level uint8
callee bool
}
// same as loadMixedStack, but the args have been moved to stash (therefore stack layout is different)
type loadMixedStack1 loadMixedStack
func (g *loadMixedStack) exec(vm *vm) {
stash := vm.stash
name := g.name
level := int(g.level)
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
loadStack(g.idx).exec(vm)
return
end:
vm.pc++
}
func (g *loadMixedStack1) exec(vm *vm) {
stash := vm.stash
name := g.name
level := int(g.level)
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
loadStack1(g.idx).exec(vm)
return
end:
vm.pc++
}
type loadMixedStackLex loadMixedStack
// same as loadMixedStackLex but when the arguments have been moved into stash
type loadMixedStack1Lex loadMixedStack
func (g *loadMixedStackLex) exec(vm *vm) {
stash := vm.stash
name := g.name
level := int(g.level)
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
loadStackLex(g.idx).exec(vm)
return
end:
vm.pc++
}
func (g *loadMixedStack1Lex) exec(vm *vm) {
stash := vm.stash
name := g.name
level := int(g.level)
for i := 0; i < level; i++ {
if v, found := stash.getByName(name); found {
if g.callee {
if stash.obj != nil {
vm.push(stash.obj)
} else {
vm.push(_undefined)
}
}
vm.push(v)
goto end
}
stash = stash.outer
}
if g.callee {
vm.push(_undefined)
}
loadStack1Lex(g.idx).exec(vm)
return
end:
vm.pc++
}
type resolveMixed struct {
name unistring.String
idx uint32
typ varType
strict bool
}
func newStashRef(typ varType, name unistring.String, v *[]Value, idx int) ref {
switch typ {
case varTypeVar:
return &stashRef{
n: name,
v: v,
idx: idx,
}
case varTypeLet:
return &stashRefLex{
stashRef: stashRef{
n: name,
v: v,
idx: idx,
},
}
case varTypeConst, varTypeStrictConst:
return &stashRefConst{
stashRefLex: stashRefLex{
stashRef: stashRef{
n: name,
v: v,
idx: idx,
},
},
strictConst: typ == varTypeStrictConst,
}
}
panic("unsupported var type")
}
func (r *resolveMixed) exec(vm *vm) {
level := int(r.idx >> 24)
idx := r.idx & 0x00FFFFFF
stash := vm.stash
var ref ref
for i := 0; i < level; i++ {
ref = stash.getRefByName(r.name, r.strict)
if ref != nil {
goto end
}
stash = stash.outer
}
if stash != nil {
ref = newStashRef(r.typ, r.name, &stash.values, int(idx))
goto end
}
ref = &unresolvedRef{
runtime: vm.r,
name: r.name,
}
end:
vm.refStack = append(vm.refStack, ref)
vm.pc++
}
type resolveMixedStack struct {
name unistring.String
idx int
typ varType
level uint8
strict bool
}
type resolveMixedStack1 resolveMixedStack
func (r *resolveMixedStack) exec(vm *vm) {
level := int(r.level)
stash := vm.stash
var ref ref
var idx int
for i := 0; i < level; i++ {
ref = stash.getRefByName(r.name, r.strict)
if ref != nil {
goto end
}
stash = stash.outer
}
if r.idx > 0 {
idx = vm.sb + vm.args + r.idx
} else {
idx = vm.sb - r.idx
}
ref = newStashRef(r.typ, r.name, (*[]Value)(&vm.stack), idx)
end:
vm.refStack = append(vm.refStack, ref)
vm.pc++
}
func (r *resolveMixedStack1) exec(vm *vm) {
level := int(r.level)
stash := vm.stash
var ref ref
for i := 0; i < level; i++ {
ref = stash.getRefByName(r.name, r.strict)
if ref != nil {
goto end
}
stash = stash.outer
}
ref = newStashRef(r.typ, r.name, (*[]Value)(&vm.stack), vm.sb+r.idx)
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.throw(vm.r.newReferenceError(ref.refname()))
return
}
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 _putValueP struct{}
var putValueP _putValueP
func (_putValueP) 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.sp--
vm.pc++
}
type _initValueP struct{}
var initValueP _initValueP
func (_initValueP) exec(vm *vm) {
l := len(vm.refStack) - 1
ref := vm.refStack[l]
vm.refStack[l] = nil
vm.refStack = vm.refStack[:l]
ref.init(vm.stack[vm.sp-1])
vm.sp--
vm.pc++
}
type loadDynamic unistring.String
func (n loadDynamic) exec(vm *vm) {
name := unistring.String(n)
var val Value
for stash := vm.stash; stash != nil; stash = stash.outer {
if v, exists := stash.getByName(name); exists {
val = v
break
}
}
if val == nil {
val = vm.r.globalObject.self.getStr(name, nil)
if val == nil {
vm.throw(vm.r.newReferenceError(name))
return
}
}
vm.push(val)
vm.pc++
}
type loadDynamicRef unistring.String
func (n loadDynamicRef) exec(vm *vm) {
name := unistring.String(n)
var val Value
for stash := vm.stash; stash != nil; stash = stash.outer {
if v, exists := stash.getByName(name); exists {
val = v
break
}
}
if val == nil {
val = vm.r.globalObject.self.getStr(name, nil)
if val == nil {
val = valueUnresolved{r: vm.r, ref: name}
}
}
vm.push(val)
vm.pc++
}
type loadDynamicCallee unistring.String
func (n loadDynamicCallee) exec(vm *vm) {
name := unistring.String(n)
var val Value
var callee *Object
for stash := vm.stash; stash != nil; stash = stash.outer {
if v, exists := stash.getByName(name); exists {
callee = stash.obj
val = v
break
}
}
if val == nil {
val = vm.r.globalObject.self.getStr(name, nil)
if val == nil {
val = valueUnresolved{r: vm.r, ref: name}
}
}
if callee != nil {
vm.push(callee)
} else {
vm.push(_undefined)
}
vm.push(val)
vm.pc++
}
type _pop struct{}
var pop _pop
func (_pop) exec(vm *vm) {
vm.sp--
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.(String); ok {
ret := vm.r.eval(src, true, strict)
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 _callEvalVariadic struct{}
var callEvalVariadic _callEvalVariadic
func (_callEvalVariadic) exec(vm *vm) {
vm.callEval(vm.countVariadicArgs()-2, false)
}
type _callEvalVariadicStrict struct{}
var callEvalVariadicStrict _callEvalVariadicStrict
func (_callEvalVariadicStrict) exec(vm *vm) {
vm.callEval(vm.countVariadicArgs()-2, 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++
}
var variadicMarker Value = newSymbol(asciiString("[variadic marker]"))
type _startVariadic struct{}
var startVariadic _startVariadic
func (_startVariadic) exec(vm *vm) {
vm.push(variadicMarker)
vm.pc++
}
type _callVariadic struct{}
var callVariadic _callVariadic
func (vm *vm) countVariadicArgs() int {
count := 0
for i := vm.sp - 1; i >= 0; i-- {
if vm.stack[i] == variadicMarker {
return count
}
count++
}
panic("Variadic marker was not found. Compiler bug.")
}
func (_callVariadic) exec(vm *vm) {
call(vm.countVariadicArgs() - 2).exec(vm)
}
type _endVariadic struct{}
var endVariadic _endVariadic
func (_endVariadic) exec(vm *vm) {
vm.sp--
vm.stack[vm.sp-1] = vm.stack[vm.sp]
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.toCallee(v)
obj.self.vmCall(vm, n)
}
func (vm *vm) clearStack() {
sp := vm.sp
stackTail := vm.stack[sp:]
for i := range stackTail {
stackTail[i] = nil
}
vm.stack = vm.stack[:sp]
}
type enterBlock struct {
names map[unistring.String]uint32
stashSize uint32
stackSize uint32
}
func (e *enterBlock) exec(vm *vm) {
if e.stashSize > 0 {
vm.newStash()
vm.stash.values = make([]Value, e.stashSize)
if len(e.names) > 0 {
vm.stash.names = e.names
}
}
ss := int(e.stackSize)
vm.stack.expand(vm.sp + ss - 1)
vv := vm.stack[vm.sp : vm.sp+ss]
for i := range vv {
vv[i] = nil
}
vm.sp += ss
vm.pc++
}
type enterCatchBlock struct {
names map[unistring.String]uint32
stashSize uint32
stackSize uint32
}
func (e *enterCatchBlock) exec(vm *vm) {
vm.newStash()
vm.stash.values = make([]Value, e.stashSize)
if len(e.names) > 0 {
vm.stash.names = e.names
}
vm.sp--
vm.stash.values[0] = vm.stack[vm.sp]
ss := int(e.stackSize)
vm.stack.expand(vm.sp + ss - 1)
vv := vm.stack[vm.sp : vm.sp+ss]
for i := range vv {
vv[i] = nil
}
vm.sp += ss
vm.pc++
}
type leaveBlock struct {
stackSize uint32
popStash bool
}
func (l *leaveBlock) exec(vm *vm) {
if l.popStash {
vm.stash = vm.stash.outer
}
if ss := l.stackSize; ss > 0 {
vm.sp -= int(ss)
}
vm.pc++
}
type enterFunc struct {
names map[unistring.String]uint32
stashSize uint32
stackSize uint32
numArgs uint32
funcType funcType
argsToStash bool
extensible bool
}
func (e *enterFunc) exec(vm *vm) {
// Input stack:
//
// callee
// this
// arg0
// ...
// argN
// <- sp
// Output stack:
//
// this <- sb
// <local stack vars...>
// <- sp
sp := vm.sp
vm.sb = sp - vm.args - 1
vm.newStash()
stash := vm.stash
stash.funcType = e.funcType
stash.values = make([]Value, e.stashSize)
if len(e.names) > 0 {
if e.extensible {
m := make(map[unistring.String]uint32, len(e.names))
for name, idx := range e.names {
m[name] = idx
}
stash.names = m
} else {
stash.names = e.names
}
}
ss := int(e.stackSize)
ea := 0
if e.argsToStash {
offset := vm.args - int(e.numArgs)
copy(stash.values, vm.stack[sp-vm.args:sp])
if offset > 0 {
vm.stash.extraArgs = make([]Value, offset)
copy(stash.extraArgs, vm.stack[sp-offset:])
} else {
vv := stash.values[vm.args:e.numArgs]
for i := range vv {
vv[i] = _undefined
}
}
sp -= vm.args
} else {
d := int(e.numArgs) - vm.args
if d > 0 {
ss += d
ea = d
vm.args = int(e.numArgs)
}
}
vm.stack.expand(sp + ss - 1)
if ea > 0 {
vv := vm.stack[sp : vm.sp+ea]
for i := range vv {
vv[i] = _undefined
}
}
vv := vm.stack[sp+ea : sp+ss]
for i := range vv {
vv[i] = nil
}
vm.sp = sp + ss
vm.pc++
}
// Similar to enterFunc, but for when arguments may be accessed before they are initialised,
// e.g. by an eval() code or from a closure, or from an earlier initialiser code.
// In this case the arguments remain on stack, first argsToCopy of them are copied to the stash.
type enterFunc1 struct {
names map[unistring.String]uint32
stashSize uint32
numArgs uint32
argsToCopy uint32
funcType funcType
extensible bool
}
func (e *enterFunc1) exec(vm *vm) {
sp := vm.sp
vm.sb = sp - vm.args - 1
vm.newStash()
stash := vm.stash
stash.funcType = e.funcType
stash.values = make([]Value, e.stashSize)
if len(e.names) > 0 {
if e.extensible {
m := make(map[unistring.String]uint32, len(e.names))
for name, idx := range e.names {
m[name] = idx
}
stash.names = m
} else {
stash.names = e.names
}
}
offset := vm.args - int(e.argsToCopy)
if offset > 0 {
copy(stash.values, vm.stack[sp-vm.args:sp-offset])
if offset := vm.args - int(e.numArgs); offset > 0 {
vm.stash.extraArgs = make([]Value, offset)
copy(stash.extraArgs, vm.stack[sp-offset:])
}
} else {
copy(stash.values, vm.stack[sp-vm.args:sp])
if int(e.argsToCopy) > vm.args {
vv := stash.values[vm.args:e.argsToCopy]
for i := range vv {
vv[i] = _undefined
}
}
}
vm.pc++
}
// Finalises the initialisers section and starts the function body which has its own
// scope. When used in conjunction with enterFunc1 adjustStack is set to true which
// causes the arguments to be removed from the stack.
type enterFuncBody struct {
enterBlock
funcType funcType
extensible bool
adjustStack bool
}
func (e *enterFuncBody) exec(vm *vm) {
if e.stashSize > 0 || e.extensible {
vm.newStash()
stash := vm.stash
stash.funcType = e.funcType
stash.values = make([]Value, e.stashSize)
if len(e.names) > 0 {
if e.extensible {
m := make(map[unistring.String]uint32, len(e.names))
for name, idx := range e.names {
m[name] = idx
}
stash.names = m
} else {
stash.names = e.names
}
}
}
sp := vm.sp
if e.adjustStack {
sp -= vm.args
}
nsp := sp + int(e.stackSize)
if e.stackSize > 0 {
vm.stack.expand(nsp - 1)
vv := vm.stack[sp:nsp]
for i := range vv {
vv[i] = nil
}
}
vm.sp = nsp
vm.pc++
}
type _ret struct{}
var ret _ret
func (_ret) exec(vm *vm) {
// callee -3
// this -2 <- sb
// retval -1
vm.stack[vm.sb-1] = vm.stack[vm.sp-1]
vm.sp = vm.sb
vm.popCtx()
vm.pc++
}
type cret uint32
func (c cret) exec(vm *vm) {
vm.stack[vm.sb] = *vm.getStashPtr(uint32(c))
ret.exec(vm)
}
type enterFuncStashless struct {
stackSize uint32
args uint32
}
func (e *enterFuncStashless) exec(vm *vm) {
sp := vm.sp
vm.sb = sp - vm.args - 1
d := int(e.args) - vm.args
if d > 0 {
ss := sp + int(e.stackSize) + d
vm.stack.expand(ss)
vv := vm.stack[sp : sp+d]
for i := range vv {
vv[i] = _undefined
}
vv = vm.stack[sp+d : ss]
for i := range vv {
vv[i] = nil
}
vm.args = int(e.args)
vm.sp = ss
} else {
if e.stackSize > 0 {
ss := sp + int(e.stackSize)
vm.stack.expand(ss)
vv := vm.stack[sp:ss]
for i := range vv {
vv[i] = nil
}
vm.sp = ss
}
}
vm.pc++
}
type newFuncInstruction interface {
getPrg() *Program
}
type newFunc struct {
prg *Program
name unistring.String
source string
length int
strict bool
}
func (n *newFunc) exec(vm *vm) {
obj := vm.r.newFunc(n.name, n.length, n.strict)
obj.prg = n.prg
obj.stash = vm.stash
obj.privEnv = vm.privEnv
obj.src = n.source
vm.push(obj.val)
vm.pc++
}
func (n *newFunc) getPrg() *Program {
return n.prg
}
type newAsyncFunc struct {
newFunc
}
func (n *newAsyncFunc) exec(vm *vm) {
obj := vm.r.newAsyncFunc(n.name, n.length, n.strict)
obj.prg = n.prg
obj.stash = vm.stash
obj.privEnv = vm.privEnv
obj.src = n.source
vm.push(obj.val)
vm.pc++
}
type newGeneratorFunc struct {
newFunc
}
func (n *newGeneratorFunc) exec(vm *vm) {
obj := vm.r.newGeneratorFunc(n.name, n.length, n.strict)
obj.prg = n.prg
obj.stash = vm.stash
obj.privEnv = vm.privEnv
obj.src = n.source
vm.push(obj.val)
vm.pc++
}
type newMethod struct {
newFunc
homeObjOffset uint32
}
func (n *newMethod) _exec(vm *vm, obj *methodFuncObject) {
obj.prg = n.prg
obj.stash = vm.stash
obj.privEnv = vm.privEnv
obj.src = n.source
if n.homeObjOffset > 0 {
obj.homeObject = vm.r.toObject(vm.stack[vm.sp-int(n.homeObjOffset)])
}
vm.push(obj.val)
vm.pc++
}
func (n *newMethod) exec(vm *vm) {
n._exec(vm, vm.r.newMethod(n.name, n.length, n.strict))
}
type newAsyncMethod struct {
newMethod
}
func (n *newAsyncMethod) exec(vm *vm) {
obj := vm.r.newAsyncMethod(n.name, n.length, n.strict)
n._exec(vm, &obj.methodFuncObject)
}
type newGeneratorMethod struct {
newMethod
}
func (n *newGeneratorMethod) exec(vm *vm) {
obj := vm.r.newGeneratorMethod(n.name, n.length, n.strict)
n._exec(vm, &obj.methodFuncObject)
}
type newArrowFunc struct {
newFunc
}
type newAsyncArrowFunc struct {
newArrowFunc
}
func getFuncObject(v Value) *Object {
if o, ok := v.(*Object); ok {
if fn, ok := o.self.(*arrowFuncObject); ok {
return fn.funcObj
}
return o
}
if v == _undefined {
return nil
}
panic(typeError("Value is not an Object"))
}
func getHomeObject(v Value) *Object {
if o, ok := v.(*Object); ok {
switch fn := o.self.(type) {
case *methodFuncObject:
return fn.homeObject
case *generatorMethodFuncObject:
return fn.homeObject
case *asyncMethodFuncObject:
return fn.homeObject
case *classFuncObject:
return o.runtime.toObject(fn.getStr("prototype", nil))
case *arrowFuncObject:
return getHomeObject(fn.funcObj)
case *asyncArrowFuncObject:
return getHomeObject(fn.funcObj)
}
}
panic(newTypeError("Compiler bug: getHomeObject() on the wrong value: %T", v))
}
func (n *newArrowFunc) _exec(vm *vm, obj *arrowFuncObject) {
obj.prg = n.prg
obj.stash = vm.stash
obj.privEnv = vm.privEnv
obj.src = n.source
if vm.sb > 0 {
obj.funcObj = getFuncObject(vm.stack[vm.sb-1])
}
vm.push(obj.val)
vm.pc++
}
func (n *newArrowFunc) exec(vm *vm) {
n._exec(vm, vm.r.newArrowFunc(n.name, n.length, n.strict))
}
func (n *newAsyncArrowFunc) exec(vm *vm) {
obj := vm.r.newAsyncArrowFunc(n.name, n.length, n.strict)
n._exec(vm, &obj.arrowFuncObject)
}
func (vm *vm) alreadyDeclared(name unistring.String) Value {
return vm.r.newError(vm.r.getSyntaxError(), "Identifier '%s' has already been declared", name)
}
func (vm *vm) checkBindVarsGlobal(names []unistring.String) {
o := vm.r.globalObject.self
sn := vm.r.global.stash.names
if bo, ok := o.(*baseObject); ok {
// shortcut
if bo.extensible {
for _, name := range names {
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
}
} else {
for _, name := range names {
if !bo.hasOwnPropertyStr(name) {
panic(vm.r.NewTypeError("Cannot define global variable '%s', global object is not extensible", name))
}
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
}
}
} else {
for _, name := range names {
if !o.hasOwnPropertyStr(name) && !o.isExtensible() {
panic(vm.r.NewTypeError("Cannot define global variable '%s', global object is not extensible", name))
}
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
}
}
}
func (vm *vm) createGlobalVarBindings(names []unistring.String, d bool) {
o := vm.r.globalObject.self
if bo, ok := o.(*templatedObject); ok {
for _, name := range names {
if !bo.hasOwnPropertyStr(name) && bo.extensible {
bo._putProp(name, _undefined, true, true, d)
}
}
} else {
var cf Flag
if d {
cf = FLAG_TRUE
} else {
cf = FLAG_FALSE
}
for _, name := range names {
if !o.hasOwnPropertyStr(name) && o.isExtensible() {
o.defineOwnPropertyStr(name, PropertyDescriptor{
Value: _undefined,
Writable: FLAG_TRUE,
Enumerable: FLAG_TRUE,
Configurable: cf,
}, true)
o.setOwnStr(name, _undefined, false)
}
}
}
}
func (vm *vm) createGlobalFuncBindings(names []unistring.String, d bool) {
o := vm.r.globalObject.self
b := vm.sp - len(names)
var shortcutObj *templatedObject
if o, ok := o.(*templatedObject); ok {
shortcutObj = o
}
for i, name := range names {
var desc PropertyDescriptor
prop := o.getOwnPropStr(name)
desc.Value = vm.stack[b+i]
if shortcutObj != nil && prop == nil && shortcutObj.extensible {
shortcutObj._putProp(name, desc.Value, true, true, d)
} else {
if prop, ok := prop.(*valueProperty); ok && !prop.configurable {
// no-op
} else {
desc.Writable = FLAG_TRUE
desc.Enumerable = FLAG_TRUE
if d {
desc.Configurable = FLAG_TRUE
} else {
desc.Configurable = FLAG_FALSE
}
}
if shortcutObj != nil {
shortcutObj.defineOwnPropertyStr(name, desc, true)
} else {
o.defineOwnPropertyStr(name, desc, true)
o.setOwnStr(name, desc.Value, false) // not a bug, see https://262.ecma-international.org/#sec-createglobalfunctionbinding
}
}
}
vm.sp = b
}
func (vm *vm) checkBindFuncsGlobal(names []unistring.String) {
o := vm.r.globalObject.self
sn := vm.r.global.stash.names
for _, name := range names {
if _, exists := sn[name]; exists {
panic(vm.alreadyDeclared(name))
}
prop := o.getOwnPropStr(name)
allowed := true
switch prop := prop.(type) {
case nil:
allowed = o.isExtensible()
case *valueProperty:
allowed = prop.configurable || prop.getterFunc == nil && prop.setterFunc == nil && prop.writable && prop.enumerable
}
if !allowed {
panic(vm.r.NewTypeError("Cannot redefine global function '%s'", name))
}
}
}
func (vm *vm) checkBindLexGlobal(names []unistring.String) {
o := vm.r.globalObject.self
s := &vm.r.global.stash
for _, name := range names {
if _, exists := s.names[name]; exists {
goto fail
}
if prop, ok := o.getOwnPropStr(name).(*valueProperty); ok && !prop.configurable {
goto fail
}
continue
fail:
panic(vm.alreadyDeclared(name))
}
}
type bindVars struct {
names []unistring.String
deletable bool
}
func (d *bindVars) exec(vm *vm) {
var target *stash
for _, name := range d.names {
for s := vm.stash; s != nil; s = s.outer {
if idx, exists := s.names[name]; exists && idx&maskVar == 0 {
vm.throw(vm.alreadyDeclared(name))
return
}
if s.isVariable() {
target = s
break
}
}
}
if target == nil {
target = vm.stash
}
deletable := d.deletable
for _, name := range d.names {
target.createBinding(name, deletable)
}
vm.pc++
}
type bindGlobal struct {
vars, funcs, lets, consts []unistring.String
deletable bool
}
func (b *bindGlobal) exec(vm *vm) {
vm.checkBindFuncsGlobal(b.funcs)
vm.checkBindLexGlobal(b.lets)
vm.checkBindLexGlobal(b.consts)
vm.checkBindVarsGlobal(b.vars)
s := &vm.r.global.stash
for _, name := range b.lets {
s.createLexBinding(name, false)
}
for _, name := range b.consts {
s.createLexBinding(name, true)
}
vm.createGlobalFuncBindings(b.funcs, b.deletable)
vm.createGlobalVarBindings(b.vars, b.deletable)
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.sp--
vm.pc++
}
}
type jneq1 int32
func (j jneq1) exec(vm *vm) {
if !vm.stack[vm.sp-1].ToBoolean() {
vm.pc += int(j)
} else {
vm.sp--
vm.pc++
}
}
type jdef int32
func (j jdef) exec(vm *vm) {
if vm.stack[vm.sp-1] != _undefined {
vm.pc += int(j)
} else {
vm.sp--
vm.pc++
}
}
type jdefP int32
func (j jdefP) exec(vm *vm) {
if vm.stack[vm.sp-1] != _undefined {
vm.pc += int(j)
} else {
vm.pc++
}
vm.sp--
}
type jopt int32
func (j jopt) exec(vm *vm) {
switch vm.stack[vm.sp-1] {
case _null:
vm.stack[vm.sp-1] = _undefined
fallthrough
case _undefined:
vm.pc += int(j)
default:
vm.pc++
}
}
type joptc int32
func (j joptc) exec(vm *vm) {
switch vm.stack[vm.sp-1].(type) {
case valueNull, valueUndefined, memberUnresolved:
vm.sp--
vm.stack[vm.sp-1] = _undefined
vm.pc += int(j)
default:
vm.pc++
}
}
type jcoalesc int32
func (j jcoalesc) exec(vm *vm) {
switch vm.stack[vm.sp-1] {
case _undefined, _null:
vm.sp--
vm.pc++
default:
vm.pc += int(j)
}
}
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.toPrimitiveNumber()
}
return v
}
func toPrimitive(v Value) Value {
if o, ok := v.(*Object); ok {
return o.toPrimitive()
}
return v
}
func cmp(px, py Value) Value {
var ret bool
xs, isPxString := px.(String)
ys, isPyString := py.(String)
if isPxString && isPyString {
ret = xs.CompareTo(ys) < 0
goto end
} else {
if px, ok := px.(*valueBigInt); ok && isPyString {
ny, err := stringToBigInt(ys.toTrimmedUTF8())
if err != nil {
return _undefined
}
ret = (*big.Int)(px).Cmp(ny) < 0
goto end
}
if py, ok := py.(*valueBigInt); ok && isPxString {
nx, err := stringToBigInt(xs.toTrimmedUTF8())
if err != nil {
return _undefined
}
ret = nx.Cmp((*big.Int)(py)) < 0
goto end
}
}
px = toNumeric(px)
py = toNumeric(py)
switch nx := px.(type) {
case valueInt:
switch ny := py.(type) {
case valueInt:
ret = nx < ny
goto end
case *valueBigInt:
ret = big.NewInt(int64(nx)).Cmp((*big.Int)(ny)) < 0
goto end
}
case valueFloat:
switch ny := py.(type) {
case *valueBigInt:
switch {
case math.IsNaN(float64(nx)):
return _undefined
case nx == _negativeInf:
ret = true
goto end
}
if nx := big.NewFloat(float64(nx)); nx.IsInt() {
nx, _ := nx.Int(nil)
ret = nx.Cmp((*big.Int)(ny)) < 0
} else {
ret = nx.Cmp(new(big.Float).SetInt((*big.Int)(ny))) < 0
}
goto end
}
case *valueBigInt:
switch ny := py.(type) {
case valueInt:
ret = (*big.Int)(nx).Cmp(big.NewInt(int64(ny))) < 0
goto end
case valueFloat:
switch {
case math.IsNaN(float64(ny)):
return _undefined
case ny == _positiveInf:
ret = true
goto end
}
if ny := big.NewFloat(float64(ny)); ny.IsInt() {
ny, _ := ny.Int(nil)
ret = (*big.Int)(nx).Cmp(ny) < 0
} else {
ret = new(big.Float).SetInt((*big.Int)(nx)).Cmp(ny) < 0
}
goto end
case *valueBigInt:
ret = (*big.Int)(nx).Cmp((*big.Int)(ny)) < 0
goto end
}
}
if nx, ny := px.ToFloat(), py.ToFloat(); math.IsNaN(nx) || math.IsNaN(ny) {
return _undefined
} else {
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 instanceOfOperator(left, right) {
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.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
}
func (t try) exec(vm *vm) {
var catchPos, finallyPos int32
if t.catchOffset > 0 {
catchPos = int32(vm.pc) + t.catchOffset
} else {
catchPos = -1
}
if t.finallyOffset > 0 {
finallyPos = int32(vm.pc) + t.finallyOffset
} else {
finallyPos = -1
}
vm.pushTryFrame(catchPos, finallyPos)
vm.pc++
}
type leaveTry struct{}
func (leaveTry) exec(vm *vm) {
tf := &vm.tryStack[len(vm.tryStack)-1]
if tf.finallyPos >= 0 {
tf.finallyRet = int32(vm.pc + 1)
vm.pc = int(tf.finallyPos)
tf.finallyPos = -1
tf.catchPos = -1
} else {
vm.popTryFrame()
vm.pc++
}
}
type enterFinally struct{}
func (enterFinally) exec(vm *vm) {
tf := &vm.tryStack[len(vm.tryStack)-1]
tf.finallyPos = -1
vm.pc++
}
type leaveFinally struct{}
func (leaveFinally) exec(vm *vm) {
tf := &vm.tryStack[len(vm.tryStack)-1]
ex, ret := tf.exception, tf.finallyRet
tf.exception = nil
vm.popTryFrame()
if ex != nil {
vm.throw(ex)
return
} else {
if ret != -1 {
vm.pc = int(ret)
} else {
vm.pc++
}
}
}
type _throw struct{}
var throw _throw
func (_throw) exec(vm *vm) {
v := vm.stack[vm.sp-1]
ex := &Exception{
val: v,
}
if o, ok := v.(*Object); ok {
if e, ok := o.self.(*errorObject); ok {
if len(e.stack) > 0 {
ex.stack = e.stack
}
}
}
if ex.stack == nil {
ex.stack = vm.captureStack(make([]StackFrame, 0, len(vm.callStack)+1), 0)
}
if ex = vm.handleThrow(ex); ex != nil {
panic(ex)
}
}
type _newVariadic struct{}
var newVariadic _newVariadic
func (_newVariadic) exec(vm *vm) {
_new(vm.countVariadicArgs() - 1).exec(vm)
}
type _new uint32
func (n _new) exec(vm *vm) {
sp := vm.sp - int(n)
obj := vm.stack[sp-1]
ctor := vm.r.toConstructor(obj)
vm.stack[sp-1] = ctor(vm.stack[sp:vm.sp], nil)
vm.sp = sp
vm.pc++
}
type superCall uint32
func (s superCall) exec(vm *vm) {
l := len(vm.refStack) - 1
thisRef := vm.refStack[l]
vm.refStack[l] = nil
vm.refStack = vm.refStack[:l]
obj := vm.r.toObject(vm.stack[vm.sb-1])
var cls *classFuncObject
switch fn := obj.self.(type) {
case *classFuncObject:
cls = fn
case *arrowFuncObject:
cls, _ = fn.funcObj.self.(*classFuncObject)
}
if cls == nil {
vm.throw(vm.r.NewTypeError("wrong callee type for super()"))
return
}
sp := vm.sp - int(s)
newTarget := vm.r.toObject(vm.newTarget)
v := cls.createInstance(vm.stack[sp:vm.sp], newTarget)
thisRef.set(v)
vm.sp = sp
cls._initFields(v)
vm.push(v)
vm.pc++
}
type _superCallVariadic struct{}
var superCallVariadic _superCallVariadic
func (_superCallVariadic) exec(vm *vm) {
superCall(vm.countVariadicArgs()).exec(vm)
}
type _loadNewTarget struct{}
var loadNewTarget _loadNewTarget
func (_loadNewTarget) exec(vm *vm) {
if t := vm.newTarget; t != nil {
vm.push(t)
} else {
vm.push(_undefined)
}
vm.pc++
}
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:
r = v.self.typeOf()
case valueBool:
r = stringBoolean
case String:
r = stringString
case valueInt, valueFloat:
r = stringNumber
case *valueBigInt:
r = stringBigInt
case *Symbol:
r = stringSymbol
default:
panic(newTypeError("Compiler bug: unknown type: %T", v))
}
vm.stack[vm.sp-1] = r
vm.pc++
}
type createArgsMapped uint32
func (formalArgs createArgsMapped) 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(unistring.String(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(unistring.String(strconv.Itoa(i)), v)
i++
}
args._putProp("callee", vm.stack[vm.sb-1], true, false, true)
args._putSym(SymIterator, valueProp(vm.r.getArrayValues(), true, false, true))
vm.push(v)
vm.pc++
}
type createArgsUnmapped uint32
func (formalArgs createArgsUnmapped) 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(unistring.String(strconv.Itoa(i)), v)
i++
}
for _, v := range vm.stash.extraArgs {
args._put(unistring.String(strconv.Itoa(i)), v)
i++
}
args._putProp("length", intToValue(int64(vm.args)), true, false, true)
args._put("callee", vm.r.newThrowerProperty(false))
args._putSym(SymIterator, valueProp(vm.r.getArrayValues(), true, false, true))
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)
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: enumerateRecursive(v.ToObject(vm.r))})
}
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 = 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++
}
type _enumPopClose struct{}
var enumPopClose _enumPopClose
func (_enumPopClose) exec(vm *vm) {
l := len(vm.iterStack) - 1
item := vm.iterStack[l]
vm.iterStack[l] = iterStackItem{}
vm.iterStack = vm.iterStack[:l]
if iter := item.iter; iter != nil {
iter.returnIter()
}
vm.pc++
}
type _iterateP struct{}
var iterateP _iterateP
func (_iterateP) exec(vm *vm) {
iter := vm.r.getIterator(vm.stack[vm.sp-1], nil)
vm.iterStack = append(vm.iterStack, iterStackItem{iter: iter})
vm.sp--
vm.pc++
}
type _iterate struct{}
var iterate _iterate
func (_iterate) exec(vm *vm) {
iter := vm.r.getIterator(vm.stack[vm.sp-1], nil)
vm.iterStack = append(vm.iterStack, iterStackItem{iter: iter})
vm.pc++
}
type iterNext int32
func (jmp iterNext) exec(vm *vm) {
l := len(vm.iterStack) - 1
iter := vm.iterStack[l].iter
value, ex := iter.step()
if ex == nil {
if value == nil {
vm.pc += int(jmp)
} else {
vm.iterStack[l].val = value
vm.pc++
}
} else {
l := len(vm.iterStack) - 1
vm.iterStack[l] = iterStackItem{}
vm.iterStack = vm.iterStack[:l]
vm.throw(ex.val)
return
}
}
type iterGetNextOrUndef struct{}
func (iterGetNextOrUndef) exec(vm *vm) {
l := len(vm.iterStack) - 1
iter := vm.iterStack[l].iter
var value Value
if iter.iterator != nil {
var ex *Exception
value, ex = iter.step()
if ex != nil {
l := len(vm.iterStack) - 1
vm.iterStack[l] = iterStackItem{}
vm.iterStack = vm.iterStack[:l]
vm.throw(ex.val)
return
}
}
vm.push(nilSafe(value))
vm.pc++
}
type copyStash struct{}
func (copyStash) exec(vm *vm) {
oldStash := vm.stash
newStash := &stash{
outer: oldStash.outer,
}
vm.stashAllocs++
newStash.values = append([]Value(nil), oldStash.values...)
newStash.names = oldStash.names
vm.stash = newStash
vm.pc++
}
type _throwAssignToConst struct{}
var throwAssignToConst _throwAssignToConst
func (_throwAssignToConst) exec(vm *vm) {
vm.throw(errAssignToConst)
}
func (r *Runtime) copyDataProperties(target, source Value) {
targetObj := r.toObject(target)
if source == _null || source == _undefined {
return
}
sourceObj := source.ToObject(r)
for item, next := iterateEnumerableProperties(sourceObj)(); next != nil; item, next = next() {
createDataPropertyOrThrow(targetObj, item.name, item.value)
}
}
type _copySpread struct{}
var copySpread _copySpread
func (_copySpread) exec(vm *vm) {
vm.r.copyDataProperties(vm.stack[vm.sp-2], vm.stack[vm.sp-1])
vm.sp--
vm.pc++
}
type _copyRest struct{}
var copyRest _copyRest
func (_copyRest) exec(vm *vm) {
vm.push(vm.r.NewObject())
vm.r.copyDataProperties(vm.stack[vm.sp-1], vm.stack[vm.sp-2])
vm.pc++
}
type _createDestructSrc struct{}
var createDestructSrc _createDestructSrc
func (_createDestructSrc) exec(vm *vm) {
v := vm.stack[vm.sp-1]
vm.r.checkObjectCoercible(v)
vm.push(vm.r.newDestructKeyedSource(v))
vm.pc++
}
type _checkObjectCoercible struct{}
var checkObjectCoercible _checkObjectCoercible
func (_checkObjectCoercible) exec(vm *vm) {
vm.r.checkObjectCoercible(vm.stack[vm.sp-1])
vm.pc++
}
type createArgsRestStack int
func (n createArgsRestStack) exec(vm *vm) {
var values []Value
delta := vm.args - int(n)
if delta > 0 {
values = make([]Value, delta)
copy(values, vm.stack[vm.sb+int(n)+1:])
}
vm.push(vm.r.newArrayValues(values))
vm.pc++
}
type _createArgsRestStash struct{}
var createArgsRestStash _createArgsRestStash
func (_createArgsRestStash) exec(vm *vm) {
vm.push(vm.r.newArrayValues(vm.stash.extraArgs))
vm.stash.extraArgs = nil
vm.pc++
}
type concatStrings int
func (n concatStrings) exec(vm *vm) {
strs := vm.stack[vm.sp-int(n) : vm.sp]
length := 0
allAscii := true
for i, s := range strs {
switch s := s.(type) {
case asciiString:
length += s.Length()
case unicodeString:
length += s.Length()
allAscii = false
case *importedString:
s.ensureScanned()
if s.u != nil {
strs[i] = s.u
length += s.u.Length()
allAscii = false
} else {
strs[i] = asciiString(s.s)
length += len(s.s)
}
default:
panic(unknownStringTypeErr(s))
}
}
vm.sp -= int(n) - 1
if allAscii {
var buf strings.Builder
buf.Grow(length)
for _, s := range strs {
buf.WriteString(string(s.(asciiString)))
}
vm.stack[vm.sp-1] = asciiString(buf.String())
} else {
var buf unicodeStringBuilder
buf.ensureStarted(length)
for _, s := range strs {
buf.writeString(s.(String))
}
vm.stack[vm.sp-1] = buf.String()
}
vm.pc++
}
type getTaggedTmplObject struct {
raw, cooked []Value
}
// As tagged template objects are not cached (because it's hard to ensure the cache is cleaned without using
// finalizers) this wrapper is needed to override the equality method so that two objects for the same template
// literal appeared to be equal from the code's point of view.
type taggedTemplateArray struct {
*arrayObject
idPtr *[]Value
}
func (a *taggedTemplateArray) equal(other objectImpl) bool {
if o, ok := other.(*taggedTemplateArray); ok {
return a.idPtr == o.idPtr
}
return false
}
func (c *getTaggedTmplObject) exec(vm *vm) {
cooked := vm.r.newArrayObject()
setArrayValues(cooked, c.cooked)
raw := vm.r.newArrayObject()
setArrayValues(raw, c.raw)
cooked.propValueCount = len(c.cooked)
cooked.lengthProp.writable = false
raw.propValueCount = len(c.raw)
raw.lengthProp.writable = false
raw.preventExtensions(true)
raw.val.self = &taggedTemplateArray{
arrayObject: raw,
idPtr: &c.raw,
}
cooked._putProp("raw", raw.val, false, false, false)
cooked.preventExtensions(true)
cooked.val.self = &taggedTemplateArray{
arrayObject: cooked,
idPtr: &c.cooked,
}
vm.push(cooked.val)
vm.pc++
}
type _loadSuper struct{}
var loadSuper _loadSuper
func (_loadSuper) exec(vm *vm) {
homeObject := getHomeObject(vm.stack[vm.sb-1])
if proto := homeObject.Prototype(); proto != nil {
vm.push(proto)
} else {
vm.push(_undefined)
}
vm.pc++
}
type newClass struct {
ctor *Program
name unistring.String
source string
initFields *Program
privateFields, privateMethods []unistring.String // only set when dynamic resolution is needed
numPrivateFields, numPrivateMethods uint32
length int
hasPrivateEnv bool
}
type newDerivedClass struct {
newClass
}
func (vm *vm) createPrivateType(f *classFuncObject, numFields, numMethods uint32) {
typ := &privateEnvType{}
typ.numFields = numFields
typ.numMethods = numMethods
f.privateEnvType = typ
f.privateMethods = make([]Value, numMethods)
}
func (vm *vm) fillPrivateNamesMap(typ *privateEnvType, privateFields, privateMethods []unistring.String) {
if len(privateFields) > 0 || len(privateMethods) > 0 {
penv := vm.privEnv.names
if penv == nil {
penv = make(privateNames)
vm.privEnv.names = penv
}
for idx, field := range privateFields {
penv[field] = &privateId{
typ: typ,
idx: uint32(idx),
}
}
for idx, method := range privateMethods {
penv[method] = &privateId{
typ: typ,
idx: uint32(idx),
isMethod: true,
}
}
}
}
func (c *newClass) create(protoParent, ctorParent *Object, vm *vm, derived bool) (prototype, cls *Object) {
proto := vm.r.newBaseObject(protoParent, classObject)
f := vm.r.newClassFunc(c.name, c.length, ctorParent, derived)
f._putProp("prototype", proto.val, false, false, false)
proto._putProp("constructor", f.val, true, false, true)
f.prg = c.ctor
f.stash = vm.stash
f.src = c.source
f.initFields = c.initFields
if c.hasPrivateEnv {
vm.privEnv = &privateEnv{
outer: vm.privEnv,
}
vm.createPrivateType(f, c.numPrivateFields, c.numPrivateMethods)
vm.fillPrivateNamesMap(f.privateEnvType, c.privateFields, c.privateMethods)
vm.privEnv.instanceType = f.privateEnvType
}
f.privEnv = vm.privEnv
return proto.val, f.val
}
func (c *newClass) exec(vm *vm) {
proto, cls := c.create(vm.r.global.ObjectPrototype, vm.r.getFunctionPrototype(), vm, false)
sp := vm.sp
vm.stack.expand(sp + 1)
vm.stack[sp] = proto
vm.stack[sp+1] = cls
vm.sp = sp + 2
vm.pc++
}
func (c *newDerivedClass) exec(vm *vm) {
var protoParent *Object
var superClass *Object
if o := vm.stack[vm.sp-1]; o != _null {
if sc, ok := o.(*Object); !ok || sc.self.assertConstructor() == nil {
vm.throw(vm.r.NewTypeError("Class extends value is not a constructor or null"))
return
} else {
v := sc.self.getStr("prototype", nil)
if v != _null {
if o, ok := v.(*Object); ok {
protoParent = o
} else {
vm.throw(vm.r.NewTypeError("Class extends value does not have valid prototype property"))
return
}
}
superClass = sc
}
} else {
superClass = vm.r.getFunctionPrototype()
}
proto, cls := c.create(protoParent, superClass, vm, true)
vm.stack[vm.sp-1] = proto
vm.push(cls)
vm.pc++
}
// Creates a special instance of *classFuncObject which is only used during evaluation of a class declaration
// to initialise static fields and instance private methods of another class.
type newStaticFieldInit struct {
initFields *Program
numPrivateFields, numPrivateMethods uint32
}
func (c *newStaticFieldInit) exec(vm *vm) {
f := vm.r.newClassFunc("", 0, vm.r.getFunctionPrototype(), false)
if c.numPrivateFields > 0 || c.numPrivateMethods > 0 {
vm.createPrivateType(f, c.numPrivateFields, c.numPrivateMethods)
}
f.initFields = c.initFields
f.stash = vm.stash
vm.push(f.val)
vm.pc++
}
func (vm *vm) loadThis(v Value) {
if v != nil {
vm.push(v)
} else {
vm.throw(vm.r.newError(vm.r.getReferenceError(), "Must call super constructor in derived class before accessing 'this'"))
return
}
vm.pc++
}
type loadThisStash uint32
func (l loadThisStash) exec(vm *vm) {
vm.loadThis(*vm.getStashPtr(uint32(l)))
}
type loadThisStack struct{}
func (loadThisStack) exec(vm *vm) {
vm.loadThis(vm.stack[vm.sb])
}
func (vm *vm) getStashPtr(s uint32) *Value {
level := int(s) >> 24
idx := s & 0x00FFFFFF
stash := vm.stash
for i := 0; i < level; i++ {
stash = stash.outer
}
return &stash.values[idx]
}
type getThisDynamic struct{}
func (getThisDynamic) exec(vm *vm) {
for stash := vm.stash; stash != nil; stash = stash.outer {
if stash.obj == nil {
if v, exists := stash.getByName(thisBindingName); exists {
vm.push(v)
vm.pc++
return
}
}
}
vm.push(vm.r.globalObject)
vm.pc++
}
type throwConst struct {
v interface{}
}
func (t throwConst) exec(vm *vm) {
vm.throw(t.v)
}
type resolveThisStack struct{}
func (r resolveThisStack) exec(vm *vm) {
vm.refStack = append(vm.refStack, &thisRef{v: (*[]Value)(&vm.stack), idx: vm.sb})
vm.pc++
}
type resolveThisStash uint32
func (r resolveThisStash) exec(vm *vm) {
level := int(r) >> 24
idx := r & 0x00FFFFFF
stash := vm.stash
for i := 0; i < level; i++ {
stash = stash.outer
}
vm.refStack = append(vm.refStack, &thisRef{v: &stash.values, idx: int(idx)})
vm.pc++
}
type resolveThisDynamic struct{}
func (resolveThisDynamic) exec(vm *vm) {
for stash := vm.stash; stash != nil; stash = stash.outer {
if stash.obj == nil {
if idx, exists := stash.names[thisBindingName]; exists {
vm.refStack = append(vm.refStack, &thisRef{v: &stash.values, idx: int(idx &^ maskTyp)})
vm.pc++
return
}
}
}
panic(vm.r.newError(vm.r.getReferenceError(), "Compiler bug: 'this' reference is not found in resolveThisDynamic"))
}
type defineComputedKey int
func (offset defineComputedKey) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-int(offset)])
if h, ok := obj.self.(*classFuncObject); ok {
key := toPropertyKey(vm.stack[vm.sp-1])
h.computedKeys = append(h.computedKeys, key)
vm.sp--
vm.pc++
return
}
panic(vm.r.NewTypeError("Compiler bug: unexpected target for defineComputedKey: %v", obj))
}
type loadComputedKey int
func (idx loadComputedKey) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sb-1])
if h, ok := obj.self.(*classFuncObject); ok {
vm.push(h.computedKeys[idx])
vm.pc++
return
}
panic(vm.r.NewTypeError("Compiler bug: unexpected target for loadComputedKey: %v", obj))
}
type initStaticElements struct {
privateFields, privateMethods []unistring.String
}
func (i *initStaticElements) exec(vm *vm) {
cls := vm.stack[vm.sp-1]
staticInit := vm.r.toObject(vm.stack[vm.sp-3])
vm.sp -= 2
if h, ok := staticInit.self.(*classFuncObject); ok {
h._putProp("prototype", cls, true, true, true) // so that 'super' resolution work
h.privEnv = vm.privEnv
if h.privateEnvType != nil {
vm.privEnv.staticType = h.privateEnvType
vm.fillPrivateNamesMap(h.privateEnvType, i.privateFields, i.privateMethods)
}
h._initFields(vm.r.toObject(cls))
vm.stack[vm.sp-1] = cls
vm.pc++
return
}
panic(vm.r.NewTypeError("Compiler bug: unexpected target for initStaticElements: %v", staticInit))
}
type definePrivateMethod struct {
idx int
targetOffset int
}
func (d *definePrivateMethod) getPrivateMethods(vm *vm) []Value {
obj := vm.r.toObject(vm.stack[vm.sp-d.targetOffset])
if cls, ok := obj.self.(*classFuncObject); ok {
return cls.privateMethods
} else {
panic(vm.r.NewTypeError("Compiler bug: wrong target type for definePrivateMethod: %T", obj.self))
}
}
func (d *definePrivateMethod) exec(vm *vm) {
methods := d.getPrivateMethods(vm)
methods[d.idx] = vm.stack[vm.sp-1]
vm.sp--
vm.pc++
}
type definePrivateGetter struct {
definePrivateMethod
}
func (d *definePrivateGetter) exec(vm *vm) {
methods := d.getPrivateMethods(vm)
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
p, _ := methods[d.idx].(*valueProperty)
if p == nil {
p = &valueProperty{
accessor: true,
}
methods[d.idx] = p
}
if p.getterFunc != nil {
vm.throw(vm.r.NewTypeError("Private getter has already been declared"))
return
}
p.getterFunc = method
vm.sp--
vm.pc++
}
type definePrivateSetter struct {
definePrivateMethod
}
func (d *definePrivateSetter) exec(vm *vm) {
methods := d.getPrivateMethods(vm)
val := vm.stack[vm.sp-1]
method := vm.r.toObject(val)
p, _ := methods[d.idx].(*valueProperty)
if p == nil {
p = &valueProperty{
accessor: true,
}
methods[d.idx] = p
}
if p.setterFunc != nil {
vm.throw(vm.r.NewTypeError("Private setter has already been declared"))
return
}
p.setterFunc = method
vm.sp--
vm.pc++
}
type definePrivateProp struct {
idx int
}
func (d *definePrivateProp) exec(vm *vm) {
f := vm.r.toObject(vm.stack[vm.sb-1]).self.(*classFuncObject)
obj := vm.r.toObject(vm.stack[vm.sp-2])
penv := obj.self.getPrivateEnv(f.privateEnvType, false)
penv.fields[d.idx] = vm.stack[vm.sp-1]
vm.sp--
vm.pc++
}
type getPrivatePropRes resolvedPrivateName
func (vm *vm) getPrivateType(level uint8, isStatic bool) *privateEnvType {
e := vm.privEnv
for i := uint8(0); i < level; i++ {
e = e.outer
}
if isStatic {
return e.staticType
}
return e.instanceType
}
func (g *getPrivatePropRes) _get(base Value, vm *vm) Value {
return vm.getPrivateProp(base, g.name, vm.getPrivateType(g.level, g.isStatic), g.idx, g.isMethod)
}
func (g *getPrivatePropRes) exec(vm *vm) {
vm.stack[vm.sp-1] = g._get(vm.stack[vm.sp-1], vm)
vm.pc++
}
type getPrivatePropId privateId
func (g *getPrivatePropId) exec(vm *vm) {
vm.stack[vm.sp-1] = vm.getPrivateProp(vm.stack[vm.sp-1], g.name, g.typ, g.idx, g.isMethod)
vm.pc++
}
type getPrivatePropIdCallee privateId
func (g *getPrivatePropIdCallee) exec(vm *vm) {
prop := vm.getPrivateProp(vm.stack[vm.sp-1], g.name, g.typ, g.idx, g.isMethod)
if prop == nil {
prop = memberUnresolved{valueUnresolved{r: vm.r, ref: (*privateId)(g).string()}}
}
vm.push(prop)
vm.pc++
}
func (vm *vm) getPrivateProp(base Value, name unistring.String, typ *privateEnvType, idx uint32, isMethod bool) Value {
obj := vm.r.toObject(base)
penv := obj.self.getPrivateEnv(typ, false)
var v Value
if penv != nil {
if isMethod {
v = penv.methods[idx]
} else {
v = penv.fields[idx]
if v == nil {
panic(vm.r.NewTypeError("Private member #%s is accessed before it is initialized", name))
}
}
} else {
panic(vm.r.NewTypeError("Cannot read private member #%s from an object whose class did not declare it", name))
}
if prop, ok := v.(*valueProperty); ok {
if prop.getterFunc == nil {
panic(vm.r.NewTypeError("'#%s' was defined without a getter", name))
}
v = prop.get(obj)
}
return v
}
type getPrivatePropResCallee getPrivatePropRes
func (g *getPrivatePropResCallee) exec(vm *vm) {
prop := (*getPrivatePropRes)(g)._get(vm.stack[vm.sp-1], vm)
if prop == nil {
prop = memberUnresolved{valueUnresolved{r: vm.r, ref: (*resolvedPrivateName)(g).string()}}
}
vm.push(prop)
vm.pc++
}
func (vm *vm) setPrivateProp(base Value, name unistring.String, typ *privateEnvType, idx uint32, isMethod bool, val Value) {
obj := vm.r.toObject(base)
penv := obj.self.getPrivateEnv(typ, false)
if penv != nil {
if isMethod {
v := penv.methods[idx]
if prop, ok := v.(*valueProperty); ok {
if prop.setterFunc != nil {
prop.set(base, val)
} else {
panic(vm.r.NewTypeError("Cannot assign to read only property '#%s'", name))
}
} else {
panic(vm.r.NewTypeError("Private method '#%s' is not writable", name))
}
} else {
ptr := &penv.fields[idx]
if *ptr == nil {
panic(vm.r.NewTypeError("Private member #%s is accessed before it is initialized", name))
}
*ptr = val
}
} else {
panic(vm.r.NewTypeError("Cannot write private member #%s from an object whose class did not declare it", name))
}
}
func (vm *vm) exceptionFromValue(x interface{}) *Exception {
var ex *Exception
switch x1 := x.(type) {
case *Object:
ex = &Exception{
val: x1,
}
if er, ok := x1.self.(*errorObject); ok {
ex.stack = er.stack
}
case Value:
ex = &Exception{
val: x1,
}
case *Exception:
ex = x1
case typeError:
ex = &Exception{
val: vm.r.NewTypeError(string(x1)),
}
case referenceError:
ex = &Exception{
val: vm.r.newError(vm.r.getReferenceError(), string(x1)),
}
case rangeError:
ex = &Exception{
val: vm.r.newError(vm.r.getRangeError(), string(x1)),
}
case syntaxError:
ex = &Exception{
val: vm.r.newError(vm.r.getSyntaxError(), string(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))
*/
return nil
}
if ex.stack == nil {
ex.stack = vm.captureStack(make([]StackFrame, 0, len(vm.callStack)+1), 0)
}
return ex
}
type setPrivatePropRes resolvedPrivateName
func (p *setPrivatePropRes) _set(base Value, val Value, vm *vm) {
vm.setPrivateProp(base, p.name, vm.getPrivateType(p.level, p.isStatic), p.idx, p.isMethod, val)
}
func (p *setPrivatePropRes) exec(vm *vm) {
v := vm.stack[vm.sp-1]
p._set(vm.stack[vm.sp-2], v, vm)
vm.stack[vm.sp-2] = v
vm.sp--
vm.pc++
}
type setPrivatePropResP setPrivatePropRes
func (p *setPrivatePropResP) exec(vm *vm) {
v := vm.stack[vm.sp-1]
(*setPrivatePropRes)(p)._set(vm.stack[vm.sp-2], v, vm)
vm.sp -= 2
vm.pc++
}
type setPrivatePropId privateId
func (p *setPrivatePropId) exec(vm *vm) {
v := vm.stack[vm.sp-1]
vm.setPrivateProp(vm.stack[vm.sp-2], p.name, p.typ, p.idx, p.isMethod, v)
vm.stack[vm.sp-2] = v
vm.sp--
vm.pc++
}
type setPrivatePropIdP privateId
func (p *setPrivatePropIdP) exec(vm *vm) {
v := vm.stack[vm.sp-1]
vm.setPrivateProp(vm.stack[vm.sp-2], p.name, p.typ, p.idx, p.isMethod, v)
vm.sp -= 2
vm.pc++
}
type popPrivateEnv struct{}
func (popPrivateEnv) exec(vm *vm) {
vm.privEnv = vm.privEnv.outer
vm.pc++
}
type privateInRes resolvedPrivateName
func (i *privateInRes) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-1])
pe := obj.self.getPrivateEnv(vm.getPrivateType(i.level, i.isStatic), false)
if pe != nil && (i.isMethod && pe.methods[i.idx] != nil || !i.isMethod && pe.fields[i.idx] != nil) {
vm.stack[vm.sp-1] = valueTrue
} else {
vm.stack[vm.sp-1] = valueFalse
}
vm.pc++
}
type privateInId privateId
func (i *privateInId) exec(vm *vm) {
obj := vm.r.toObject(vm.stack[vm.sp-1])
pe := obj.self.getPrivateEnv(i.typ, false)
if pe != nil && (i.isMethod && pe.methods[i.idx] != nil || !i.isMethod && pe.fields[i.idx] != nil) {
vm.stack[vm.sp-1] = valueTrue
} else {
vm.stack[vm.sp-1] = valueFalse
}
vm.pc++
}
type getPrivateRefRes resolvedPrivateName
func (r *getPrivateRefRes) exec(vm *vm) {
vm.refStack = append(vm.refStack, &privateRefRes{
base: vm.stack[vm.sp-1].ToObject(vm.r),
name: (*resolvedPrivateName)(r),
})
vm.sp--
vm.pc++
}
type getPrivateRefId privateId
func (r *getPrivateRefId) exec(vm *vm) {
vm.refStack = append(vm.refStack, &privateRefId{
base: vm.stack[vm.sp-1].ToObject(vm.r),
id: (*privateId)(r),
})
vm.sp--
vm.pc++
}
func (y *yieldMarker) exec(vm *vm) {
vm.pc = -vm.pc // this will terminate the run loop
vm.push(y) // marker so the caller knows it's a yield, not a return
}
func (y *yieldMarker) String() string {
if y == yieldEmpty {
return "empty"
}
switch y.resultType {
case resultYield:
return "yield"
case resultYieldRes:
return "yieldRes"
case resultYieldDelegate:
return "yield*"
case resultYieldDelegateRes:
return "yield*Res"
case resultAwait:
return "await"
default:
return "unknown"
}
}