gojs/dop251/goja/func.go
2024-10-02 00:07:02 +08:00

1106 lines
25 KiB
Go

package goja
import (
"fmt"
"reflect"
"apigo.cc/apigo/gojs/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
ret := f.f(FunctionCall{
Arguments: vm.stack[vm.sp-n : vm.sp],
This: vm.stack[vm.sp-n-2],
})
if ret == nil {
ret = _undefined
}
vm.stack[vm.sp-n-2] = ret
vm.popCtx()
} else {
vm.stack[vm.sp-n-2] = _undefined
}
vm.sp -= n + 1
vm.pc++
}
func (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
}