12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230 |
- package goja
- import (
- "bytes"
- "errors"
- "fmt"
- "go/ast"
- "hash/maphash"
- "math"
- "math/big"
- "math/bits"
- "math/rand"
- "reflect"
- "runtime"
- "strconv"
- "time"
- "golang.org/x/text/collate"
- js_ast "github.com/dop251/goja/ast"
- "github.com/dop251/goja/file"
- "github.com/dop251/goja/parser"
- "github.com/dop251/goja/unistring"
- )
- const (
- sqrt1_2 float64 = math.Sqrt2 / 2
- deoptimiseRegexp = false
- )
- var (
- typeCallable = reflect.TypeOf(Callable(nil))
- typeValue = reflect.TypeOf((*Value)(nil)).Elem()
- typeObject = reflect.TypeOf((*Object)(nil))
- typeTime = reflect.TypeOf(time.Time{})
- typeBigInt = reflect.TypeOf((*big.Int)(nil))
- typeBytes = reflect.TypeOf(([]byte)(nil))
- )
- type iterationKind int
- const (
- iterationKindKey iterationKind = iota
- iterationKindValue
- iterationKindKeyValue
- )
- type global struct {
- stash stash
- Object *Object
- Array *Object
- Function *Object
- String *Object
- Number *Object
- BigInt *Object
- Boolean *Object
- RegExp *Object
- Date *Object
- Symbol *Object
- Proxy *Object
- Reflect *Object
- Promise *Object
- Math *Object
- JSON *Object
- AsyncFunction *Object
- ArrayBuffer *Object
- DataView *Object
- TypedArray *Object
- Uint8Array *Object
- Uint8ClampedArray *Object
- Int8Array *Object
- Uint16Array *Object
- Int16Array *Object
- Uint32Array *Object
- Int32Array *Object
- Float32Array *Object
- Float64Array *Object
- BigInt64Array *Object
- BigUint64Array *Object
- WeakSet *Object
- WeakMap *Object
- Map *Object
- Set *Object
- Error *Object
- AggregateError *Object
- TypeError *Object
- ReferenceError *Object
- SyntaxError *Object
- RangeError *Object
- EvalError *Object
- URIError *Object
- GoError *Object
- ObjectPrototype *Object
- ArrayPrototype *Object
- NumberPrototype *Object
- BigIntPrototype *Object
- StringPrototype *Object
- BooleanPrototype *Object
- FunctionPrototype *Object
- RegExpPrototype *Object
- DatePrototype *Object
- SymbolPrototype *Object
- ArrayBufferPrototype *Object
- DataViewPrototype *Object
- TypedArrayPrototype *Object
- WeakSetPrototype *Object
- WeakMapPrototype *Object
- MapPrototype *Object
- SetPrototype *Object
- PromisePrototype *Object
- GeneratorFunctionPrototype *Object
- GeneratorFunction *Object
- GeneratorPrototype *Object
- AsyncFunctionPrototype *Object
- IteratorPrototype *Object
- ArrayIteratorPrototype *Object
- MapIteratorPrototype *Object
- SetIteratorPrototype *Object
- StringIteratorPrototype *Object
- RegExpStringIteratorPrototype *Object
- ErrorPrototype *Object
- Eval *Object
- thrower *Object
- stdRegexpProto *guardedObject
- weakSetAdder *Object
- weakMapAdder *Object
- mapAdder *Object
- setAdder *Object
- arrayValues *Object
- arrayToString *Object
- stringproto_trimEnd *Object
- stringproto_trimStart *Object
- parseFloat, parseInt *Object
- typedArrayValues *Object
- }
- type Flag int
- const (
- FLAG_NOT_SET Flag = iota
- FLAG_FALSE
- FLAG_TRUE
- )
- func (f Flag) Bool() bool {
- return f == FLAG_TRUE
- }
- func ToFlag(b bool) Flag {
- if b {
- return FLAG_TRUE
- }
- return FLAG_FALSE
- }
- type RandSource func() float64
- type Now func() time.Time
- type Runtime struct {
- global global
- globalObject *Object
- stringSingleton *stringObject
- rand RandSource
- now Now
- _collator *collate.Collator
- parserOptions []parser.Option
- symbolRegistry map[unistring.String]*Symbol
- fieldsInfoCache map[reflect.Type]*reflectFieldsInfo
- methodsInfoCache map[reflect.Type]*reflectMethodsInfo
- fieldNameMapper FieldNameMapper
- vm *vm
- hash *maphash.Hash
- idSeq uint64
- jobQueue []func()
- promiseRejectionTracker PromiseRejectionTracker
- asyncContextTracker AsyncContextTracker
- }
- type StackFrame struct {
- prg *Program
- funcName unistring.String
- pc int
- }
- func (f *StackFrame) SrcName() string {
- if f.prg == nil {
- return "<native>"
- }
- return f.prg.src.Name()
- }
- func (f *StackFrame) FuncName() string {
- if f.funcName == "" && f.prg == nil {
- return "<native>"
- }
- if f.funcName == "" {
- return "<anonymous>"
- }
- return f.funcName.String()
- }
- func (f *StackFrame) Position() file.Position {
- if f.prg == nil || f.prg.src == nil {
- return file.Position{}
- }
- return f.prg.src.Position(f.prg.sourceOffset(f.pc))
- }
- func (f *StackFrame) WriteToValueBuilder(b *StringBuilder) {
- if f.prg != nil {
- if n := f.prg.funcName; n != "" {
- b.WriteString(stringValueFromRaw(n))
- b.writeASCII(" (")
- }
- p := f.Position()
- if p.Filename != "" {
- b.WriteUTF8String(p.Filename)
- } else {
- b.writeASCII("<eval>")
- }
- b.WriteRune(':')
- b.writeASCII(strconv.Itoa(p.Line))
- b.WriteRune(':')
- b.writeASCII(strconv.Itoa(p.Column))
- b.WriteRune('(')
- b.writeASCII(strconv.Itoa(f.pc))
- b.WriteRune(')')
- if f.prg.funcName != "" {
- b.WriteRune(')')
- }
- } else {
- if f.funcName != "" {
- b.WriteString(stringValueFromRaw(f.funcName))
- b.writeASCII(" (")
- }
- b.writeASCII("native")
- if f.funcName != "" {
- b.WriteRune(')')
- }
- }
- }
- func (f *StackFrame) Write(b *bytes.Buffer) {
- if f.prg != nil {
- if n := f.prg.funcName; n != "" {
- b.WriteString(n.String())
- b.WriteString(" (")
- }
- p := f.Position()
- if p.Filename != "" {
- b.WriteString(p.Filename)
- } else {
- b.WriteString("<eval>")
- }
- b.WriteByte(':')
- b.WriteString(strconv.Itoa(p.Line))
- b.WriteByte(':')
- b.WriteString(strconv.Itoa(p.Column))
- b.WriteByte('(')
- b.WriteString(strconv.Itoa(f.pc))
- b.WriteByte(')')
- if f.prg.funcName != "" {
- b.WriteByte(')')
- }
- } else {
- if f.funcName != "" {
- b.WriteString(f.funcName.String())
- b.WriteString(" (")
- }
- b.WriteString("native")
- if f.funcName != "" {
- b.WriteByte(')')
- }
- }
- }
- // An un-catchable exception is not catchable by try/catch statements (finally is not executed either),
- // but it is returned as an error to a Go caller rather than causing a panic.
- type uncatchableException interface {
- error
- _uncatchableException()
- }
- type Exception struct {
- val Value
- stack []StackFrame
- }
- type baseUncatchableException struct {
- Exception
- }
- func (e *baseUncatchableException) _uncatchableException() {}
- type InterruptedError struct {
- baseUncatchableException
- iface interface{}
- }
- func (e *InterruptedError) Unwrap() error {
- if err, ok := e.iface.(error); ok {
- return err
- }
- return nil
- }
- type StackOverflowError struct {
- baseUncatchableException
- }
- func (e *InterruptedError) Value() interface{} {
- return e.iface
- }
- func (e *InterruptedError) String() string {
- if e == nil {
- return "<nil>"
- }
- var b bytes.Buffer
- if e.iface != nil {
- b.WriteString(fmt.Sprint(e.iface))
- b.WriteByte('\n')
- }
- e.writeFullStack(&b)
- return b.String()
- }
- func (e *InterruptedError) Error() string {
- if e == nil || e.iface == nil {
- return "<nil>"
- }
- var b bytes.Buffer
- b.WriteString(fmt.Sprint(e.iface))
- e.writeShortStack(&b)
- return b.String()
- }
- func (e *Exception) writeFullStack(b *bytes.Buffer) {
- for _, frame := range e.stack {
- b.WriteString("\tat ")
- frame.Write(b)
- b.WriteByte('\n')
- }
- }
- func (e *Exception) writeShortStack(b *bytes.Buffer) {
- if len(e.stack) > 0 && (e.stack[0].prg != nil || e.stack[0].funcName != "") {
- b.WriteString(" at ")
- e.stack[0].Write(b)
- }
- }
- func (e *Exception) String() string {
- if e == nil {
- return "<nil>"
- }
- var b bytes.Buffer
- if e.val != nil {
- b.WriteString(e.val.String())
- b.WriteByte('\n')
- }
- e.writeFullStack(&b)
- return b.String()
- }
- func (e *Exception) Error() string {
- if e == nil {
- return "<nil>"
- }
- var b bytes.Buffer
- if e.val != nil {
- b.WriteString(e.val.String())
- }
- e.writeShortStack(&b)
- return b.String()
- }
- func (e *Exception) Value() Value {
- return e.val
- }
- func (e *Exception) Unwrap() error {
- if obj, ok := e.val.(*Object); ok {
- if obj.runtime.getGoError().self.hasInstance(obj) {
- if val := obj.Get("value"); val != nil {
- e1, _ := val.Export().(error)
- return e1
- }
- }
- }
- return nil
- }
- func (e *Exception) Stack() []StackFrame {
- return e.stack
- }
- func (r *Runtime) createIterProto(val *Object) objectImpl {
- o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
- o._putSym(SymIterator, valueProp(r.newNativeFunc(r.returnThis, "[Symbol.iterator]", 0), true, false, true))
- return o
- }
- func (r *Runtime) getIteratorPrototype() *Object {
- var o *Object
- if o = r.global.IteratorPrototype; o == nil {
- o = &Object{runtime: r}
- r.global.IteratorPrototype = o
- o.self = r.createIterProto(o)
- }
- return o
- }
- func (r *Runtime) init() {
- r.rand = rand.Float64
- r.now = time.Now
- r.global.ObjectPrototype = &Object{runtime: r}
- r.newTemplatedObject(getObjectProtoTemplate(), r.global.ObjectPrototype)
- r.globalObject = &Object{runtime: r}
- r.newTemplatedObject(getGlobalObjectTemplate(), r.globalObject)
- r.vm = &vm{
- r: r,
- }
- r.vm.init()
- }
- func (r *Runtime) typeErrorResult(throw bool, args ...interface{}) {
- if throw {
- panic(r.NewTypeError(args...))
- }
- }
- func (r *Runtime) newError(typ *Object, format string, args ...interface{}) Value {
- var msg string
- if len(args) > 0 {
- msg = fmt.Sprintf(format, args...)
- } else {
- msg = format
- }
- return r.builtin_new(typ, []Value{newStringValue(msg)})
- }
- func (r *Runtime) throwReferenceError(name unistring.String) {
- panic(r.newReferenceError(name))
- }
- func (r *Runtime) newReferenceError(name unistring.String) Value {
- return r.newError(r.getReferenceError(), "%s is not defined", name)
- }
- func (r *Runtime) newSyntaxError(msg string, offset int) Value {
- return r.builtin_new(r.getSyntaxError(), []Value{newStringValue(msg)})
- }
- func newBaseObjectObj(obj, proto *Object, class string) *baseObject {
- o := &baseObject{
- class: class,
- val: obj,
- extensible: true,
- prototype: proto,
- }
- obj.self = o
- o.init()
- return o
- }
- func newGuardedObj(proto *Object, class string) *guardedObject {
- return &guardedObject{
- baseObject: baseObject{
- class: class,
- extensible: true,
- prototype: proto,
- },
- }
- }
- func (r *Runtime) newBaseObject(proto *Object, class string) (o *baseObject) {
- v := &Object{runtime: r}
- return newBaseObjectObj(v, proto, class)
- }
- func (r *Runtime) newGuardedObject(proto *Object, class string) (o *guardedObject) {
- v := &Object{runtime: r}
- o = newGuardedObj(proto, class)
- v.self = o
- o.val = v
- o.init()
- return
- }
- func (r *Runtime) NewObject() (v *Object) {
- return r.newBaseObject(r.global.ObjectPrototype, classObject).val
- }
- // CreateObject creates an object with given prototype. Equivalent of Object.create(proto).
- func (r *Runtime) CreateObject(proto *Object) *Object {
- return r.newBaseObject(proto, classObject).val
- }
- func (r *Runtime) NewArray(items ...interface{}) *Object {
- values := make([]Value, len(items))
- for i, item := range items {
- values[i] = r.ToValue(item)
- }
- return r.newArrayValues(values)
- }
- func (r *Runtime) NewTypeError(args ...interface{}) *Object {
- msg := ""
- if len(args) > 0 {
- f, _ := args[0].(string)
- msg = fmt.Sprintf(f, args[1:]...)
- }
- return r.builtin_new(r.getTypeError(), []Value{newStringValue(msg)})
- }
- func (r *Runtime) NewGoError(err error) *Object {
- e := r.newError(r.getGoError(), err.Error()).(*Object)
- e.Set("value", err)
- return e
- }
- func (r *Runtime) newFunc(name unistring.String, length int, strict bool) (f *funcObject) {
- f = &funcObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) newAsyncFunc(name unistring.String, length int, strict bool) (f *asyncFuncObject) {
- f = &asyncFuncObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.class = classFunction
- f.prototype = r.getAsyncFunctionPrototype()
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) newGeneratorFunc(name unistring.String, length int, strict bool) (f *generatorFuncObject) {
- f = &generatorFuncObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.class = classFunction
- f.prototype = r.getGeneratorFunctionPrototype()
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- f._putProp("prototype", r.newBaseObject(r.getGeneratorPrototype(), classObject).val, true, false, false)
- return
- }
- func (r *Runtime) newClassFunc(name unistring.String, length int, proto *Object, derived bool) (f *classFuncObject) {
- v := &Object{runtime: r}
- f = &classFuncObject{}
- f.class = classFunction
- f.val = v
- f.extensible = true
- f.strict = true
- f.derived = derived
- v.self = f
- f.prototype = proto
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) initBaseJsFunction(f *baseJsFuncObject, strict bool) {
- v := &Object{runtime: r}
- f.class = classFunction
- f.val = v
- f.extensible = true
- f.strict = strict
- f.prototype = r.getFunctionPrototype()
- }
- func (r *Runtime) newMethod(name unistring.String, length int, strict bool) (f *methodFuncObject) {
- f = &methodFuncObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) newGeneratorMethod(name unistring.String, length int, strict bool) (f *generatorMethodFuncObject) {
- f = &generatorMethodFuncObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.prototype = r.getGeneratorFunctionPrototype()
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- f._putProp("prototype", r.newBaseObject(r.getGeneratorPrototype(), classObject).val, true, false, false)
- return
- }
- func (r *Runtime) newAsyncMethod(name unistring.String, length int, strict bool) (f *asyncMethodFuncObject) {
- f = &asyncMethodFuncObject{}
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) initArrowFunc(f *arrowFuncObject, strict bool) {
- r.initBaseJsFunction(&f.baseJsFuncObject, strict)
- f.newTarget = r.vm.newTarget
- }
- func (r *Runtime) newArrowFunc(name unistring.String, length int, strict bool) (f *arrowFuncObject) {
- f = &arrowFuncObject{}
- r.initArrowFunc(f, strict)
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) newAsyncArrowFunc(name unistring.String, length int, strict bool) (f *asyncArrowFuncObject) {
- f = &asyncArrowFuncObject{}
- r.initArrowFunc(&f.arrowFuncObject, strict)
- f.class = classObject
- f.prototype = r.getAsyncFunctionPrototype()
- f.val.self = f
- f.init(name, intToValue(int64(length)))
- return
- }
- func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name unistring.String, length int64) *Object {
- v := &Object{runtime: r}
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.getFunctionPrototype(),
- },
- },
- }
- f.f = func(c FunctionCall) Value {
- thisObj, _ := c.This.(*Object)
- if thisObj != nil {
- res := call(ConstructorCall{
- This: thisObj,
- Arguments: c.Arguments,
- })
- if res == nil {
- return _undefined
- }
- return res
- }
- return f.defaultConstruct(call, c.Arguments, nil)
- }
- f.construct = func(args []Value, newTarget *Object) *Object {
- return f.defaultConstruct(call, args, newTarget)
- }
- v.self = f
- f.init(name, intToValue(length))
- proto := r.NewObject()
- proto.self._putProp("constructor", v, true, false, true)
- f._putProp("prototype", proto, true, false, false)
- return v
- }
- func (r *Runtime) newNativeConstructOnly(v *Object, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, length int64) *nativeFuncObject {
- return r.newNativeFuncAndConstruct(v, func(call FunctionCall) Value {
- return ctor(call.Arguments, nil)
- },
- func(args []Value, newTarget *Object) *Object {
- if newTarget == nil {
- newTarget = v
- }
- return ctor(args, newTarget)
- }, defaultProto, name, intToValue(length))
- }
- func (r *Runtime) newNativeFuncAndConstruct(v *Object, call func(call FunctionCall) Value, ctor func(args []Value, newTarget *Object) *Object, defaultProto *Object, name unistring.String, l Value) *nativeFuncObject {
- if v == nil {
- v = &Object{runtime: r}
- }
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.getFunctionPrototype(),
- },
- },
- f: call,
- construct: ctor,
- }
- v.self = f
- f.init(name, l)
- if defaultProto != nil {
- f._putProp("prototype", defaultProto, false, false, false)
- }
- return f
- }
- func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, name unistring.String, length int) *Object {
- v := &Object{runtime: r}
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.getFunctionPrototype(),
- },
- },
- f: call,
- }
- v.self = f
- f.init(name, intToValue(int64(length)))
- return v
- }
- func (r *Runtime) newWrappedFunc(value reflect.Value) *Object {
- v := &Object{runtime: r}
- f := &wrappedFuncObject{
- nativeFuncObject: nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.getFunctionPrototype(),
- },
- },
- f: r.wrapReflectFunc(value),
- },
- wrapped: value,
- }
- v.self = f
- name := unistring.NewFromString(runtime.FuncForPC(value.Pointer()).Name())
- f.init(name, intToValue(int64(value.Type().NumIn())))
- return v
- }
- func (r *Runtime) newNativeFuncConstructObj(v *Object, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length int) *nativeFuncObject {
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.getFunctionPrototype(),
- },
- },
- f: r.constructToCall(construct, proto),
- construct: r.wrapNativeConstruct(construct, v, proto),
- }
- f.init(name, intToValue(int64(length)))
- if proto != nil {
- f._putProp("prototype", proto, false, false, false)
- }
- return f
- }
- func (r *Runtime) newNativeFuncConstruct(v *Object, construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int64) *Object {
- return r.newNativeFuncConstructProto(v, construct, name, prototype, r.getFunctionPrototype(), length)
- }
- func (r *Runtime) newNativeFuncConstructProto(v *Object, construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int64) *Object {
- f := &nativeFuncObject{}
- f.class = classFunction
- f.val = v
- f.extensible = true
- v.self = f
- f.prototype = proto
- f.f = r.constructToCall(construct, prototype)
- f.construct = r.wrapNativeConstruct(construct, v, prototype)
- f.init(name, intToValue(length))
- if prototype != nil {
- f._putProp("prototype", prototype, false, false, false)
- }
- return v
- }
- func (r *Runtime) newPrimitiveObject(value Value, proto *Object, class string) *Object {
- v := &Object{runtime: r}
- o := &primitiveValueObject{}
- o.class = class
- o.val = v
- o.extensible = true
- v.self = o
- o.prototype = proto
- o.pValue = value
- o.init()
- return v
- }
- func (r *Runtime) builtin_Number(call FunctionCall) Value {
- if len(call.Arguments) > 0 {
- switch t := call.Arguments[0].(type) {
- case *Object:
- primValue := t.toPrimitiveNumber()
- if bigint, ok := primValue.(*valueBigInt); ok {
- return intToValue((*big.Int)(bigint).Int64())
- }
- return primValue.ToNumber()
- case *valueBigInt:
- return intToValue((*big.Int)(t).Int64())
- default:
- return t.ToNumber()
- }
- } else {
- return valueInt(0)
- }
- }
- func (r *Runtime) builtin_newNumber(args []Value, proto *Object) *Object {
- var v Value
- if len(args) > 0 {
- switch t := args[0].(type) {
- case *Object:
- primValue := t.toPrimitiveNumber()
- if bigint, ok := primValue.(*valueBigInt); ok {
- v = intToValue((*big.Int)(bigint).Int64())
- } else {
- v = primValue.ToNumber()
- }
- case *valueBigInt:
- v = intToValue((*big.Int)(t).Int64())
- default:
- v = t.ToNumber()
- }
- } else {
- v = intToValue(0)
- }
- return r.newPrimitiveObject(v, proto, classNumber)
- }
- func (r *Runtime) builtin_Boolean(call FunctionCall) Value {
- if len(call.Arguments) > 0 {
- if call.Arguments[0].ToBoolean() {
- return valueTrue
- } else {
- return valueFalse
- }
- } else {
- return valueFalse
- }
- }
- func (r *Runtime) builtin_newBoolean(args []Value, proto *Object) *Object {
- var v Value
- if len(args) > 0 {
- if args[0].ToBoolean() {
- v = valueTrue
- } else {
- v = valueFalse
- }
- } else {
- v = valueFalse
- }
- return r.newPrimitiveObject(v, proto, classBoolean)
- }
- func (r *Runtime) builtin_new(construct *Object, args []Value) *Object {
- return r.toConstructor(construct)(args, construct)
- }
- func (r *Runtime) builtin_thrower(call FunctionCall) Value {
- obj := r.toObject(call.This)
- strict := true
- switch fn := obj.self.(type) {
- case *funcObject:
- strict = fn.strict
- }
- r.typeErrorResult(strict, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them")
- return nil
- }
- func (r *Runtime) eval(srcVal String, direct, strict bool) Value {
- src := escapeInvalidUtf16(srcVal)
- vm := r.vm
- inGlobal := true
- if direct {
- for s := vm.stash; s != nil; s = s.outer {
- if s.isVariable() {
- inGlobal = false
- break
- }
- }
- }
- vm.pushCtx()
- funcObj := _undefined
- if !direct {
- vm.stash = &r.global.stash
- vm.privEnv = nil
- } else {
- if sb := vm.sb; sb > 0 {
- funcObj = vm.stack[sb-1]
- }
- }
- p, err := r.compile("<eval>", src, strict, inGlobal, r.vm)
- if err != nil {
- panic(err)
- }
- vm.prg = p
- vm.pc = 0
- vm.args = 0
- vm.result = _undefined
- vm.push(funcObj)
- vm.sb = vm.sp
- vm.push(nil) // this
- ex := vm.runTry()
- retval := vm.result
- vm.popCtx()
- if ex != nil {
- panic(ex)
- }
- vm.sp -= 2
- return retval
- }
- func (r *Runtime) builtin_eval(call FunctionCall) Value {
- if len(call.Arguments) == 0 {
- return _undefined
- }
- if str, ok := call.Arguments[0].(String); ok {
- return r.eval(str, false, false)
- }
- return call.Arguments[0]
- }
- func (r *Runtime) constructToCall(construct func(args []Value, proto *Object) *Object, proto *Object) func(call FunctionCall) Value {
- return func(call FunctionCall) Value {
- return construct(call.Arguments, proto)
- }
- }
- func (r *Runtime) wrapNativeConstruct(c func(args []Value, proto *Object) *Object, ctorObj, defProto *Object) func(args []Value, newTarget *Object) *Object {
- if c == nil {
- return nil
- }
- return func(args []Value, newTarget *Object) *Object {
- var proto *Object
- if newTarget != nil {
- proto = r.getPrototypeFromCtor(newTarget, ctorObj, defProto)
- } else {
- proto = defProto
- }
- return c(args, proto)
- }
- }
- func (r *Runtime) toCallable(v Value) func(FunctionCall) Value {
- if call, ok := r.toObject(v).self.assertCallable(); ok {
- return call
- }
- r.typeErrorResult(true, "Value is not callable: %s", v.toString())
- return nil
- }
- func (r *Runtime) checkObjectCoercible(v Value) {
- switch v.(type) {
- case valueUndefined, valueNull:
- r.typeErrorResult(true, "Value is not object coercible")
- }
- }
- func toInt8(v Value) int8 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return int8(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int8(int64(f))
- }
- }
- return 0
- }
- func toUint8(v Value) uint8 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return uint8(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint8(int64(f))
- }
- }
- return 0
- }
- func toUint8Clamp(v Value) uint8 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- if i < 0 {
- return 0
- }
- if i <= 255 {
- return uint8(i)
- }
- return 255
- }
- if num, ok := v.(valueFloat); ok {
- num := float64(num)
- if !math.IsNaN(num) {
- if num < 0 {
- return 0
- }
- if num > 255 {
- return 255
- }
- f := math.Floor(num)
- f1 := f + 0.5
- if f1 < num {
- return uint8(f + 1)
- }
- if f1 > num {
- return uint8(f)
- }
- r := uint8(f)
- if r&1 != 0 {
- return r + 1
- }
- return r
- }
- }
- return 0
- }
- func toInt16(v Value) int16 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return int16(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int16(int64(f))
- }
- }
- return 0
- }
- func toUint16(v Value) uint16 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return uint16(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint16(int64(f))
- }
- }
- return 0
- }
- func toInt32(v Value) int32 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return int32(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int32(int64(f))
- }
- }
- return 0
- }
- func toUint32(v Value) uint32 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return uint32(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint32(int64(f))
- }
- }
- return 0
- }
- func toInt64(v Value) int64 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return int64(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int64(f)
- }
- }
- return 0
- }
- func toUint64(v Value) uint64 {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return uint64(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint64(int64(f))
- }
- }
- return 0
- }
- func toInt(v Value) int {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return int(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int(f)
- }
- }
- return 0
- }
- func toUint(v Value) uint {
- v = v.ToNumber()
- if i, ok := v.(valueInt); ok {
- return uint(i)
- }
- if f, ok := v.(valueFloat); ok {
- f := float64(f)
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint(int64(f))
- }
- }
- return 0
- }
- func toFloat32(v Value) float32 {
- return float32(v.ToFloat())
- }
- func toLength(v Value) int64 {
- if v == nil {
- return 0
- }
- i := v.ToInteger()
- if i < 0 {
- return 0
- }
- if i >= maxInt {
- return maxInt - 1
- }
- return i
- }
- func (r *Runtime) toLengthUint32(v Value) uint32 {
- var intVal int64
- repeat:
- switch num := v.(type) {
- case valueInt:
- intVal = int64(num)
- case valueFloat:
- if v != _negativeZero {
- if i, ok := floatToInt(float64(num)); ok {
- intVal = i
- } else {
- goto fail
- }
- }
- case String:
- v = num.ToNumber()
- goto repeat
- default:
- // Legacy behaviour as specified in https://tc39.es/ecma262/#sec-arraysetlength (see the note)
- n2 := toUint32(v)
- n1 := v.ToNumber()
- if f, ok := n1.(valueFloat); ok {
- f := float64(f)
- if f != 0 || !math.Signbit(f) {
- goto fail
- }
- }
- if n1.ToInteger() != int64(n2) {
- goto fail
- }
- return n2
- }
- if intVal >= 0 && intVal <= math.MaxUint32 {
- return uint32(intVal)
- }
- fail:
- panic(r.newError(r.getRangeError(), "Invalid array length"))
- }
- func toIntStrict(i int64) int {
- if bits.UintSize == 32 {
- if i > math.MaxInt32 || i < math.MinInt32 {
- panic(rangeError("Integer value overflows 32-bit int"))
- }
- }
- return int(i)
- }
- func toIntClamp(i int64) int {
- if bits.UintSize == 32 {
- if i > math.MaxInt32 {
- return math.MaxInt32
- }
- if i < math.MinInt32 {
- return math.MinInt32
- }
- }
- return int(i)
- }
- func (r *Runtime) toIndex(v Value) int {
- num := v.ToInteger()
- if num >= 0 && num < maxInt {
- if bits.UintSize == 32 && num >= math.MaxInt32 {
- panic(r.newError(r.getRangeError(), "Index %s overflows int", v.String()))
- }
- return int(num)
- }
- panic(r.newError(r.getRangeError(), "Invalid index %s", v.String()))
- }
- func (r *Runtime) toBoolean(b bool) Value {
- if b {
- return valueTrue
- } else {
- return valueFalse
- }
- }
- // New creates an instance of a Javascript runtime that can be used to run code. Multiple instances may be created and
- // used simultaneously, however it is not possible to pass JS values across runtimes.
- func New() *Runtime {
- r := &Runtime{}
- r.init()
- return r
- }
- // Compile creates an internal representation of the JavaScript code that can be later run using the Runtime.RunProgram()
- // method. This representation is not linked to a runtime in any way and can be run in multiple runtimes (possibly
- // at the same time).
- func Compile(name, src string, strict bool) (*Program, error) {
- return compile(name, src, strict, true, nil)
- }
- // CompileAST creates an internal representation of the JavaScript code that can be later run using the Runtime.RunProgram()
- // method. This representation is not linked to a runtime in any way and can be run in multiple runtimes (possibly
- // at the same time).
- func CompileAST(prg *js_ast.Program, strict bool) (*Program, error) {
- return compileAST(prg, strict, true, nil)
- }
- // MustCompile is like Compile but panics if the code cannot be compiled.
- // It simplifies safe initialization of global variables holding compiled JavaScript code.
- func MustCompile(name, src string, strict bool) *Program {
- prg, err := Compile(name, src, strict)
- if err != nil {
- panic(err)
- }
- return prg
- }
- // Parse takes a source string and produces a parsed AST. Use this function if you want to pass options
- // to the parser, e.g.:
- //
- // p, err := Parse("test.js", "var a = true", parser.WithDisableSourceMaps)
- // if err != nil { /* ... */ }
- // prg, err := CompileAST(p, true)
- // // ...
- //
- // Otherwise use Compile which combines both steps.
- func Parse(name, src string, options ...parser.Option) (prg *js_ast.Program, err error) {
- prg, err1 := parser.ParseFile(nil, name, src, 0, options...)
- if err1 != nil {
- // FIXME offset
- err = &CompilerSyntaxError{
- CompilerError: CompilerError{
- Message: err1.Error(),
- },
- }
- }
- return
- }
- func compile(name, src string, strict, inGlobal bool, evalVm *vm, parserOptions ...parser.Option) (p *Program, err error) {
- prg, err := Parse(name, src, parserOptions...)
- if err != nil {
- return
- }
- return compileAST(prg, strict, inGlobal, evalVm)
- }
- func compileAST(prg *js_ast.Program, strict, inGlobal bool, evalVm *vm) (p *Program, err error) {
- c := newCompiler()
- defer func() {
- if x := recover(); x != nil {
- p = nil
- switch x1 := x.(type) {
- case *CompilerSyntaxError:
- err = x1
- default:
- panic(x)
- }
- }
- }()
- c.compile(prg, strict, inGlobal, evalVm)
- p = c.p
- return
- }
- func (r *Runtime) compile(name, src string, strict, inGlobal bool, evalVm *vm) (p *Program, err error) {
- p, err = compile(name, src, strict, inGlobal, evalVm, r.parserOptions...)
- if err != nil {
- switch x1 := err.(type) {
- case *CompilerSyntaxError:
- err = &Exception{
- val: r.builtin_new(r.getSyntaxError(), []Value{newStringValue(x1.Error())}),
- }
- case *CompilerReferenceError:
- err = &Exception{
- val: r.newError(r.getReferenceError(), x1.Message),
- } // TODO proper message
- }
- }
- return
- }
- // RunString executes the given string in the global context.
- func (r *Runtime) RunString(str string) (Value, error) {
- return r.RunScript("", str)
- }
- // RunScript executes the given string in the global context.
- func (r *Runtime) RunScript(name, src string) (Value, error) {
- p, err := r.compile(name, src, false, true, nil)
- if err != nil {
- return nil, err
- }
- return r.RunProgram(p)
- }
- func isUncatchableException(e error) bool {
- for ; e != nil; e = errors.Unwrap(e) {
- if _, ok := e.(uncatchableException); ok {
- return true
- }
- }
- return false
- }
- func asUncatchableException(v interface{}) error {
- switch v := v.(type) {
- case uncatchableException:
- return v
- case error:
- if isUncatchableException(v) {
- return v
- }
- }
- return nil
- }
- // RunProgram executes a pre-compiled (see Compile()) code in the global context.
- func (r *Runtime) RunProgram(p *Program) (result Value, err error) {
- vm := r.vm
- recursive := len(vm.callStack) > 0
- defer func() {
- if recursive {
- vm.sp -= 2
- vm.popCtx()
- } else {
- vm.callStack = vm.callStack[:len(vm.callStack)-1]
- }
- if x := recover(); x != nil {
- if ex := asUncatchableException(x); ex != nil {
- err = ex
- if len(vm.callStack) == 0 {
- r.leaveAbrupt()
- }
- } else {
- panic(x)
- }
- }
- }()
- if recursive {
- vm.pushCtx()
- vm.stash = &r.global.stash
- vm.privEnv = nil
- vm.newTarget = nil
- vm.args = 0
- sp := vm.sp
- vm.stack.expand(sp + 1)
- vm.stack[sp] = _undefined // 'callee'
- vm.stack[sp+1] = nil // 'this'
- vm.sb = sp + 1
- vm.sp = sp + 2
- } else {
- vm.callStack = append(vm.callStack, context{})
- }
- vm.prg = p
- vm.pc = 0
- vm.result = _undefined
- ex := vm.runTry()
- if ex == nil {
- result = r.vm.result
- } else {
- err = ex
- }
- if recursive {
- vm.clearStack()
- } else {
- vm.prg = nil
- vm.sb = -1
- r.leave()
- }
- return
- }
- // CaptureCallStack appends the current call stack frames to the stack slice (which may be nil) up to the specified depth.
- // The most recent frame will be the first one.
- // If depth <= 0 or more than the number of available frames, returns the entire stack.
- // This method is not safe for concurrent use and should only be called by a Go function that is
- // called from a running script.
- func (r *Runtime) CaptureCallStack(depth int, stack []StackFrame) []StackFrame {
- l := len(r.vm.callStack)
- var offset int
- if depth > 0 {
- offset = l - depth + 1
- if offset < 0 {
- offset = 0
- }
- }
- if stack == nil {
- stack = make([]StackFrame, 0, l-offset+1)
- }
- return r.vm.captureStack(stack, offset)
- }
- // Interrupt a running JavaScript. The corresponding Go call will return an *InterruptedError containing v.
- // If the interrupt propagates until the stack is empty the currently queued promise resolve/reject jobs will be cleared
- // without being executed. This is the same time they would be executed otherwise.
- // Note, it only works while in JavaScript code, it does not interrupt native Go functions (which includes all built-ins).
- // If the runtime is currently not running, it will be immediately interrupted on the next Run*() call.
- // To avoid that use ClearInterrupt()
- func (r *Runtime) Interrupt(v interface{}) {
- r.vm.Interrupt(v)
- }
- // ClearInterrupt resets the interrupt flag. Typically this needs to be called before the runtime
- // is made available for re-use if there is a chance it could have been interrupted with Interrupt().
- // Otherwise if Interrupt() was called when runtime was not running (e.g. if it had already finished)
- // so that Interrupt() didn't actually trigger, an attempt to use the runtime will immediately cause
- // an interruption. It is up to the user to ensure proper synchronisation so that ClearInterrupt() is
- // only called when the runtime has finished and there is no chance of a concurrent Interrupt() call.
- func (r *Runtime) ClearInterrupt() {
- r.vm.ClearInterrupt()
- }
- /*
- ToValue converts a Go value into a JavaScript value of a most appropriate type. Structural types (such as structs, maps
- and slices) are wrapped so that changes are reflected on the original value which can be retrieved using Value.Export().
- WARNING! These wrapped Go values do not behave in the same way as native ECMAScript values. If you plan to modify
- them in ECMAScript, bear in mind the following caveats:
- 1. If a regular JavaScript Object is assigned as an element of a wrapped Go struct, map or array, it is
- Export()'ed and therefore copied. This may result in an unexpected behaviour in JavaScript:
- m := map[string]interface{}{}
- vm.Set("m", m)
- vm.RunString(`
- var obj = {test: false};
- m.obj = obj; // obj gets Export()'ed, i.e. copied to a new map[string]interface{} and then this map is set as m["obj"]
- obj.test = true; // note, m.obj.test is still false
- `)
- fmt.Println(m["obj"].(map[string]interface{})["test"]) // prints "false"
- 2. Be careful with nested non-pointer compound types (structs, slices and arrays) if you modify them in
- ECMAScript. Better avoid it at all if possible. One of the fundamental differences between ECMAScript and Go is in
- the former all Objects are references whereas in Go you can have a literal struct or array. Consider the following
- example:
- type S struct {
- Field int
- }
- a := []S{{1}, {2}} // slice of literal structs
- vm.Set("a", &a)
- vm.RunString(`
- let tmp = {Field: 1};
- a[0] = tmp;
- a[1] = tmp;
- tmp.Field = 2;
- `)
- In ECMAScript one would expect a[0].Field and a[1].Field to be equal to 2, but this is really not possible
- (or at least non-trivial without some complex reference tracking).
- To cover the most common use cases and to avoid excessive memory allocation, the following 'copy-on-change' mechanism
- is implemented (for both arrays and structs):
- * When a nested compound value is accessed, the returned ES value becomes a reference to the literal value.
- This ensures that things like 'a[0].Field = 1' work as expected and simple access to 'a[0].Field' does not result
- in copying of a[0].
- * The original container ('a' in our case) keeps track of the returned reference value and if a[0] is reassigned
- (e.g. by direct assignment, deletion or shrinking the array) the old a[0] is copied and the earlier returned value
- becomes a reference to the copy:
- let tmp = a[0]; // no copy, tmp is a reference to a[0]
- tmp.Field = 1; // a[0].Field === 1 after this
- a[0] = {Field: 2}; // tmp is now a reference to a copy of the old value (with Field === 1)
- a[0].Field === 2 && tmp.Field === 1; // true
- * Array value swaps caused by in-place sort (using Array.prototype.sort()) do not count as re-assignments, instead
- the references are adjusted to point to the new indices.
- * Assignment to an inner compound value always does a copy (and sometimes type conversion):
- a[1] = tmp; // a[1] is now a copy of tmp
- tmp.Field = 3; // does not affect a[1].Field
- 3. Non-addressable structs, slices and arrays get copied. This sometimes may lead to a confusion as assigning to
- inner fields does not appear to work:
- a1 := []interface{}{S{1}, S{2}}
- vm.Set("a1", &a1)
- vm.RunString(`
- a1[0].Field === 1; // true
- a1[0].Field = 2;
- a1[0].Field === 2; // FALSE, because what it really did was copy a1[0] set its Field to 2 and immediately drop it
- `)
- An alternative would be making a1[0].Field a non-writable property which would probably be more in line with
- ECMAScript, however it would require to manually copy the value if it does need to be modified which may be
- impractical.
- Note, the same applies to slices. If a slice is passed by value (not as a pointer), resizing the slice does not reflect on the original
- value. Moreover, extending the slice may result in the underlying array being re-allocated and copied.
- For example:
- a := []interface{}{1}
- vm.Set("a", a)
- vm.RunString(`a.push(2); a[0] = 0;`)
- fmt.Println(a[0]) // prints "1"
- Notes on individual types:
- # Primitive types
- Primitive types (numbers, string, bool) are converted to the corresponding JavaScript primitives. These values
- are goroutine-safe and can be transferred between runtimes.
- # *big.Int
- A *big.Int value is converted to a BigInt value. Note, because BigInt is immutable, but *big.Int isn't, the value is
- copied. Export()'ing this value returns a *big.Int which is also a copy.
- If the pointer value is nil, the resulting BigInt is 0n.
- # Strings
- Because of the difference in internal string representation between ECMAScript (which uses UTF-16) and Go (which uses
- UTF-8) conversion from JS to Go may be lossy. In particular, code points that can be part of UTF-16 surrogate pairs
- (0xD800-0xDFFF) cannot be represented in UTF-8 unless they form a valid surrogate pair and are replaced with
- utf8.RuneError.
- The string value must be a valid UTF-8. If it is not, invalid characters are replaced with utf8.RuneError, but
- the behaviour of a subsequent Export() is unspecified (it may return the original value, or a value with replaced
- invalid characters).
- # Nil
- Nil is converted to null.
- # Functions
- func(FunctionCall) Value is treated as a native JavaScript function. This increases performance because there are no
- automatic argument and return value type conversions (which involves reflect). Attempting to use
- the function as a constructor will result in a TypeError. Note: implementations must not retain and use references
- to FunctionCall.Arguments after the function returns.
- func(FunctionCall, *Runtime) Value is treated as above, except the *Runtime is also passed as a parameter.
- func(ConstructorCall) *Object is treated as a native constructor, allowing to use it with the new
- operator:
- func MyObject(call goja.ConstructorCall) *goja.Object {
- // call.This contains the newly created object as per http://www.ecma-international.org/ecma-262/5.1/index.html#sec-13.2.2
- // call.Arguments contain arguments passed to the function
- call.This.Set("method", method)
- //...
- // If return value is a non-nil *Object, it will be used instead of call.This
- // This way it is possible to return a Go struct or a map converted
- // into goja.Value using ToValue(), however in this case
- // instanceof will not work as expected, unless you set the prototype:
- //
- // instance := &myCustomStruct{}
- // instanceValue := vm.ToValue(instance).(*Object)
- // instanceValue.SetPrototype(call.This.Prototype())
- // return instanceValue
- return nil
- }
- runtime.Set("MyObject", MyObject)
- Then it can be used in JS as follows:
- var o = new MyObject(arg);
- var o1 = MyObject(arg); // same thing
- o instanceof MyObject && o1 instanceof MyObject; // true
- When a native constructor is called directly (without the new operator) its behavior depends on
- this value: if it's an Object, it is passed through, otherwise a new one is created exactly as
- if it was called with the new operator. In either case call.NewTarget will be nil.
- func(ConstructorCall, *Runtime) *Object is treated as above, except the *Runtime is also passed as a parameter.
- Any other Go function is wrapped so that the arguments are automatically converted into the required Go types and the
- return value is converted to a JavaScript value (using this method). If conversion is not possible, a TypeError is
- thrown.
- Functions with multiple return values return an Array. If the last return value is an `error` it is not returned but
- converted into a JS exception. If the error is *Exception, it is thrown as is, otherwise it's wrapped in a GoEerror.
- Note that if there are exactly two return values and the last is an `error`, the function returns the first value as is,
- not an Array.
- # Structs
- Structs are converted to Object-like values. Fields and methods are available as properties, their values are
- results of this method (ToValue()) applied to the corresponding Go value.
- Field properties are writable and non-configurable. Method properties are non-writable and non-configurable.
- Attempt to define a new property or delete an existing property will fail (throw in strict mode) unless it's a Symbol
- property. Symbol properties only exist in the wrapper and do not affect the underlying Go value.
- Note that because a wrapper is created every time a property is accessed it may lead to unexpected results such as this:
- type Field struct{
- }
- type S struct {
- Field *Field
- }
- var s = S{
- Field: &Field{},
- }
- vm := New()
- vm.Set("s", &s)
- res, err := vm.RunString(`
- var sym = Symbol(66);
- var field1 = s.Field;
- field1[sym] = true;
- var field2 = s.Field;
- field1 === field2; // true, because the equality operation compares the wrapped values, not the wrappers
- field1[sym] === true; // true
- field2[sym] === undefined; // also true
- `)
- The same applies to values from maps and slices as well.
- # Handling of time.Time
- time.Time does not get special treatment and therefore is converted just like any other `struct` providing access to
- all its methods. This is done deliberately instead of converting it to a `Date` because these two types are not fully
- compatible: `time.Time` includes zone, whereas JS `Date` doesn't. Doing the conversion implicitly therefore would
- result in a loss of information.
- If you need to convert it to a `Date`, it can be done either in JS:
- var d = new Date(goval.UnixNano()/1e6);
- ... or in Go:
- now := time.Now()
- vm := New()
- val, err := vm.New(vm.Get("Date").ToObject(vm), vm.ToValue(now.UnixNano()/1e6))
- if err != nil {
- ...
- }
- vm.Set("d", val)
- Note that Value.Export() for a `Date` value returns time.Time in local timezone.
- # Maps
- Maps with string, integer, or float key types are converted into host objects that largely behave like a JavaScript Object.
- One noticeable difference is that the key order is not stable, as with maps in Go.
- Keys are converted to strings following the fmt.Sprintf("%v") convention.
- # Maps with methods
- If a map type has at least one method defined, the properties of the resulting Object represent methods, not map keys.
- This is because in JavaScript there is no distinction between 'object.key` and `object[key]`, unlike Go.
- If access to the map values is required, it can be achieved by defining another method or, if it's not possible, by
- defining an external getter function.
- # Slices
- Slices are converted into host objects that behave largely like JavaScript Array. It has the appropriate
- prototype and all the usual methods should work. There is, however, a caveat: converted Arrays may not contain holes
- (because Go slices cannot). This means that hasOwnProperty(n) always returns `true` if n < length. Deleting an item with
- an index < length will set it to a zero value (but the property will remain). Nil slice elements are be converted to
- `null`. Accessing an element beyond `length` returns `undefined`. Also see the warning above about passing slices as
- values (as opposed to pointers).
- # Arrays
- Arrays are converted similarly to slices, except the resulting Arrays are not resizable (and therefore the 'length'
- property is non-writable).
- Any other type is converted to a generic reflect based host object. Depending on the underlying type it behaves similar
- to a Number, String, Boolean or Object.
- Note that the underlying type is not lost, calling Export() returns the original Go value. This applies to all
- reflect based types.
- */
- func (r *Runtime) ToValue(i interface{}) Value {
- return r.toValue(i, reflect.Value{})
- }
- func (r *Runtime) toValue(i interface{}, origValue reflect.Value) Value {
- switch i := i.(type) {
- case nil:
- return _null
- case *Object:
- if i == nil || i.self == nil {
- return _null
- }
- if i.runtime != nil && i.runtime != r {
- panic(r.NewTypeError("Illegal runtime transition of an Object"))
- }
- return i
- case valueContainer:
- return i.toValue(r)
- case Value:
- return i
- case string:
- if len(i) <= 16 {
- if u := unistring.Scan(i); u != nil {
- return &importedString{s: i, u: u, scanned: true}
- }
- return asciiString(i)
- }
- return &importedString{s: i}
- case bool:
- if i {
- return valueTrue
- } else {
- return valueFalse
- }
- case func(FunctionCall) Value:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeFunc(i, name, 0)
- case func(FunctionCall, *Runtime) Value:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeFunc(func(call FunctionCall) Value {
- return i(call, r)
- }, name, 0)
- case func(ConstructorCall) *Object:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeConstructor(i, name, 0)
- case func(ConstructorCall, *Runtime) *Object:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeConstructor(func(call ConstructorCall) *Object {
- return i(call, r)
- }, name, 0)
- case int:
- return intToValue(int64(i))
- case int8:
- return intToValue(int64(i))
- case int16:
- return intToValue(int64(i))
- case int32:
- return intToValue(int64(i))
- case int64:
- return intToValue(i)
- case uint:
- if uint64(i) <= math.MaxInt64 {
- return intToValue(int64(i))
- } else {
- return floatToValue(float64(i))
- }
- case uint8:
- return intToValue(int64(i))
- case uint16:
- return intToValue(int64(i))
- case uint32:
- return intToValue(int64(i))
- case uint64:
- if i <= math.MaxInt64 {
- return intToValue(int64(i))
- }
- return floatToValue(float64(i))
- case float32:
- return floatToValue(float64(i))
- case float64:
- return floatToValue(i)
- case *big.Int:
- v := new(big.Int)
- if i != nil {
- v.Set(i)
- }
- return (*valueBigInt)(v)
- case map[string]interface{}:
- if i == nil {
- return _null
- }
- obj := &Object{runtime: r}
- m := &objectGoMapSimple{
- baseObject: baseObject{
- val: obj,
- extensible: true,
- },
- data: i,
- }
- obj.self = m
- m.init()
- return obj
- case []interface{}:
- return r.newObjectGoSlice(&i, false).val
- case *[]interface{}:
- if i == nil {
- return _null
- }
- return r.newObjectGoSlice(i, true).val
- }
- if !origValue.IsValid() {
- origValue = reflect.ValueOf(i)
- }
- value := origValue
- for value.Kind() == reflect.Ptr {
- value = value.Elem()
- }
- if !value.IsValid() {
- return _null
- }
- switch value.Kind() {
- case reflect.Map:
- if value.Type().NumMethod() == 0 {
- switch value.Type().Key().Kind() {
- case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
- reflect.Float64, reflect.Float32:
- obj := &Object{runtime: r}
- m := &objectGoMapReflect{
- objectGoReflect: objectGoReflect{
- baseObject: baseObject{
- val: obj,
- extensible: true,
- },
- origValue: origValue,
- fieldsValue: value,
- },
- }
- m.init()
- obj.self = m
- return obj
- }
- }
- case reflect.Array:
- obj := &Object{runtime: r}
- a := &objectGoArrayReflect{
- objectGoReflect: objectGoReflect{
- baseObject: baseObject{
- val: obj,
- },
- origValue: origValue,
- fieldsValue: value,
- },
- }
- a.init()
- obj.self = a
- return obj
- case reflect.Slice:
- obj := &Object{runtime: r}
- a := &objectGoSliceReflect{
- objectGoArrayReflect: objectGoArrayReflect{
- objectGoReflect: objectGoReflect{
- baseObject: baseObject{
- val: obj,
- },
- origValue: origValue,
- fieldsValue: value,
- },
- },
- }
- a.init()
- obj.self = a
- return obj
- case reflect.Func:
- return r.newWrappedFunc(value)
- }
- obj := &Object{runtime: r}
- o := &objectGoReflect{
- baseObject: baseObject{
- val: obj,
- },
- origValue: origValue,
- fieldsValue: value,
- }
- obj.self = o
- o.init()
- return obj
- }
- func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value {
- return func(call FunctionCall) Value {
- typ := value.Type()
- nargs := typ.NumIn()
- var in []reflect.Value
- if l := len(call.Arguments); l < nargs {
- // fill missing arguments with zero values
- n := nargs
- if typ.IsVariadic() {
- n--
- }
- in = make([]reflect.Value, n)
- for i := l; i < n; i++ {
- in[i] = reflect.Zero(typ.In(i))
- }
- } else {
- if l > nargs && !typ.IsVariadic() {
- l = nargs
- }
- in = make([]reflect.Value, l)
- }
- for i, a := range call.Arguments {
- var t reflect.Type
- n := i
- if n >= nargs-1 && typ.IsVariadic() {
- if n > nargs-1 {
- n = nargs - 1
- }
- t = typ.In(n).Elem()
- } else if n > nargs-1 { // ignore extra arguments
- break
- } else {
- t = typ.In(n)
- }
- v := reflect.New(t).Elem()
- err := r.toReflectValue(a, v, &objectExportCtx{})
- if err != nil {
- panic(r.NewTypeError("could not convert function call parameter %d: %v", i, err))
- }
- in[i] = v
- }
- out := value.Call(in)
- if len(out) == 0 {
- return _undefined
- }
- if last := out[len(out)-1]; last.Type() == reflectTypeError {
- if !last.IsNil() {
- err := last.Interface().(error)
- if _, ok := err.(*Exception); ok {
- panic(err)
- }
- if isUncatchableException(err) {
- panic(err)
- }
- panic(r.NewGoError(err))
- }
- out = out[:len(out)-1]
- }
- switch len(out) {
- case 0:
- return _undefined
- case 1:
- return r.ToValue(out[0].Interface())
- default:
- s := make([]interface{}, len(out))
- for i, v := range out {
- s[i] = v.Interface()
- }
- return r.ToValue(s)
- }
- }
- }
- func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCtx) error {
- typ := dst.Type()
- if typ == typeValue {
- dst.Set(reflect.ValueOf(v))
- return nil
- }
- if typ == typeObject {
- if obj, ok := v.(*Object); ok {
- dst.Set(reflect.ValueOf(obj))
- return nil
- }
- }
- if typ == typeCallable {
- if fn, ok := AssertFunction(v); ok {
- dst.Set(reflect.ValueOf(fn))
- return nil
- }
- }
- et := v.ExportType()
- if et == nil || et == reflectTypeNil {
- dst.Set(reflect.Zero(typ))
- return nil
- }
- kind := typ.Kind()
- for i := 0; ; i++ {
- if et.AssignableTo(typ) {
- ev := reflect.ValueOf(exportValue(v, ctx))
- for ; i > 0; i-- {
- ev = ev.Elem()
- }
- dst.Set(ev)
- return nil
- }
- expKind := et.Kind()
- if expKind == kind && et.ConvertibleTo(typ) || expKind == reflect.String && typ == typeBytes {
- ev := reflect.ValueOf(exportValue(v, ctx))
- for ; i > 0; i-- {
- ev = ev.Elem()
- }
- dst.Set(ev.Convert(typ))
- return nil
- }
- if expKind == reflect.Ptr {
- et = et.Elem()
- } else {
- break
- }
- }
- if typ == typeTime {
- if obj, ok := v.(*Object); ok {
- if d, ok := obj.self.(*dateObject); ok {
- dst.Set(reflect.ValueOf(d.time()))
- return nil
- }
- }
- if et.Kind() == reflect.String {
- tme, ok := dateParse(v.String())
- if !ok {
- return fmt.Errorf("could not convert string %v to %v", v, typ)
- }
- dst.Set(reflect.ValueOf(tme))
- return nil
- }
- }
- switch kind {
- case reflect.String:
- dst.Set(reflect.ValueOf(v.String()).Convert(typ))
- return nil
- case reflect.Bool:
- dst.Set(reflect.ValueOf(v.ToBoolean()).Convert(typ))
- return nil
- case reflect.Int:
- dst.Set(reflect.ValueOf(toInt(v)).Convert(typ))
- return nil
- case reflect.Int64:
- dst.Set(reflect.ValueOf(toInt64(v)).Convert(typ))
- return nil
- case reflect.Int32:
- dst.Set(reflect.ValueOf(toInt32(v)).Convert(typ))
- return nil
- case reflect.Int16:
- dst.Set(reflect.ValueOf(toInt16(v)).Convert(typ))
- return nil
- case reflect.Int8:
- dst.Set(reflect.ValueOf(toInt8(v)).Convert(typ))
- return nil
- case reflect.Uint:
- dst.Set(reflect.ValueOf(toUint(v)).Convert(typ))
- return nil
- case reflect.Uint64:
- dst.Set(reflect.ValueOf(toUint64(v)).Convert(typ))
- return nil
- case reflect.Uint32:
- dst.Set(reflect.ValueOf(toUint32(v)).Convert(typ))
- return nil
- case reflect.Uint16:
- dst.Set(reflect.ValueOf(toUint16(v)).Convert(typ))
- return nil
- case reflect.Uint8:
- dst.Set(reflect.ValueOf(toUint8(v)).Convert(typ))
- return nil
- case reflect.Float64:
- dst.Set(reflect.ValueOf(v.ToFloat()).Convert(typ))
- return nil
- case reflect.Float32:
- dst.Set(reflect.ValueOf(toFloat32(v)).Convert(typ))
- return nil
- case reflect.Slice, reflect.Array:
- if o, ok := v.(*Object); ok {
- if v, exists := ctx.getTyped(o, typ); exists {
- dst.Set(reflect.ValueOf(v))
- return nil
- }
- return o.self.exportToArrayOrSlice(dst, typ, ctx)
- }
- case reflect.Map:
- if o, ok := v.(*Object); ok {
- if v, exists := ctx.getTyped(o, typ); exists {
- dst.Set(reflect.ValueOf(v))
- return nil
- }
- return o.self.exportToMap(dst, typ, ctx)
- }
- case reflect.Struct:
- if o, ok := v.(*Object); ok {
- t := reflect.PtrTo(typ)
- if v, exists := ctx.getTyped(o, t); exists {
- dst.Set(reflect.ValueOf(v).Elem())
- return nil
- }
- s := dst
- ctx.putTyped(o, t, s.Addr().Interface())
- for i := 0; i < typ.NumField(); i++ {
- field := typ.Field(i)
- if ast.IsExported(field.Name) {
- name := field.Name
- if r.fieldNameMapper != nil {
- name = r.fieldNameMapper.FieldName(typ, field)
- }
- var v Value
- if field.Anonymous {
- v = o
- } else {
- v = o.self.getStr(unistring.NewFromString(name), nil)
- }
- if v != nil {
- err := r.toReflectValue(v, s.Field(i), ctx)
- if err != nil {
- return fmt.Errorf("could not convert struct value %v to %v for field %s: %w", v, field.Type, field.Name, err)
- }
- }
- }
- }
- return nil
- }
- case reflect.Func:
- if fn, ok := AssertFunction(v); ok {
- dst.Set(reflect.MakeFunc(typ, r.wrapJSFunc(fn, typ)))
- return nil
- }
- case reflect.Ptr:
- if o, ok := v.(*Object); ok {
- if v, exists := ctx.getTyped(o, typ); exists {
- dst.Set(reflect.ValueOf(v))
- return nil
- }
- }
- if dst.IsNil() {
- dst.Set(reflect.New(typ.Elem()))
- }
- return r.toReflectValue(v, dst.Elem(), ctx)
- }
- return fmt.Errorf("could not convert %v to %v", v, typ)
- }
- func (r *Runtime) wrapJSFunc(fn Callable, typ reflect.Type) func(args []reflect.Value) (results []reflect.Value) {
- return func(args []reflect.Value) (results []reflect.Value) {
- var jsArgs []Value
- if len(args) > 0 {
- if typ.IsVariadic() {
- varArg := args[len(args)-1]
- args = args[:len(args)-1]
- jsArgs = make([]Value, 0, len(args)+varArg.Len())
- for _, arg := range args {
- jsArgs = append(jsArgs, r.ToValue(arg.Interface()))
- }
- for i := 0; i < varArg.Len(); i++ {
- jsArgs = append(jsArgs, r.ToValue(varArg.Index(i).Interface()))
- }
- } else {
- jsArgs = make([]Value, len(args))
- for i, arg := range args {
- jsArgs[i] = r.ToValue(arg.Interface())
- }
- }
- }
- numOut := typ.NumOut()
- results = make([]reflect.Value, numOut)
- res, err := fn(_undefined, jsArgs...)
- if err == nil {
- if numOut > 0 {
- v := reflect.New(typ.Out(0)).Elem()
- err = r.toReflectValue(res, v, &objectExportCtx{})
- if err == nil {
- results[0] = v
- }
- }
- }
- if err != nil {
- if numOut > 0 && typ.Out(numOut-1) == reflectTypeError {
- if ex, ok := err.(*Exception); ok {
- if exo, ok := ex.val.(*Object); ok {
- if v := exo.self.getStr("value", nil); v != nil {
- if v.ExportType().AssignableTo(reflectTypeError) {
- err = v.Export().(error)
- }
- }
- }
- }
- results[numOut-1] = reflect.ValueOf(err).Convert(typ.Out(numOut - 1))
- } else {
- panic(err)
- }
- }
- for i, v := range results {
- if !v.IsValid() {
- results[i] = reflect.Zero(typ.Out(i))
- }
- }
- return
- }
- }
- // ExportTo converts a JavaScript value into the specified Go value. The second parameter must be a non-nil pointer.
- // Returns error if conversion is not possible.
- //
- // Notes on specific cases:
- //
- // # Empty interface
- //
- // Exporting to an interface{} results in a value of the same type as Value.Export() would produce.
- //
- // # Numeric types
- //
- // Exporting to numeric types uses the standard ECMAScript conversion operations, same as used when assigning
- // values to non-clamped typed array items, e.g. https://262.ecma-international.org/#sec-toint32.
- //
- // # Functions
- //
- // Exporting to a 'func' creates a strictly typed 'gateway' into an ES function which can be called from Go.
- // The arguments are converted into ES values using Runtime.ToValue(). If the func has no return values,
- // the return value is ignored. If the func has exactly one return value, it is converted to the appropriate
- // type using ExportTo(). If the last return value is 'error', exceptions are caught and returned as *Exception
- // (instances of GoError are unwrapped, i.e. their 'value' is returned instead). In all other cases exceptions
- // result in a panic. Any extra return values are zeroed.
- //
- // 'this' value will always be set to 'undefined'.
- //
- // For a more low-level mechanism see AssertFunction().
- //
- // # Map types
- //
- // An ES Map can be exported into a Go map type. If any exported key value is non-hashable, the operation panics
- // (as reflect.Value.SetMapIndex() would). Symbol.iterator is ignored.
- //
- // Exporting an ES Set into a map type results in the map being populated with (element) -> (zero value) key/value
- // pairs. If any value is non-hashable, the operation panics (as reflect.Value.SetMapIndex() would).
- // Symbol.iterator is ignored.
- //
- // Any other Object populates the map with own enumerable non-symbol properties.
- //
- // # Slice types
- //
- // Exporting an ES Set into a slice type results in its elements being exported.
- //
- // Exporting any Object that implements the iterable protocol (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol)
- // into a slice type results in the slice being populated with the results of the iteration.
- //
- // Array is treated as iterable (i.e. overwriting Symbol.iterator affects the result).
- //
- // If an object has a 'length' property and is not a function it is treated as array-like. The resulting slice
- // will contain obj[0], ... obj[length-1].
- //
- // ArrayBuffer and ArrayBuffer-backed types (i.e. typed arrays and DataView) can be exported into []byte. The result
- // is backed by the original data, no copy is performed.
- //
- // For any other Object an error is returned.
- //
- // # Array types
- //
- // Anything that can be exported to a slice type can also be exported to an array type, as long as the lengths
- // match. If they do not, an error is returned.
- //
- // # Proxy
- //
- // Proxy objects are treated the same way as if they were accessed from ES code in regard to their properties
- // (such as 'length' or [Symbol.iterator]). This means exporting them to slice types works, however
- // exporting a proxied Map into a map type does not produce its contents, because the Proxy is not recognised
- // as a Map. Same applies to a proxied Set.
- func (r *Runtime) ExportTo(v Value, target interface{}) error {
- tval := reflect.ValueOf(target)
- if tval.Kind() != reflect.Ptr || tval.IsNil() {
- return errors.New("target must be a non-nil pointer")
- }
- return r.toReflectValue(v, tval.Elem(), &objectExportCtx{})
- }
- // GlobalObject returns the global object.
- func (r *Runtime) GlobalObject() *Object {
- return r.globalObject
- }
- // SetGlobalObject sets the global object to the given object.
- // Note, any existing references to the previous global object will continue to reference that object.
- func (r *Runtime) SetGlobalObject(object *Object) {
- r.globalObject = object
- }
- // Set the specified variable in the global context.
- // Equivalent to running "name = value" in non-strict mode.
- // The value is first converted using ToValue().
- // Note, this is not the same as GlobalObject().Set(name, value),
- // because if a global lexical binding (let or const) exists, it is set instead.
- func (r *Runtime) Set(name string, value interface{}) error {
- return r.try(func() {
- name := unistring.NewFromString(name)
- v := r.ToValue(value)
- if ref := r.global.stash.getRefByName(name, false); ref != nil {
- ref.set(v)
- } else {
- r.globalObject.self.setOwnStr(name, v, true)
- }
- })
- }
- // Get the specified variable in the global context.
- // Equivalent to dereferencing a variable by name in non-strict mode. If variable is not defined returns nil.
- // Note, this is not the same as GlobalObject().Get(name),
- // because if a global lexical binding (let or const) exists, it is used instead.
- // This method will panic with an *Exception if a JavaScript exception is thrown in the process. Use Runtime.Try to catch these.
- func (r *Runtime) Get(name string) Value {
- n := unistring.NewFromString(name)
- if v, exists := r.global.stash.getByName(n); exists {
- return v
- } else {
- return r.globalObject.self.getStr(n, nil)
- }
- }
- // SetRandSource sets random source for this Runtime. If not called, the default math/rand is used.
- func (r *Runtime) SetRandSource(source RandSource) {
- r.rand = source
- }
- // SetTimeSource sets the current time source for this Runtime.
- // If not called, the default time.Now() is used.
- func (r *Runtime) SetTimeSource(now Now) {
- r.now = now
- }
- // SetParserOptions sets parser options to be used by RunString, RunScript and eval() within the code.
- func (r *Runtime) SetParserOptions(opts ...parser.Option) {
- r.parserOptions = opts
- }
- // SetMaxCallStackSize sets the maximum function call depth. When exceeded, a *StackOverflowError is thrown and
- // returned by RunProgram or by a Callable call. This is useful to prevent memory exhaustion caused by an
- // infinite recursion. The default value is math.MaxInt32.
- // This method (as the rest of the Set* methods) is not safe for concurrent use and may only be called
- // from the vm goroutine or when the vm is not running.
- func (r *Runtime) SetMaxCallStackSize(size int) {
- r.vm.maxCallStackSize = size
- }
- // New is an equivalent of the 'new' operator allowing to call it directly from Go.
- func (r *Runtime) New(construct Value, args ...Value) (o *Object, err error) {
- err = r.try(func() {
- o = r.builtin_new(r.toObject(construct), args)
- })
- return
- }
- // Callable represents a JavaScript function that can be called from Go.
- type Callable func(this Value, args ...Value) (Value, error)
- // AssertFunction checks if the Value is a function and returns a Callable.
- // Note, for classes this returns a callable and a 'true', however calling it will always result in a TypeError.
- // For classes use AssertConstructor().
- func AssertFunction(v Value) (Callable, bool) {
- if obj, ok := v.(*Object); ok {
- if f, ok := obj.self.assertCallable(); ok {
- return func(this Value, args ...Value) (ret Value, err error) {
- err = obj.runtime.runWrapped(func() {
- ret = f(FunctionCall{
- This: this,
- Arguments: args,
- })
- })
- return
- }, true
- }
- }
- return nil, false
- }
- // Constructor is a type that can be used to call constructors. The first argument (newTarget) can be nil
- // which sets it to the constructor function itself.
- type Constructor func(newTarget *Object, args ...Value) (*Object, error)
- // AssertConstructor checks if the Value is a constructor and returns a Constructor.
- func AssertConstructor(v Value) (Constructor, bool) {
- if obj, ok := v.(*Object); ok {
- if ctor := obj.self.assertConstructor(); ctor != nil {
- return func(newTarget *Object, args ...Value) (ret *Object, err error) {
- err = obj.runtime.runWrapped(func() {
- ret = ctor(args, newTarget)
- })
- return
- }, true
- }
- }
- return nil, false
- }
- func (r *Runtime) runWrapped(f func()) (err error) {
- defer func() {
- if x := recover(); x != nil {
- if ex := asUncatchableException(x); ex != nil {
- err = ex
- if len(r.vm.callStack) == 0 {
- r.leaveAbrupt()
- }
- } else {
- panic(x)
- }
- }
- }()
- ex := r.vm.try(f)
- if ex != nil {
- err = ex
- }
- if len(r.vm.callStack) == 0 {
- r.leave()
- } else {
- r.vm.clearStack()
- }
- return
- }
- // IsUndefined returns true if the supplied Value is undefined. Note, it checks against the real undefined, not
- // against the global object's 'undefined' property.
- func IsUndefined(v Value) bool {
- return v == _undefined
- }
- // IsNull returns true if the supplied Value is null.
- func IsNull(v Value) bool {
- return v == _null
- }
- // IsNaN returns true if the supplied value is NaN.
- func IsNaN(v Value) bool {
- f, ok := v.(valueFloat)
- return ok && math.IsNaN(float64(f))
- }
- // IsInfinity returns true if the supplied is (+/-)Infinity
- func IsInfinity(v Value) bool {
- return v == _positiveInf || v == _negativeInf
- }
- func IsNumber(v Value) bool {
- switch v.(type) {
- case valueInt, valueFloat:
- return true
- default:
- return false
- }
- }
- func IsBigInt(v Value) bool {
- _, ok := v.(*valueBigInt)
- return ok
- }
- func IsString(v Value) bool {
- _, ok := v.(String)
- return ok
- }
- // Undefined returns JS undefined value. Note if global 'undefined' property is changed this still returns the original value.
- func Undefined() Value {
- return _undefined
- }
- // Null returns JS null value.
- func Null() Value {
- return _null
- }
- // NaN returns a JS NaN value.
- func NaN() Value {
- return _NaN
- }
- // PositiveInf returns a JS +Inf value.
- func PositiveInf() Value {
- return _positiveInf
- }
- // NegativeInf returns a JS -Inf value.
- func NegativeInf() Value {
- return _negativeInf
- }
- func tryFunc(f func()) (ret interface{}) {
- defer func() {
- ret = recover()
- }()
- f()
- return
- }
- // Try runs a given function catching and returning any JS exception. Use this method to run any code
- // that may throw exceptions (such as Object.Get, Object.String, Object.ToInteger, Object.Export, Runtime.Get, Runtime.InstanceOf, etc.)
- // outside the Runtime execution context (i.e. when calling directly from Go, not from a JS function implemented in Go).
- func (r *Runtime) Try(f func()) *Exception {
- return r.vm.try(f)
- }
- func (r *Runtime) try(f func()) error {
- if ex := r.vm.try(f); ex != nil {
- return ex
- }
- return nil
- }
- func (r *Runtime) toObject(v Value, args ...interface{}) *Object {
- if obj, ok := v.(*Object); ok {
- return obj
- }
- if len(args) > 0 {
- panic(r.NewTypeError(args...))
- } else {
- var s string
- if v == nil {
- s = "undefined"
- } else {
- s = v.String()
- }
- panic(r.NewTypeError("Value is not an object: %s", s))
- }
- }
- func (r *Runtime) speciesConstructor(o, defaultConstructor *Object) func(args []Value, newTarget *Object) *Object {
- c := o.self.getStr("constructor", nil)
- if c != nil && c != _undefined {
- c = r.toObject(c).self.getSym(SymSpecies, nil)
- }
- if c == nil || c == _undefined || c == _null {
- c = defaultConstructor
- }
- return r.toConstructor(c)
- }
- func (r *Runtime) speciesConstructorObj(o, defaultConstructor *Object) *Object {
- c := o.self.getStr("constructor", nil)
- if c != nil && c != _undefined {
- c = r.toObject(c).self.getSym(SymSpecies, nil)
- }
- if c == nil || c == _undefined || c == _null {
- return defaultConstructor
- }
- obj := r.toObject(c)
- if obj.self.assertConstructor() == nil {
- panic(r.NewTypeError("Value is not a constructor"))
- }
- return obj
- }
- func (r *Runtime) returnThis(call FunctionCall) Value {
- return call.This
- }
- func createDataProperty(o *Object, p Value, v Value) {
- o.defineOwnProperty(p, PropertyDescriptor{
- Writable: FLAG_TRUE,
- Enumerable: FLAG_TRUE,
- Configurable: FLAG_TRUE,
- Value: v,
- }, false)
- }
- func createDataPropertyOrThrow(o *Object, p Value, v Value) {
- o.defineOwnProperty(p, PropertyDescriptor{
- Writable: FLAG_TRUE,
- Enumerable: FLAG_TRUE,
- Configurable: FLAG_TRUE,
- Value: v,
- }, true)
- }
- func toPropertyKey(key Value) Value {
- return key.ToString()
- }
- func (r *Runtime) getVStr(v Value, p unistring.String) Value {
- o := v.ToObject(r)
- return o.self.getStr(p, v)
- }
- func (r *Runtime) getV(v Value, p Value) Value {
- o := v.ToObject(r)
- return o.get(p, v)
- }
- type iteratorRecord struct {
- iterator *Object
- next func(FunctionCall) Value
- }
- func (r *Runtime) getIterator(obj Value, method func(FunctionCall) Value) *iteratorRecord {
- if method == nil {
- method = toMethod(r.getV(obj, SymIterator))
- if method == nil {
- panic(r.NewTypeError("object is not iterable"))
- }
- }
- iter := r.toObject(method(FunctionCall{
- This: obj,
- }))
- var next func(FunctionCall) Value
- if obj, ok := iter.self.getStr("next", nil).(*Object); ok {
- if call, ok := obj.self.assertCallable(); ok {
- next = call
- }
- }
- return &iteratorRecord{
- iterator: iter,
- next: next,
- }
- }
- func iteratorComplete(iterResult *Object) bool {
- return nilSafe(iterResult.self.getStr("done", nil)).ToBoolean()
- }
- func iteratorValue(iterResult *Object) Value {
- return nilSafe(iterResult.self.getStr("value", nil))
- }
- func (ir *iteratorRecord) iterate(step func(Value)) {
- r := ir.iterator.runtime
- for {
- if ir.next == nil {
- panic(r.NewTypeError("iterator.next is missing or not a function"))
- }
- res := r.toObject(ir.next(FunctionCall{This: ir.iterator}))
- if iteratorComplete(res) {
- break
- }
- value := iteratorValue(res)
- ret := tryFunc(func() {
- step(value)
- })
- if ret != nil {
- _ = tryFunc(func() {
- ir.returnIter()
- })
- panic(ret)
- }
- }
- }
- func (ir *iteratorRecord) step() (value Value, ex *Exception) {
- r := ir.iterator.runtime
- ex = r.vm.try(func() {
- res := r.toObject(ir.next(FunctionCall{This: ir.iterator}))
- done := iteratorComplete(res)
- if !done {
- value = iteratorValue(res)
- } else {
- ir.close()
- }
- })
- return
- }
- func (ir *iteratorRecord) returnIter() {
- if ir.iterator == nil {
- return
- }
- retMethod := toMethod(ir.iterator.self.getStr("return", nil))
- if retMethod != nil {
- ir.iterator.runtime.toObject(retMethod(FunctionCall{This: ir.iterator}))
- }
- ir.iterator = nil
- ir.next = nil
- }
- func (ir *iteratorRecord) close() {
- ir.iterator = nil
- ir.next = nil
- }
- // ForOf is a Go equivalent of for-of loop. The function panics if an exception is thrown at any point
- // while iterating, including if the supplied value is not iterable
- // (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol).
- // When using outside of Runtime.Run (i.e. when calling directly from Go code, not from a JS function implemented
- // in Go) it must be enclosed in Try. See the example.
- func (r *Runtime) ForOf(iterable Value, step func(curValue Value) (continueIteration bool)) {
- iter := r.getIterator(iterable, nil)
- for {
- value, ex := iter.step()
- if ex != nil {
- panic(ex)
- }
- if value != nil {
- var continueIteration bool
- ex := r.vm.try(func() {
- continueIteration = step(value)
- })
- if ex != nil {
- iter.returnIter()
- panic(ex)
- }
- if !continueIteration {
- iter.returnIter()
- break
- }
- } else {
- break
- }
- }
- }
- func (r *Runtime) createIterResultObject(value Value, done bool) Value {
- o := r.NewObject()
- o.self.setOwnStr("value", value, false)
- o.self.setOwnStr("done", r.toBoolean(done), false)
- return o
- }
- func (r *Runtime) getHash() *maphash.Hash {
- if r.hash == nil {
- r.hash = &maphash.Hash{}
- }
- return r.hash
- }
- // called when the top level function returns normally (i.e. control is passed outside the Runtime).
- func (r *Runtime) leave() {
- var jobs []func()
- for len(r.jobQueue) > 0 {
- jobs, r.jobQueue = r.jobQueue, jobs[:0]
- for _, job := range jobs {
- job()
- }
- }
- r.jobQueue = nil
- r.vm.stack = nil
- }
- // called when the top level function returns (i.e. control is passed outside the Runtime) but it was due to an interrupt
- func (r *Runtime) leaveAbrupt() {
- r.jobQueue = nil
- r.ClearInterrupt()
- }
- func nilSafe(v Value) Value {
- if v != nil {
- return v
- }
- return _undefined
- }
- func isArray(object *Object) bool {
- self := object.self
- if proxy, ok := self.(*proxyObject); ok {
- if proxy.target == nil {
- panic(typeError("Cannot perform 'IsArray' on a proxy that has been revoked"))
- }
- return isArray(proxy.target)
- }
- switch self.className() {
- case classArray:
- return true
- default:
- return false
- }
- }
- func isRegexp(v Value) bool {
- if o, ok := v.(*Object); ok {
- matcher := o.self.getSym(SymMatch, nil)
- if matcher != nil && matcher != _undefined {
- return matcher.ToBoolean()
- }
- _, reg := o.self.(*regexpObject)
- return reg
- }
- return false
- }
- func limitCallArgs(call FunctionCall, n int) FunctionCall {
- if len(call.Arguments) > n {
- return FunctionCall{This: call.This, Arguments: call.Arguments[:n]}
- } else {
- return call
- }
- }
- func shrinkCap(newSize, oldCap int) int {
- if oldCap > 8 {
- if cap := oldCap / 2; cap >= newSize {
- return cap
- }
- }
- return oldCap
- }
- func growCap(newSize, oldSize, oldCap int) int {
- // Use the same algorithm as in runtime.growSlice
- doublecap := oldCap + oldCap
- if newSize > doublecap {
- return newSize
- } else {
- if oldSize < 1024 {
- return doublecap
- } else {
- cap := oldCap
- // Check 0 < cap to detect overflow
- // and prevent an infinite loop.
- for 0 < cap && cap < newSize {
- cap += cap / 4
- }
- // Return the requested cap when
- // the calculation overflowed.
- if cap <= 0 {
- return newSize
- }
- return cap
- }
- }
- }
- func (r *Runtime) genId() (ret uint64) {
- if r.hash == nil {
- h := r.getHash()
- r.idSeq = h.Sum64()
- }
- if r.idSeq == 0 {
- r.idSeq = 1
- }
- ret = r.idSeq
- r.idSeq++
- return
- }
- func (r *Runtime) setGlobal(name unistring.String, v Value, strict bool) {
- if ref := r.global.stash.getRefByName(name, strict); ref != nil {
- ref.set(v)
- } else {
- o := r.globalObject.self
- if strict {
- if o.hasOwnPropertyStr(name) {
- o.setOwnStr(name, v, true)
- } else {
- r.throwReferenceError(name)
- }
- } else {
- o.setOwnStr(name, v, false)
- }
- }
- }
- func (r *Runtime) trackPromiseRejection(p *Promise, operation PromiseRejectionOperation) {
- if r.promiseRejectionTracker != nil {
- r.promiseRejectionTracker(p, operation)
- }
- }
- func (r *Runtime) callJobCallback(job *jobCallback, this Value, args ...Value) Value {
- return job.callback(FunctionCall{This: this, Arguments: args})
- }
- func (r *Runtime) invoke(v Value, p unistring.String, args ...Value) Value {
- o := v.ToObject(r)
- return r.toCallable(o.self.getStr(p, nil))(FunctionCall{This: v, Arguments: args})
- }
- func (r *Runtime) iterableToList(iterable Value, method func(FunctionCall) Value) []Value {
- iter := r.getIterator(iterable, method)
- var values []Value
- iter.iterate(func(item Value) {
- values = append(values, item)
- })
- return values
- }
- func (r *Runtime) putSpeciesReturnThis(o objectImpl) {
- o._putSym(SymSpecies, &valueProperty{
- getterFunc: r.newNativeFunc(r.returnThis, "get [Symbol.species]", 0),
- accessor: true,
- configurable: true,
- })
- }
- func strToArrayIdx(s unistring.String) uint32 {
- if s == "" {
- return math.MaxUint32
- }
- l := len(s)
- if s[0] == '0' {
- if l == 1 {
- return 0
- }
- return math.MaxUint32
- }
- var n uint32
- if l < 10 {
- // guaranteed not to overflow
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return math.MaxUint32
- }
- n = n*10 + uint32(c-'0')
- }
- return n
- }
- if l > 10 {
- // guaranteed to overflow
- return math.MaxUint32
- }
- c9 := s[9]
- if c9 < '0' || c9 > '9' {
- return math.MaxUint32
- }
- for i := 0; i < 9; i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return math.MaxUint32
- }
- n = n*10 + uint32(c-'0')
- }
- if n >= math.MaxUint32/10+1 {
- return math.MaxUint32
- }
- n *= 10
- n1 := n + uint32(c9-'0')
- if n1 < n {
- return math.MaxUint32
- }
- return n1
- }
- func strToInt32(s unistring.String) (int32, bool) {
- if s == "" {
- return -1, false
- }
- neg := s[0] == '-'
- if neg {
- s = s[1:]
- }
- l := len(s)
- if s[0] == '0' {
- if l == 1 {
- return 0, !neg
- }
- return -1, false
- }
- var n uint32
- if l < 10 {
- // guaranteed not to overflow
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1, false
- }
- n = n*10 + uint32(c-'0')
- }
- } else if l > 10 {
- // guaranteed to overflow
- return -1, false
- } else {
- c9 := s[9]
- if c9 >= '0' {
- if !neg && c9 > '7' || c9 > '8' {
- // guaranteed to overflow
- return -1, false
- }
- for i := 0; i < 9; i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1, false
- }
- n = n*10 + uint32(c-'0')
- }
- if n >= math.MaxInt32/10+1 {
- // valid number, but it overflows integer
- return 0, false
- }
- n = n*10 + uint32(c9-'0')
- } else {
- return -1, false
- }
- }
- if neg {
- return int32(-n), true
- }
- return int32(n), true
- }
- func strToInt64(s unistring.String) (int64, bool) {
- if s == "" {
- return -1, false
- }
- neg := s[0] == '-'
- if neg {
- s = s[1:]
- }
- l := len(s)
- if s[0] == '0' {
- if l == 1 {
- return 0, !neg
- }
- return -1, false
- }
- var n uint64
- if l < 19 {
- // guaranteed not to overflow
- for i := 0; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1, false
- }
- n = n*10 + uint64(c-'0')
- }
- } else if l > 19 {
- // guaranteed to overflow
- return -1, false
- } else {
- c18 := s[18]
- if c18 >= '0' {
- if !neg && c18 > '7' || c18 > '8' {
- // guaranteed to overflow
- return -1, false
- }
- for i := 0; i < 18; i++ {
- c := s[i]
- if c < '0' || c > '9' {
- return -1, false
- }
- n = n*10 + uint64(c-'0')
- }
- if n >= math.MaxInt64/10+1 {
- // valid number, but it overflows integer
- return 0, false
- }
- n = n*10 + uint64(c18-'0')
- } else {
- return -1, false
- }
- }
- if neg {
- return int64(-n), true
- }
- return int64(n), true
- }
- func strToInt(s unistring.String) (int, bool) {
- if bits.UintSize == 32 {
- n, ok := strToInt32(s)
- return int(n), ok
- }
- n, ok := strToInt64(s)
- return int(n), ok
- }
- // Attempts to convert a string into a canonical integer.
- // On success returns (number, true).
- // If it was a canonical number, but not an integer returns (0, false). This includes -0 and overflows.
- // In all other cases returns (-1, false).
- // See https://262.ecma-international.org/#sec-canonicalnumericindexstring
- func strToIntNum(s unistring.String) (int, bool) {
- n, ok := strToInt64(s)
- if n == 0 {
- return 0, ok
- }
- if ok && n >= -maxInt && n <= maxInt {
- if bits.UintSize == 32 {
- if n > math.MaxInt32 || n < math.MinInt32 {
- return 0, false
- }
- }
- return int(n), true
- }
- str := stringValueFromRaw(s)
- if str.ToNumber().toString().SameAs(str) {
- return 0, false
- }
- return -1, false
- }
- func strToGoIdx(s unistring.String) int {
- if n, ok := strToInt(s); ok {
- return n
- }
- return -1
- }
- func strToIdx64(s unistring.String) int64 {
- if n, ok := strToInt64(s); ok {
- return n
- }
- return -1
- }
- func assertCallable(v Value) (func(FunctionCall) Value, bool) {
- if obj, ok := v.(*Object); ok {
- return obj.self.assertCallable()
- }
- return nil, false
- }
- // InstanceOf is an equivalent of "left instanceof right".
- // This method will panic with an *Exception if a JavaScript exception is thrown in the process. Use Runtime.Try to catch these.
- func (r *Runtime) InstanceOf(left Value, right *Object) (res bool) {
- return instanceOfOperator(left, right)
- }
- func (r *Runtime) methodProp(f func(FunctionCall) Value, name unistring.String, nArgs int) Value {
- return valueProp(r.newNativeFunc(f, name, nArgs), true, false, true)
- }
- func (r *Runtime) getPrototypeFromCtor(newTarget, defCtor, defProto *Object) *Object {
- if newTarget == defCtor {
- return defProto
- }
- proto := newTarget.self.getStr("prototype", nil)
- if obj, ok := proto.(*Object); ok {
- return obj
- }
- return defProto
- }
|