package goja
import (
"fmt"
"reflect"
"github.com/dop251/goja/unistring"
)
type resultType uint8
const (
resultNormal resultType = iota
resultYield
resultYieldRes
resultYieldDelegate
resultYieldDelegateRes
resultAwait
)
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 }
)
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
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 {
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 })
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
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
}
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
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 ]
}
return
}
func (g *generator ) enterNext () {
g .vm .pushCtx ()
g .vm .pushTryFrame (tryPanicMarker , -1 )
g .vm .callStack = append (g .vm .callStack , context {pc : -2 })
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
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
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .