1117 lines
25 KiB
Go
1117 lines
25 KiB
Go
|
package goja
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/dop251/goja/unistring"
|
||
|
)
|
||
|
|
||
|
type resultType uint8
|
||
|
|
||
|
const (
|
||
|
resultNormal resultType = iota
|
||
|
resultYield
|
||
|
resultYieldRes // a yield that expects a value in return
|
||
|
resultYieldDelegate // yield*
|
||
|
resultYieldDelegateRes
|
||
|
resultAwait
|
||
|
)
|
||
|
|
||
|
// used both as an instruction and as a Value
|
||
|
type yieldMarker struct {
|
||
|
valueNull
|
||
|
resultType resultType
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
await = &yieldMarker{resultType: resultAwait}
|
||
|
|
||
|
yield = &yieldMarker{resultType: resultYield}
|
||
|
yieldRes = &yieldMarker{resultType: resultYieldRes}
|
||
|
yieldDelegate = &yieldMarker{resultType: resultYieldDelegate}
|
||
|
yieldDelegateRes = &yieldMarker{resultType: resultYieldDelegateRes}
|
||
|
yieldEmpty = &yieldMarker{resultType: resultYield}
|
||
|
)
|
||
|
|
||
|
// AsyncContextTracker is a handler that allows to track an async execution context to ensure it remains
|
||
|
// consistent across all callback invocations.
|
||
|
// Whenever a Promise reaction job is scheduled the Grab method is called. It is supposed to return the
|
||
|
// current context. The same context will be supplied to the Resumed method before the reaction job is
|
||
|
// executed. The Exited method is called after the reaction job is finished.
|
||
|
// This means that for each invocation of the Grab method there will be exactly one subsequent invocation
|
||
|
// of Resumed and then Exited methods (assuming the Promise is fulfilled or rejected). Also, the Resumed/Exited
|
||
|
// calls cannot be nested, so Exited can simply clear the current context instead of popping from a stack.
|
||
|
// Note, this works for both async functions and regular Promise.then()/Promise.catch() callbacks.
|
||
|
// See TestAsyncContextTracker for more insight.
|
||
|
//
|
||
|
// To register it call Runtime.SetAsyncContextTracker().
|
||
|
type AsyncContextTracker interface {
|
||
|
Grab() (trackingObject interface{})
|
||
|
Resumed(trackingObject interface{})
|
||
|
Exited()
|
||
|
}
|
||
|
|
||
|
type funcObjectImpl interface {
|
||
|
source() String
|
||
|
}
|
||
|
|
||
|
type baseFuncObject struct {
|
||
|
baseObject
|
||
|
|
||
|
lenProp valueProperty
|
||
|
}
|
||
|
|
||
|
type baseJsFuncObject struct {
|
||
|
baseFuncObject
|
||
|
|
||
|
stash *stash
|
||
|
privEnv *privateEnv
|
||
|
|
||
|
prg *Program
|
||
|
src string
|
||
|
strict bool
|
||
|
}
|
||
|
|
||
|
type funcObject struct {
|
||
|
baseJsFuncObject
|
||
|
}
|
||
|
|
||
|
type generatorFuncObject struct {
|
||
|
baseJsFuncObject
|
||
|
}
|
||
|
|
||
|
type asyncFuncObject struct {
|
||
|
baseJsFuncObject
|
||
|
}
|
||
|
|
||
|
type classFuncObject struct {
|
||
|
baseJsFuncObject
|
||
|
initFields *Program
|
||
|
computedKeys []Value
|
||
|
|
||
|
privateEnvType *privateEnvType
|
||
|
privateMethods []Value
|
||
|
|
||
|
derived bool
|
||
|
}
|
||
|
|
||
|
type methodFuncObject struct {
|
||
|
baseJsFuncObject
|
||
|
homeObject *Object
|
||
|
}
|
||
|
|
||
|
type generatorMethodFuncObject struct {
|
||
|
methodFuncObject
|
||
|
}
|
||
|
|
||
|
type asyncMethodFuncObject struct {
|
||
|
methodFuncObject
|
||
|
}
|
||
|
|
||
|
type arrowFuncObject struct {
|
||
|
baseJsFuncObject
|
||
|
funcObj *Object
|
||
|
newTarget Value
|
||
|
}
|
||
|
|
||
|
type asyncArrowFuncObject struct {
|
||
|
arrowFuncObject
|
||
|
}
|
||
|
|
||
|
type nativeFuncObject struct {
|
||
|
baseFuncObject
|
||
|
|
||
|
f func(FunctionCall) Value
|
||
|
construct func(args []Value, newTarget *Object) *Object
|
||
|
}
|
||
|
|
||
|
type wrappedFuncObject struct {
|
||
|
nativeFuncObject
|
||
|
wrapped reflect.Value
|
||
|
}
|
||
|
|
||
|
type boundFuncObject struct {
|
||
|
nativeFuncObject
|
||
|
wrapped *Object
|
||
|
}
|
||
|
|
||
|
type generatorState uint8
|
||
|
|
||
|
const (
|
||
|
genStateUndefined generatorState = iota
|
||
|
genStateSuspendedStart
|
||
|
genStateExecuting
|
||
|
genStateSuspendedYield
|
||
|
genStateSuspendedYieldRes
|
||
|
genStateCompleted
|
||
|
)
|
||
|
|
||
|
type generatorObject struct {
|
||
|
baseObject
|
||
|
gen generator
|
||
|
delegated *iteratorRecord
|
||
|
state generatorState
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) source() String {
|
||
|
return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.f
|
||
|
}
|
||
|
|
||
|
func (f *wrappedFuncObject) exportType() reflect.Type {
|
||
|
return f.wrapped.Type()
|
||
|
}
|
||
|
|
||
|
func (f *wrappedFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.wrapped.Interface()
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) _addProto(n unistring.String) Value {
|
||
|
if n == "prototype" {
|
||
|
if _, exists := f.values[n]; !exists {
|
||
|
return f.addPrototype()
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) getStr(p unistring.String, receiver Value) Value {
|
||
|
return f.getStrWithOwnProp(f.getOwnPropStr(p), p, receiver)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) getOwnPropStr(name unistring.String) Value {
|
||
|
if v := f._addProto(name); v != nil {
|
||
|
return v
|
||
|
}
|
||
|
|
||
|
return f.baseObject.getOwnPropStr(name)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
|
||
|
f._addProto(name)
|
||
|
return f.baseObject.setOwnStr(name, val, throw)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
|
||
|
return f._setForeignStr(name, f.getOwnPropStr(name), val, receiver, throw)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
|
||
|
f._addProto(name)
|
||
|
return f.baseObject.defineOwnPropertyStr(name, descr, throw)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) deleteStr(name unistring.String, throw bool) bool {
|
||
|
f._addProto(name)
|
||
|
return f.baseObject.deleteStr(name, throw)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) addPrototype() Value {
|
||
|
proto := f.val.runtime.NewObject()
|
||
|
proto.self._putProp("constructor", f.val, true, false, true)
|
||
|
return f._putProp("prototype", proto, true, false, false)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) hasOwnPropertyStr(name unistring.String) bool {
|
||
|
if f.baseObject.hasOwnPropertyStr(name) {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
if name == "prototype" {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) stringKeys(all bool, accum []Value) []Value {
|
||
|
if all {
|
||
|
if _, exists := f.values["prototype"]; !exists {
|
||
|
accum = append(accum, asciiString("prototype"))
|
||
|
}
|
||
|
}
|
||
|
return f.baseFuncObject.stringKeys(all, accum)
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) iterateStringKeys() iterNextFunc {
|
||
|
if _, exists := f.values["prototype"]; !exists {
|
||
|
f.addPrototype()
|
||
|
}
|
||
|
return f.baseFuncObject.iterateStringKeys()
|
||
|
}
|
||
|
|
||
|
func (f *baseFuncObject) createInstance(newTarget *Object) *Object {
|
||
|
r := f.val.runtime
|
||
|
if newTarget == nil {
|
||
|
newTarget = f.val
|
||
|
}
|
||
|
proto := r.getPrototypeFromCtor(newTarget, nil, r.global.ObjectPrototype)
|
||
|
|
||
|
return f.val.runtime.newBaseObject(proto, classObject).val
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) source() String {
|
||
|
return newStringValue(f.src)
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) construct(args []Value, newTarget *Object) *Object {
|
||
|
if newTarget == nil {
|
||
|
newTarget = f.val
|
||
|
}
|
||
|
proto := newTarget.self.getStr("prototype", nil)
|
||
|
var protoObj *Object
|
||
|
if p, ok := proto.(*Object); ok {
|
||
|
protoObj = p
|
||
|
} else {
|
||
|
protoObj = f.val.runtime.global.ObjectPrototype
|
||
|
}
|
||
|
|
||
|
obj := f.val.runtime.newBaseObject(protoObj, classObject).val
|
||
|
ret := f.call(FunctionCall{
|
||
|
This: obj,
|
||
|
Arguments: args,
|
||
|
}, newTarget)
|
||
|
|
||
|
if ret, ok := ret.(*Object); ok {
|
||
|
return ret
|
||
|
}
|
||
|
return obj
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) Call(FunctionCall) Value {
|
||
|
panic(f.val.runtime.NewTypeError("Class constructor cannot be invoked without 'new'"))
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) vmCall(vm *vm, n int) {
|
||
|
f.Call(FunctionCall{})
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) createInstance(args []Value, newTarget *Object) (instance *Object) {
|
||
|
if f.derived {
|
||
|
if ctor := f.prototype.self.assertConstructor(); ctor != nil {
|
||
|
instance = ctor(args, newTarget)
|
||
|
} else {
|
||
|
panic(f.val.runtime.NewTypeError("Super constructor is not a constructor"))
|
||
|
}
|
||
|
} else {
|
||
|
instance = f.baseFuncObject.createInstance(newTarget)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) _initFields(instance *Object) {
|
||
|
if f.privateEnvType != nil {
|
||
|
penv := instance.self.getPrivateEnv(f.privateEnvType, true)
|
||
|
penv.methods = f.privateMethods
|
||
|
}
|
||
|
if f.initFields != nil {
|
||
|
vm := f.val.runtime.vm
|
||
|
vm.pushCtx()
|
||
|
vm.prg = f.initFields
|
||
|
vm.stash = f.stash
|
||
|
vm.privEnv = f.privEnv
|
||
|
vm.newTarget = nil
|
||
|
|
||
|
// so that 'super' base could be correctly resolved (including from direct eval())
|
||
|
vm.push(f.val)
|
||
|
|
||
|
vm.sb = vm.sp
|
||
|
vm.push(instance)
|
||
|
vm.pc = 0
|
||
|
ex := vm.runTry()
|
||
|
vm.popCtx()
|
||
|
if ex != nil {
|
||
|
panic(ex)
|
||
|
}
|
||
|
vm.sp -= 2
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) construct(args []Value, newTarget *Object) *Object {
|
||
|
if newTarget == nil {
|
||
|
newTarget = f.val
|
||
|
}
|
||
|
if f.prg == nil {
|
||
|
instance := f.createInstance(args, newTarget)
|
||
|
f._initFields(instance)
|
||
|
return instance
|
||
|
} else {
|
||
|
var instance *Object
|
||
|
var thisVal Value
|
||
|
if !f.derived {
|
||
|
instance = f.createInstance(args, newTarget)
|
||
|
f._initFields(instance)
|
||
|
thisVal = instance
|
||
|
}
|
||
|
ret := f._call(args, newTarget, thisVal)
|
||
|
|
||
|
if ret, ok := ret.(*Object); ok {
|
||
|
return ret
|
||
|
}
|
||
|
if f.derived {
|
||
|
r := f.val.runtime
|
||
|
if ret != _undefined {
|
||
|
panic(r.NewTypeError("Derived constructors may only return object or undefined"))
|
||
|
}
|
||
|
if v := r.vm.stack[r.vm.sp+1]; v != nil { // using residual 'this' value (a bit hacky)
|
||
|
instance = r.toObject(v)
|
||
|
} else {
|
||
|
panic(r.newError(r.getReferenceError(), "Must call super constructor in derived class before returning from derived constructor"))
|
||
|
}
|
||
|
}
|
||
|
return instance
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *classFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
|
||
|
return f.construct
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) Call(call FunctionCall) Value {
|
||
|
return f.call(call, nil)
|
||
|
}
|
||
|
|
||
|
func (f *arrowFuncObject) Call(call FunctionCall) Value {
|
||
|
return f._call(call.Arguments, f.newTarget, nil)
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) __call(args []Value, newTarget, this Value) (Value, *Exception) {
|
||
|
vm := f.val.runtime.vm
|
||
|
|
||
|
vm.stack.expand(vm.sp + len(args) + 1)
|
||
|
vm.stack[vm.sp] = f.val
|
||
|
vm.sp++
|
||
|
vm.stack[vm.sp] = this
|
||
|
vm.sp++
|
||
|
for _, arg := range args {
|
||
|
if arg != nil {
|
||
|
vm.stack[vm.sp] = arg
|
||
|
} else {
|
||
|
vm.stack[vm.sp] = _undefined
|
||
|
}
|
||
|
vm.sp++
|
||
|
}
|
||
|
|
||
|
vm.pushTryFrame(tryPanicMarker, -1)
|
||
|
defer vm.popTryFrame()
|
||
|
|
||
|
var needPop bool
|
||
|
if vm.prg != nil {
|
||
|
vm.pushCtx()
|
||
|
vm.callStack = append(vm.callStack, context{pc: -2}) // extra frame so that run() halts after ret
|
||
|
needPop = true
|
||
|
} else {
|
||
|
vm.pc = -2
|
||
|
vm.pushCtx()
|
||
|
}
|
||
|
|
||
|
vm.args = len(args)
|
||
|
vm.prg = f.prg
|
||
|
vm.stash = f.stash
|
||
|
vm.privEnv = f.privEnv
|
||
|
vm.newTarget = newTarget
|
||
|
vm.pc = 0
|
||
|
for {
|
||
|
ex := vm.runTryInner()
|
||
|
if ex != nil {
|
||
|
return nil, ex
|
||
|
}
|
||
|
if vm.halted() {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if needPop {
|
||
|
vm.popCtx()
|
||
|
}
|
||
|
|
||
|
return vm.pop(), nil
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) _call(args []Value, newTarget, this Value) Value {
|
||
|
res, ex := f.__call(args, newTarget, this)
|
||
|
if ex != nil {
|
||
|
panic(ex)
|
||
|
}
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) call(call FunctionCall, newTarget Value) Value {
|
||
|
return f._call(call.Arguments, newTarget, nilSafe(call.This))
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *baseFuncObject) exportType() reflect.Type {
|
||
|
return reflectTypeFunc
|
||
|
}
|
||
|
|
||
|
func (f *baseFuncObject) typeOf() String {
|
||
|
return stringFunction
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *funcObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
|
||
|
return f.construct
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) vmCall(vm *vm, n int) {
|
||
|
vm.pushCtx()
|
||
|
vm.args = n
|
||
|
vm.prg = f.prg
|
||
|
vm.stash = f.stash
|
||
|
vm.privEnv = f.privEnv
|
||
|
vm.pc = 0
|
||
|
vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = vm.stack[vm.sp-n-2], vm.stack[vm.sp-n-1]
|
||
|
}
|
||
|
|
||
|
func (f *arrowFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *arrowFuncObject) vmCall(vm *vm, n int) {
|
||
|
vm.pushCtx()
|
||
|
vm.args = n
|
||
|
vm.prg = f.prg
|
||
|
vm.stash = f.stash
|
||
|
vm.privEnv = f.privEnv
|
||
|
vm.pc = 0
|
||
|
vm.stack[vm.sp-n-1], vm.stack[vm.sp-n-2] = nil, vm.stack[vm.sp-n-1]
|
||
|
vm.newTarget = f.newTarget
|
||
|
}
|
||
|
|
||
|
func (f *arrowFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *baseFuncObject) init(name unistring.String, length Value) {
|
||
|
f.baseObject.init()
|
||
|
|
||
|
f.lenProp.configurable = true
|
||
|
f.lenProp.value = length
|
||
|
f._put("length", &f.lenProp)
|
||
|
|
||
|
f._putProp("name", stringValueFromRaw(name), false, false, true)
|
||
|
}
|
||
|
|
||
|
func hasInstance(val *Object, v Value) bool {
|
||
|
if v, ok := v.(*Object); ok {
|
||
|
o := val.self.getStr("prototype", nil)
|
||
|
if o1, ok := o.(*Object); ok {
|
||
|
for {
|
||
|
v = v.self.proto()
|
||
|
if v == nil {
|
||
|
return false
|
||
|
}
|
||
|
if o1 == v {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
panic(val.runtime.NewTypeError("prototype is not an object"))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (f *baseFuncObject) hasInstance(v Value) bool {
|
||
|
return hasInstance(f.val, v)
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) defaultConstruct(ccall func(ConstructorCall) *Object, args []Value, newTarget *Object) *Object {
|
||
|
obj := f.createInstance(newTarget)
|
||
|
ret := ccall(ConstructorCall{
|
||
|
This: obj,
|
||
|
Arguments: args,
|
||
|
NewTarget: newTarget,
|
||
|
})
|
||
|
|
||
|
if ret != nil {
|
||
|
return ret
|
||
|
}
|
||
|
return obj
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
if f.f != nil {
|
||
|
return f.f, true
|
||
|
}
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) vmCall(vm *vm, n int) {
|
||
|
if f.f != nil {
|
||
|
vm.pushCtx()
|
||
|
vm.prg = nil
|
||
|
vm.sb = vm.sp - n // so that [sb-1] points to the callee
|
||
|
//fmt.Println(">>>>>>>>>>>>>>>>>", n, vm.stack[vm.sp-n:vm.sp])
|
||
|
ret := f.f(FunctionCall{
|
||
|
Arguments: vm.stack[vm.sp-n : vm.sp],
|
||
|
This: vm.stack[vm.sp-n-2],
|
||
|
})
|
||
|
if ret == nil {
|
||
|
ret = _undefined
|
||
|
}
|
||
|
//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", ret.toString().String())
|
||
|
if strings.HasPrefix(ret.toString().String(), "GoError: ") {
|
||
|
//fmt.Println(">>>>>>>>>>>>>>>>>>>>>>", 111)
|
||
|
//vm.throw(Exception{
|
||
|
// val: ret.toString(),
|
||
|
//})
|
||
|
vm.throw(ret)
|
||
|
} else {
|
||
|
vm.stack[vm.sp-n-2] = ret
|
||
|
vm.popCtx()
|
||
|
}
|
||
|
} else {
|
||
|
vm.stack[vm.sp-n-2] = _undefined
|
||
|
}
|
||
|
vm.sp -= n + 1
|
||
|
vm.pc++
|
||
|
}
|
||
|
|
||
|
func (f *nativeFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
|
||
|
return f.construct
|
||
|
}
|
||
|
|
||
|
func (f *boundFuncObject) hasInstance(v Value) bool {
|
||
|
return instanceOfOperator(v, f.wrapped)
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) prepareForVmCall(call FunctionCall) {
|
||
|
vm := f.val.runtime.vm
|
||
|
args := call.Arguments
|
||
|
vm.stack.expand(vm.sp + len(args) + 1)
|
||
|
vm.stack[vm.sp] = call.This
|
||
|
vm.sp++
|
||
|
vm.stack[vm.sp] = f.val
|
||
|
vm.sp++
|
||
|
for _, arg := range args {
|
||
|
if arg != nil {
|
||
|
vm.stack[vm.sp] = arg
|
||
|
} else {
|
||
|
vm.stack[vm.sp] = _undefined
|
||
|
}
|
||
|
vm.sp++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) asyncCall(call FunctionCall, vmCall func(*vm, int)) Value {
|
||
|
f.prepareForVmCall(call)
|
||
|
ar := &asyncRunner{
|
||
|
f: f.val,
|
||
|
vmCall: vmCall,
|
||
|
}
|
||
|
ar.start(len(call.Arguments))
|
||
|
return ar.promiseCap.promise
|
||
|
}
|
||
|
|
||
|
func (f *asyncFuncObject) Call(call FunctionCall) Value {
|
||
|
return f.asyncCall(call, f.baseJsFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
func (f *asyncFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *asyncFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *asyncArrowFuncObject) Call(call FunctionCall) Value {
|
||
|
return f.asyncCall(call, f.arrowFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
func (f *asyncArrowFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *asyncArrowFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *asyncArrowFuncObject) vmCall(vm *vm, n int) {
|
||
|
f.asyncVmCall(vm, n, f.arrowFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
func (f *asyncMethodFuncObject) Call(call FunctionCall) Value {
|
||
|
return f.asyncCall(call, f.methodFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
func (f *asyncMethodFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *asyncMethodFuncObject) export(ctx *objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *asyncMethodFuncObject) vmCall(vm *vm, n int) {
|
||
|
f.asyncVmCall(vm, n, f.methodFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) asyncVmCall(vm *vm, n int, vmCall func(*vm, int)) {
|
||
|
ar := &asyncRunner{
|
||
|
f: f.val,
|
||
|
vmCall: vmCall,
|
||
|
}
|
||
|
ar.start(n)
|
||
|
vm.push(ar.promiseCap.promise)
|
||
|
vm.pc++
|
||
|
}
|
||
|
|
||
|
func (f *asyncFuncObject) vmCall(vm *vm, n int) {
|
||
|
f.asyncVmCall(vm, n, f.baseJsFuncObject.vmCall)
|
||
|
}
|
||
|
|
||
|
type asyncRunner struct {
|
||
|
gen generator
|
||
|
promiseCap *promiseCapability
|
||
|
f *Object
|
||
|
vmCall func(*vm, int)
|
||
|
}
|
||
|
|
||
|
func (ar *asyncRunner) onFulfilled(call FunctionCall) Value {
|
||
|
ar.gen.vm.curAsyncRunner = ar
|
||
|
defer func() {
|
||
|
ar.gen.vm.curAsyncRunner = nil
|
||
|
}()
|
||
|
arg := call.Argument(0)
|
||
|
res, resType, ex := ar.gen.next(arg)
|
||
|
ar.step(res, resType == resultNormal, ex)
|
||
|
return _undefined
|
||
|
}
|
||
|
|
||
|
func (ar *asyncRunner) onRejected(call FunctionCall) Value {
|
||
|
ar.gen.vm.curAsyncRunner = ar
|
||
|
defer func() {
|
||
|
ar.gen.vm.curAsyncRunner = nil
|
||
|
}()
|
||
|
reason := call.Argument(0)
|
||
|
res, resType, ex := ar.gen.nextThrow(reason)
|
||
|
ar.step(res, resType == resultNormal, ex)
|
||
|
return _undefined
|
||
|
}
|
||
|
|
||
|
func (ar *asyncRunner) step(res Value, done bool, ex *Exception) {
|
||
|
r := ar.f.runtime
|
||
|
if done || ex != nil {
|
||
|
if ex == nil {
|
||
|
ar.promiseCap.resolve(res)
|
||
|
} else {
|
||
|
ar.promiseCap.reject(ex.val)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// await
|
||
|
promise := r.promiseResolve(r.getPromise(), res)
|
||
|
promise.self.(*Promise).addReactions(&promiseReaction{
|
||
|
typ: promiseReactionFulfill,
|
||
|
handler: &jobCallback{callback: ar.onFulfilled},
|
||
|
asyncRunner: ar,
|
||
|
}, &promiseReaction{
|
||
|
typ: promiseReactionReject,
|
||
|
handler: &jobCallback{callback: ar.onRejected},
|
||
|
asyncRunner: ar,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (ar *asyncRunner) start(nArgs int) {
|
||
|
r := ar.f.runtime
|
||
|
ar.gen.vm = r.vm
|
||
|
ar.promiseCap = r.newPromiseCapability(r.getPromise())
|
||
|
sp := r.vm.sp
|
||
|
ar.gen.enter()
|
||
|
ar.vmCall(r.vm, nArgs)
|
||
|
res, resType, ex := ar.gen.step()
|
||
|
ar.step(res, resType == resultNormal, ex)
|
||
|
if ex != nil {
|
||
|
r.vm.sp = sp - nArgs - 2
|
||
|
}
|
||
|
r.vm.popTryFrame()
|
||
|
r.vm.popCtx()
|
||
|
}
|
||
|
|
||
|
type generator struct {
|
||
|
ctx execCtx
|
||
|
vm *vm
|
||
|
|
||
|
tryStackLen, iterStackLen, refStackLen uint32
|
||
|
}
|
||
|
|
||
|
func (g *generator) storeLengths() {
|
||
|
g.tryStackLen, g.iterStackLen, g.refStackLen = uint32(len(g.vm.tryStack)), uint32(len(g.vm.iterStack)), uint32(len(g.vm.refStack))
|
||
|
}
|
||
|
|
||
|
func (g *generator) enter() {
|
||
|
g.vm.pushCtx()
|
||
|
g.vm.pushTryFrame(tryPanicMarker, -1)
|
||
|
g.vm.prg, g.vm.sb, g.vm.pc = nil, -1, -2 // so that vm.run() halts after ret
|
||
|
g.storeLengths()
|
||
|
}
|
||
|
|
||
|
func (g *generator) step() (res Value, resultType resultType, ex *Exception) {
|
||
|
for {
|
||
|
ex = g.vm.runTryInner()
|
||
|
if ex != nil {
|
||
|
return
|
||
|
}
|
||
|
if g.vm.halted() {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
res = g.vm.pop()
|
||
|
if ym, ok := res.(*yieldMarker); ok {
|
||
|
resultType = ym.resultType
|
||
|
g.ctx = execCtx{}
|
||
|
g.vm.pc = -g.vm.pc + 1
|
||
|
if res != yieldEmpty {
|
||
|
res = g.vm.pop()
|
||
|
} else {
|
||
|
res = nil
|
||
|
}
|
||
|
g.vm.suspend(&g.ctx, g.tryStackLen, g.iterStackLen, g.refStackLen)
|
||
|
g.vm.sp = g.vm.sb - 1
|
||
|
g.vm.callStack = g.vm.callStack[:len(g.vm.callStack)-1] // remove the frame with pc == -2, as ret would do
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (g *generator) enterNext() {
|
||
|
g.vm.pushCtx()
|
||
|
g.vm.pushTryFrame(tryPanicMarker, -1)
|
||
|
g.vm.callStack = append(g.vm.callStack, context{pc: -2}) // extra frame so that vm.run() halts after ret
|
||
|
g.storeLengths()
|
||
|
g.vm.resume(&g.ctx)
|
||
|
}
|
||
|
|
||
|
func (g *generator) next(v Value) (Value, resultType, *Exception) {
|
||
|
g.enterNext()
|
||
|
if v != nil {
|
||
|
g.vm.push(v)
|
||
|
}
|
||
|
res, done, ex := g.step()
|
||
|
g.vm.popTryFrame()
|
||
|
g.vm.popCtx()
|
||
|
return res, done, ex
|
||
|
}
|
||
|
|
||
|
func (g *generator) nextThrow(v interface{}) (Value, resultType, *Exception) {
|
||
|
g.enterNext()
|
||
|
ex := g.vm.handleThrow(v)
|
||
|
if ex != nil {
|
||
|
g.vm.popTryFrame()
|
||
|
g.vm.popCtx()
|
||
|
return nil, resultNormal, ex
|
||
|
}
|
||
|
|
||
|
res, resType, ex := g.step()
|
||
|
g.vm.popTryFrame()
|
||
|
g.vm.popCtx()
|
||
|
return res, resType, ex
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) init(vmCall func(*vm, int), nArgs int) {
|
||
|
g.baseObject.init()
|
||
|
vm := g.val.runtime.vm
|
||
|
g.gen.vm = vm
|
||
|
|
||
|
g.gen.enter()
|
||
|
vmCall(vm, nArgs)
|
||
|
|
||
|
_, _, ex := g.gen.step()
|
||
|
|
||
|
vm.popTryFrame()
|
||
|
if ex != nil {
|
||
|
panic(ex)
|
||
|
}
|
||
|
|
||
|
g.state = genStateSuspendedStart
|
||
|
vm.popCtx()
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) validate() {
|
||
|
if g.state == genStateExecuting {
|
||
|
panic(g.val.runtime.NewTypeError("Illegal generator state"))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) step(res Value, resType resultType, ex *Exception) Value {
|
||
|
if ex != nil {
|
||
|
g.delegated = nil
|
||
|
g.state = genStateCompleted
|
||
|
panic(ex)
|
||
|
}
|
||
|
switch resType {
|
||
|
case resultYield:
|
||
|
g.state = genStateSuspendedYield
|
||
|
return g.val.runtime.createIterResultObject(res, false)
|
||
|
case resultYieldDelegate:
|
||
|
g.state = genStateSuspendedYield
|
||
|
return g.delegate(res)
|
||
|
case resultYieldRes:
|
||
|
g.state = genStateSuspendedYieldRes
|
||
|
return g.val.runtime.createIterResultObject(res, false)
|
||
|
case resultYieldDelegateRes:
|
||
|
g.state = genStateSuspendedYieldRes
|
||
|
return g.delegate(res)
|
||
|
case resultNormal:
|
||
|
g.state = genStateCompleted
|
||
|
return g.val.runtime.createIterResultObject(res, true)
|
||
|
default:
|
||
|
panic(g.val.runtime.NewTypeError("Runtime bug: unexpected result type: %v", resType))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) delegate(v Value) Value {
|
||
|
ex := g.val.runtime.try(func() {
|
||
|
g.delegated = g.val.runtime.getIterator(v, nil)
|
||
|
})
|
||
|
if ex != nil {
|
||
|
g.delegated = nil
|
||
|
g.state = genStateCompleted
|
||
|
return g.step(g.gen.nextThrow(ex))
|
||
|
}
|
||
|
return g.next(_undefined)
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) tryCallDelegated(fn func() (Value, bool)) (ret Value, done bool) {
|
||
|
ex := g.val.runtime.try(func() {
|
||
|
ret, done = fn()
|
||
|
})
|
||
|
if ex != nil {
|
||
|
g.delegated = nil
|
||
|
g.state = genStateExecuting
|
||
|
return g.step(g.gen.nextThrow(ex)), false
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) callDelegated(method func(FunctionCall) Value, v Value) (Value, bool) {
|
||
|
res := g.val.runtime.toObject(method(FunctionCall{This: g.delegated.iterator, Arguments: []Value{v}}))
|
||
|
if iteratorComplete(res) {
|
||
|
g.delegated = nil
|
||
|
return iteratorValue(res), true
|
||
|
}
|
||
|
return res, false
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) next(v Value) Value {
|
||
|
g.validate()
|
||
|
if g.state == genStateCompleted {
|
||
|
return g.val.runtime.createIterResultObject(_undefined, true)
|
||
|
}
|
||
|
if g.delegated != nil {
|
||
|
res, done := g.tryCallDelegated(func() (Value, bool) {
|
||
|
return g.callDelegated(g.delegated.next, v)
|
||
|
})
|
||
|
if !done {
|
||
|
return res
|
||
|
} else {
|
||
|
v = res
|
||
|
}
|
||
|
}
|
||
|
if g.state != genStateSuspendedYieldRes {
|
||
|
v = nil
|
||
|
}
|
||
|
g.state = genStateExecuting
|
||
|
return g.step(g.gen.next(v))
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) throw(v Value) Value {
|
||
|
g.validate()
|
||
|
if g.state == genStateSuspendedStart {
|
||
|
g.state = genStateCompleted
|
||
|
}
|
||
|
if g.state == genStateCompleted {
|
||
|
panic(v)
|
||
|
}
|
||
|
if d := g.delegated; d != nil {
|
||
|
res, done := g.tryCallDelegated(func() (Value, bool) {
|
||
|
method := toMethod(g.delegated.iterator.self.getStr("throw", nil))
|
||
|
if method != nil {
|
||
|
return g.callDelegated(method, v)
|
||
|
}
|
||
|
g.delegated = nil
|
||
|
d.returnIter()
|
||
|
panic(g.val.runtime.NewTypeError("The iterator does not provide a 'throw' method"))
|
||
|
})
|
||
|
if !done {
|
||
|
return res
|
||
|
}
|
||
|
if g.state != genStateSuspendedYieldRes {
|
||
|
res = nil
|
||
|
}
|
||
|
g.state = genStateExecuting
|
||
|
return g.step(g.gen.next(res))
|
||
|
}
|
||
|
g.state = genStateExecuting
|
||
|
return g.step(g.gen.nextThrow(v))
|
||
|
}
|
||
|
|
||
|
func (g *generatorObject) _return(v Value) Value {
|
||
|
g.validate()
|
||
|
if g.state == genStateSuspendedStart {
|
||
|
g.state = genStateCompleted
|
||
|
}
|
||
|
|
||
|
if g.state == genStateCompleted {
|
||
|
return g.val.runtime.createIterResultObject(v, true)
|
||
|
}
|
||
|
|
||
|
if d := g.delegated; d != nil {
|
||
|
res, done := g.tryCallDelegated(func() (Value, bool) {
|
||
|
method := toMethod(g.delegated.iterator.self.getStr("return", nil))
|
||
|
if method != nil {
|
||
|
return g.callDelegated(method, v)
|
||
|
}
|
||
|
g.delegated = nil
|
||
|
return v, true
|
||
|
})
|
||
|
if !done {
|
||
|
return res
|
||
|
} else {
|
||
|
v = res
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g.state = genStateExecuting
|
||
|
|
||
|
g.gen.enterNext()
|
||
|
|
||
|
vm := g.gen.vm
|
||
|
var ex *Exception
|
||
|
for len(vm.tryStack) > 0 {
|
||
|
tf := &vm.tryStack[len(vm.tryStack)-1]
|
||
|
if int(tf.callStackLen) != len(vm.callStack) {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if tf.finallyPos >= 0 {
|
||
|
vm.sp = int(tf.sp)
|
||
|
vm.stash = tf.stash
|
||
|
vm.privEnv = tf.privEnv
|
||
|
ex1 := vm.restoreStacks(tf.iterLen, tf.refLen)
|
||
|
if ex1 != nil {
|
||
|
ex = ex1
|
||
|
vm.popTryFrame()
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
vm.pc = int(tf.finallyPos)
|
||
|
tf.catchPos = tryPanicMarker
|
||
|
tf.finallyPos = -1
|
||
|
tf.finallyRet = -2 // -1 would cause it to continue after leaveFinally
|
||
|
for {
|
||
|
ex1 := vm.runTryInner()
|
||
|
if ex1 != nil {
|
||
|
ex = ex1
|
||
|
vm.popTryFrame()
|
||
|
break
|
||
|
}
|
||
|
if vm.halted() {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
vm.popTryFrame()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g.state = genStateCompleted
|
||
|
|
||
|
vm.popTryFrame()
|
||
|
|
||
|
if ex == nil {
|
||
|
ex = vm.restoreStacks(g.gen.iterStackLen, g.gen.refStackLen)
|
||
|
}
|
||
|
|
||
|
if ex != nil {
|
||
|
panic(ex)
|
||
|
}
|
||
|
|
||
|
vm.callStack = vm.callStack[:len(vm.callStack)-1]
|
||
|
vm.sp = vm.sb - 1
|
||
|
vm.popCtx()
|
||
|
|
||
|
return g.val.runtime.createIterResultObject(v, true)
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) generatorCall(vmCall func(*vm, int), nArgs int) Value {
|
||
|
o := &Object{runtime: f.val.runtime}
|
||
|
|
||
|
genObj := &generatorObject{
|
||
|
baseObject: baseObject{
|
||
|
class: classObject,
|
||
|
val: o,
|
||
|
extensible: true,
|
||
|
},
|
||
|
}
|
||
|
o.self = genObj
|
||
|
genObj.init(vmCall, nArgs)
|
||
|
genObj.prototype = o.runtime.getPrototypeFromCtor(f.val, nil, o.runtime.getGeneratorPrototype())
|
||
|
return o
|
||
|
}
|
||
|
|
||
|
func (f *baseJsFuncObject) generatorVmCall(vmCall func(*vm, int), nArgs int) {
|
||
|
vm := f.val.runtime.vm
|
||
|
vm.push(f.generatorCall(vmCall, nArgs))
|
||
|
vm.pc++
|
||
|
}
|
||
|
|
||
|
func (f *generatorFuncObject) vmCall(_ *vm, nArgs int) {
|
||
|
f.generatorVmCall(f.baseJsFuncObject.vmCall, nArgs)
|
||
|
}
|
||
|
|
||
|
func (f *generatorFuncObject) Call(call FunctionCall) Value {
|
||
|
f.prepareForVmCall(call)
|
||
|
return f.generatorCall(f.baseJsFuncObject.vmCall, len(call.Arguments))
|
||
|
}
|
||
|
|
||
|
func (f *generatorFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *generatorFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|
||
|
|
||
|
func (f *generatorFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (f *generatorMethodFuncObject) vmCall(_ *vm, nArgs int) {
|
||
|
f.generatorVmCall(f.methodFuncObject.vmCall, nArgs)
|
||
|
}
|
||
|
|
||
|
func (f *generatorMethodFuncObject) Call(call FunctionCall) Value {
|
||
|
f.prepareForVmCall(call)
|
||
|
return f.generatorCall(f.methodFuncObject.vmCall, len(call.Arguments))
|
||
|
}
|
||
|
|
||
|
func (f *generatorMethodFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
|
||
|
return f.Call, true
|
||
|
}
|
||
|
|
||
|
func (f *generatorMethodFuncObject) export(*objectExportCtx) interface{} {
|
||
|
return f.Call
|
||
|
}
|