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(), //}) //stack := make([]StackFrame, 0) //for i := len(vm.callStack) - 1; i >= 0; 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}) // //prg := vm.callStack[i].prg // //stack = append(stack, string(funcName)+" "+prg.src.Position(prg.sourceOffset(frame.pc)).String()) // } //} vm.throw(&Exception{ val: ret.ToString(), }) return } 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 }