1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116 |
- package goja
- import (
- "bytes"
- "errors"
- "fmt"
- "go/ast"
- "hash/maphash"
- "math"
- "math/bits"
- "math/rand"
- "reflect"
- "runtime"
- "strconv"
- "time"
- "golang.org/x/text/collate"
- js_ast "github.com/dop251/goja/ast"
- "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{})
- )
- type iterationKind int
- const (
- iterationKindKey iterationKind = iota
- iterationKindValue
- iterationKindKeyValue
- )
- type global struct {
- Object *Object
- Array *Object
- Function *Object
- String *Object
- Number *Object
- Boolean *Object
- RegExp *Object
- Date *Object
- Symbol *Object
- Proxy *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
- WeakSet *Object
- WeakMap *Object
- Map *Object
- Set *Object
- Error *Object
- TypeError *Object
- ReferenceError *Object
- SyntaxError *Object
- RangeError *Object
- EvalError *Object
- URIError *Object
- GoError *Object
- ObjectPrototype *Object
- ArrayPrototype *Object
- NumberPrototype *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
- IteratorPrototype *Object
- ArrayIteratorPrototype *Object
- MapIteratorPrototype *Object
- SetIteratorPrototype *Object
- StringIteratorPrototype *Object
- ErrorPrototype *Object
- TypeErrorPrototype *Object
- SyntaxErrorPrototype *Object
- RangeErrorPrototype *Object
- ReferenceErrorPrototype *Object
- EvalErrorPrototype *Object
- URIErrorPrototype *Object
- GoErrorPrototype *Object
- Eval *Object
- thrower *Object
- throwerProperty Value
- stdRegexpProto *guardedObject
- weakSetAdder *Object
- weakMapAdder *Object
- mapAdder *Object
- setAdder *Object
- arrayValues *Object
- arrayToString *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
- symbolRegistry map[unistring.String]*valueSymbol
- typeInfoCache map[reflect.Type]*reflectTypeInfo
- fieldNameMapper FieldNameMapper
- vm *vm
- hash *maphash.Hash
- idSeq uint64
- }
- 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() Position {
- if f.prg == nil || f.prg.src == nil {
- return Position{
- 0,
- 0,
- }
- }
- return f.prg.src.Position(f.prg.sourceOffset(f.pc))
- }
- func (f *StackFrame) Write(b *bytes.Buffer) {
- if f.prg != nil {
- if n := f.prg.funcName; n != "" {
- b.WriteString(n.String())
- b.WriteString(" (")
- }
- if n := f.prg.src.name; n != "" {
- b.WriteString(n)
- } else {
- b.WriteString("<eval>")
- }
- b.WriteByte(':')
- b.WriteString(f.Position().String())
- 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(')')
- }
- }
- }
- type Exception struct {
- val Value
- stack []StackFrame
- }
- type InterruptedError struct {
- Exception
- iface interface{}
- }
- 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 || e.val == nil {
- return "<nil>"
- }
- var b bytes.Buffer
- b.WriteString(e.val.String())
- e.writeShortStack(&b)
- return b.String()
- }
- func (e *Exception) Value() Value {
- return e.val
- }
- func (r *Runtime) addToGlobal(name string, value Value) {
- r.globalObject.self._putProp(unistring.String(name), value, true, false, true)
- }
- func (r *Runtime) createIterProto(val *Object) objectImpl {
- o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject)
- o._putSym(symIterator, valueProp(r.newNativeFunc(r.returnThis, nil, "[Symbol.iterator]", nil, 0), true, false, true))
- return o
- }
- func (r *Runtime) init() {
- r.rand = rand.Float64
- r.now = time.Now
- r.global.ObjectPrototype = r.newBaseObject(nil, classObject).val
- r.globalObject = r.NewObject()
- r.vm = &vm{
- r: r,
- }
- r.vm.init()
- r.global.FunctionPrototype = r.newNativeFunc(func(FunctionCall) Value {
- return _undefined
- }, nil, " ", nil, 0)
- r.global.IteratorPrototype = r.newLazyObject(r.createIterProto)
- r.initObject()
- r.initFunction()
- r.initArray()
- r.initString()
- r.initGlobalObject()
- r.initNumber()
- r.initRegExp()
- r.initDate()
- r.initBoolean()
- r.initProxy()
- r.initReflect()
- r.initErrors()
- r.global.Eval = r.newNativeFunc(r.builtin_eval, nil, "eval", nil, 1)
- r.addToGlobal("eval", r.global.Eval)
- r.initMath()
- r.initJSON()
- r.initTypedArrays()
- r.initSymbol()
- r.initWeakSet()
- r.initWeakMap()
- r.initMap()
- r.initSet()
- r.global.thrower = r.newNativeFunc(r.builtin_thrower, nil, "thrower", nil, 0)
- r.global.throwerProperty = &valueProperty{
- getterFunc: r.global.thrower,
- setterFunc: r.global.thrower,
- accessor: true,
- }
- }
- 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 {
- msg := fmt.Sprintf(format, args...)
- return r.builtin_new(typ, []Value{newStringValue(msg)})
- }
- func (r *Runtime) throwReferenceError(name unistring.String) {
- panic(r.newError(r.global.ReferenceError, "%s is not defined", name))
- }
- func (r *Runtime) newSyntaxError(msg string, offset int) Value {
- return r.builtin_new(r.global.SyntaxError, []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) NewTypeError(args ...interface{}) *Object {
- msg := ""
- if len(args) > 0 {
- f, _ := args[0].(string)
- msg = fmt.Sprintf(f, args[1:]...)
- }
- return r.builtin_new(r.global.TypeError, []Value{newStringValue(msg)})
- }
- func (r *Runtime) NewGoError(err error) *Object {
- e := r.newError(r.global.GoError, err.Error()).(*Object)
- e.Set("value", err)
- return e
- }
- func (r *Runtime) newFunc(name unistring.String, len int, strict bool) (f *funcObject) {
- v := &Object{runtime: r}
- f = &funcObject{}
- f.class = classFunction
- f.val = v
- f.extensible = true
- v.self = f
- f.prototype = r.global.FunctionPrototype
- f.init(name, len)
- if strict {
- f._put("caller", r.global.throwerProperty)
- f._put("arguments", r.global.throwerProperty)
- }
- return
- }
- func (r *Runtime) newNativeFuncObj(v *Object, call func(FunctionCall) Value, 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.global.FunctionPrototype,
- },
- },
- f: call,
- construct: r.wrapNativeConstruct(construct, proto),
- }
- v.self = f
- f.init(name, length)
- if proto != nil {
- f._putProp("prototype", proto, false, false, false)
- }
- return f
- }
- func (r *Runtime) newNativeConstructor(call func(ConstructorCall) *Object, name unistring.String, length int) *Object {
- v := &Object{runtime: r}
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.global.FunctionPrototype,
- },
- },
- }
- f.f = func(c FunctionCall) Value {
- return f.defaultConstruct(call, c.Arguments)
- }
- f.construct = func(args []Value, proto *Object) *Object {
- return f.defaultConstruct(call, args)
- }
- v.self = f
- f.init(name, 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 int) *nativeFuncObject {
- if v == nil {
- v = &Object{runtime: r}
- }
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.global.FunctionPrototype,
- },
- },
- f: func(call FunctionCall) Value {
- return ctor(call.Arguments, nil)
- },
- construct: func(args []Value, newTarget *Object) *Object {
- if newTarget == nil {
- newTarget = v
- }
- return ctor(args, newTarget)
- },
- }
- v.self = f
- f.init(name, length)
- if defaultProto != nil {
- f._putProp("prototype", defaultProto, false, false, false)
- }
- return f
- }
- func (r *Runtime) newNativeFunc(call func(FunctionCall) Value, construct func(args []Value, proto *Object) *Object, name unistring.String, proto *Object, length int) *Object {
- v := &Object{runtime: r}
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.global.FunctionPrototype,
- },
- },
- f: call,
- construct: r.wrapNativeConstruct(construct, proto),
- }
- v.self = f
- f.init(name, length)
- if proto != nil {
- f._putProp("prototype", proto, false, false, false)
- proto.self._putProp("constructor", v, true, false, true)
- }
- 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.global.FunctionPrototype,
- },
- },
- f: r.constructToCall(construct, proto),
- construct: r.wrapNativeConstruct(construct, proto),
- }
- f.init(name, length)
- if proto != nil {
- f._putProp("prototype", proto, false, false, false)
- }
- return f
- }
- func (r *Runtime) newNativeFuncConstruct(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype *Object, length int) *Object {
- return r.newNativeFuncConstructProto(construct, name, prototype, r.global.FunctionPrototype, length)
- }
- func (r *Runtime) newNativeFuncConstructProto(construct func(args []Value, proto *Object) *Object, name unistring.String, prototype, proto *Object, length int) *Object {
- v := &Object{runtime: r}
- 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, prototype)
- f.init(name, length)
- if prototype != nil {
- f._putProp("prototype", prototype, false, false, false)
- prototype.self._putProp("constructor", v, true, false, true)
- }
- 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 {
- return call.Arguments[0].ToNumber()
- } else {
- return valueInt(0)
- }
- }
- func (r *Runtime) builtin_newNumber(args []Value, proto *Object) *Object {
- var v Value
- if len(args) > 0 {
- v = args[0].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) error_toString(call FunctionCall) Value {
- var nameStr, msgStr valueString
- obj := call.This.ToObject(r).self
- name := obj.getStr("name", nil)
- if name == nil || name == _undefined {
- nameStr = asciiString("Error")
- } else {
- nameStr = name.toString()
- }
- msg := obj.getStr("message", nil)
- if msg == nil || msg == _undefined {
- msgStr = stringEmpty
- } else {
- msgStr = msg.toString()
- }
- if nameStr.length() == 0 {
- return msgStr
- }
- if msgStr.length() == 0 {
- return nameStr
- }
- var sb valueStringBuilder
- sb.WriteString(nameStr)
- sb.WriteString(asciiString(": "))
- sb.WriteString(msgStr)
- return sb.String()
- }
- func (r *Runtime) builtin_Error(args []Value, proto *Object) *Object {
- obj := r.newBaseObject(proto, classError)
- if len(args) > 0 && args[0] != _undefined {
- obj._putProp("message", args[0], true, false, true)
- }
- return obj.val
- }
- func (r *Runtime) builtin_new(construct *Object, args []Value) *Object {
- return r.toConstructor(construct)(args, nil)
- }
- func (r *Runtime) throw(e Value) {
- panic(e)
- }
- func (r *Runtime) builtin_thrower(FunctionCall) Value {
- r.typeErrorResult(true, "'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 valueString, direct, strict bool, this Value) Value {
- src := escapeInvalidUtf16(srcVal)
- p, err := r.compile("<eval>", src, strict, true)
- if err != nil {
- panic(err)
- }
- vm := r.vm
- vm.pushCtx()
- vm.prg = p
- vm.pc = 0
- if !direct {
- vm.stash = nil
- }
- vm.sb = vm.sp
- vm.push(this)
- if strict {
- vm.push(valueTrue)
- } else {
- vm.push(valueFalse)
- }
- vm.run()
- vm.popCtx()
- vm.halt = false
- retval := vm.stack[vm.sp-1]
- 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].(valueString); ok {
- return r.eval(str, false, false, r.globalObject)
- }
- 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, proto *Object) func(args []Value, newTarget *Object) *Object {
- if c == nil {
- return nil
- }
- return func(args []Value, newTarget *Object) *Object {
- var p *Object
- if newTarget != nil {
- if pp, ok := newTarget.self.getStr("prototype", nil).(*Object); ok {
- p = pp
- }
- }
- if p == nil {
- p = proto
- }
- return c(args, p)
- }
- }
- 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 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 toInt(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 (r *Runtime) toIndex(v Value) int {
- intIdx := v.ToInteger()
- if intIdx >= 0 && intIdx < maxInt {
- if bits.UintSize == 32 && intIdx >= math.MaxInt32 {
- panic(r.newError(r.global.RangeError, "Index %s overflows int", v.String()))
- }
- return int(intIdx)
- }
- panic(r.newError(r.global.RangeError, "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, false)
- }
- // 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, false)
- }
- // 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
- }
- func compile(name, src string, strict, eval bool) (p *Program, err error) {
- prg, err1 := parser.ParseFile(nil, name, src, 0)
- if err1 != nil {
- switch err1 := err1.(type) {
- case parser.ErrorList:
- if len(err1) > 0 && err1[0].Message == "Invalid left-hand side in assignment" {
- err = &CompilerReferenceError{
- CompilerError: CompilerError{
- Message: err1.Error(),
- },
- }
- return
- }
- }
- // FIXME offset
- err = &CompilerSyntaxError{
- CompilerError: CompilerError{
- Message: err1.Error(),
- },
- }
- return
- }
- p, err = compileAST(prg, strict, eval)
- return
- }
- func compileAST(prg *js_ast.Program, strict, eval bool) (p *Program, err error) {
- c := newCompiler()
- c.scope.strict = strict
- c.scope.eval = eval
- defer func() {
- if x := recover(); x != nil {
- p = nil
- switch x1 := x.(type) {
- case *CompilerSyntaxError:
- err = x1
- default:
- panic(x)
- }
- }
- }()
- c.compile(prg)
- p = c.p
- return
- }
- func (r *Runtime) compile(name, src string, strict, eval bool) (p *Program, err error) {
- p, err = compile(name, src, strict, eval)
- if err != nil {
- switch x1 := err.(type) {
- case *CompilerSyntaxError:
- err = &Exception{
- val: r.builtin_new(r.global.SyntaxError, []Value{newStringValue(x1.Error())}),
- }
- case *CompilerReferenceError:
- err = &Exception{
- val: r.newError(r.global.ReferenceError, 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 := Compile(name, src, false)
- if err != nil {
- return nil, err
- }
- return r.RunProgram(p)
- }
- // RunProgram executes a pre-compiled (see Compile()) code in the global context.
- func (r *Runtime) RunProgram(p *Program) (result Value, err error) {
- defer func() {
- if x := recover(); x != nil {
- if intr, ok := x.(*InterruptedError); ok {
- err = intr
- } else {
- panic(x)
- }
- }
- }()
- recursive := false
- if len(r.vm.callStack) > 0 {
- recursive = true
- r.vm.pushCtx()
- }
- r.vm.prg = p
- r.vm.pc = 0
- ex := r.vm.runTry()
- if ex == nil {
- result = r.vm.pop()
- } else {
- err = ex
- }
- if recursive {
- r.vm.popCtx()
- r.vm.halt = false
- r.vm.clearStack()
- } else {
- r.vm.stack = nil
- }
- 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.
- 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.
- // 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().
- Notes on individual types:
- Primitive types
- Primitive types (numbers, string, bool) are converted to the corresponding JavaScript primitives.
- 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.
- 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).
- 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 (if the struct is addressable) 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 or integer key type are converted into host objects that largely behave like a JavaScript Object.
- 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 are, however, some caveats:
- - If the slice is not addressable, the array cannot be extended or shrunk. Any attempt to do so (by setting an index
- beyond the current length or by modifying the length) will result in a TypeError.
- - Converted Arrays may not contain holes (because Go slices cannot). This means that hasOwnProperty(n) will always
- return `true` if n < length. Attempt to delete an item with an index < length will fail. Nil slice elements will be
- converted to `null`. Accessing an element beyond `length` will return `undefined`.
- 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 {
- switch i := i.(type) {
- case nil:
- return _null
- case *Object:
- if i == nil || i.runtime == nil {
- return _null
- }
- if 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:
- return newStringValue(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, nil, name, nil, 0)
- case func(ConstructorCall) *Object:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeConstructor(i, 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 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{}:
- if i == nil {
- return _null
- }
- obj := &Object{runtime: r}
- a := &objectGoSlice{
- baseObject: baseObject{
- val: obj,
- },
- data: &i,
- }
- obj.self = a
- a.init()
- return obj
- case *[]interface{}:
- if i == nil {
- return _null
- }
- obj := &Object{runtime: r}
- a := &objectGoSlice{
- baseObject: baseObject{
- val: obj,
- },
- data: i,
- sliceExtensible: true,
- }
- obj.self = a
- a.init()
- return obj
- }
- origValue := reflect.ValueOf(i)
- value := origValue
- for value.Kind() == reflect.Ptr {
- value = reflect.Indirect(value)
- }
- 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,
- value: value,
- },
- }
- m.init()
- obj.self = m
- return obj
- }
- }
- case reflect.Slice:
- obj := &Object{runtime: r}
- a := &objectGoSliceReflect{
- objectGoReflect: objectGoReflect{
- baseObject: baseObject{
- val: obj,
- },
- origValue: origValue,
- value: value,
- },
- }
- a.init()
- obj.self = a
- return obj
- case reflect.Func:
- name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name())
- return r.newNativeFunc(r.wrapReflectFunc(value), nil, name, nil, value.Type().NumIn())
- }
- obj := &Object{runtime: r}
- o := &objectGoReflect{
- baseObject: baseObject{
- val: obj,
- },
- origValue: origValue,
- value: 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)
- }
- callSlice := false
- 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)
- }
- // if this is a variadic Go function, and the caller has supplied
- // exactly the number of JavaScript arguments required, and this
- // is the last JavaScript argument, try treating the it as the
- // actual set of variadic Go arguments. if that succeeds, break
- // out of the loop.
- if typ.IsVariadic() && len(call.Arguments) == nargs && i == nargs-1 {
- if v, err := r.toReflectValue(a, typ.In(n)); err == nil {
- in[i] = v
- callSlice = true
- break
- }
- }
- var err error
- in[i], err = r.toReflectValue(a, t)
- if err != nil {
- panic(r.newError(r.global.TypeError, "Could not convert function call parameter %v to %v", a, t))
- }
- }
- var out []reflect.Value
- if callSlice {
- out = value.CallSlice(in)
- } else {
- out = value.Call(in)
- }
- if len(out) == 0 {
- return _undefined
- }
- if last := out[len(out)-1]; last.Type().Name() == "error" {
- if !last.IsNil() {
- err := last.Interface()
- if _, ok := err.(*Exception); ok {
- panic(err)
- }
- panic(r.NewGoError(last.Interface().(error)))
- }
- 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, typ reflect.Type) (reflect.Value, error) {
- switch typ.Kind() {
- case reflect.String:
- return reflect.ValueOf(v.String()).Convert(typ), nil
- case reflect.Bool:
- return reflect.ValueOf(v.ToBoolean()).Convert(typ), nil
- case reflect.Int:
- i, _ := toInt64(v)
- return reflect.ValueOf(int(i)).Convert(typ), nil
- case reflect.Int64:
- i, _ := toInt64(v)
- return reflect.ValueOf(i).Convert(typ), nil
- case reflect.Int32:
- i, _ := toInt64(v)
- return reflect.ValueOf(int32(i)).Convert(typ), nil
- case reflect.Int16:
- i, _ := toInt64(v)
- return reflect.ValueOf(int16(i)).Convert(typ), nil
- case reflect.Int8:
- i, _ := toInt64(v)
- return reflect.ValueOf(int8(i)).Convert(typ), nil
- case reflect.Uint:
- i, _ := toInt64(v)
- return reflect.ValueOf(uint(i)).Convert(typ), nil
- case reflect.Uint64:
- i, _ := toInt64(v)
- return reflect.ValueOf(uint64(i)).Convert(typ), nil
- case reflect.Uint32:
- i, _ := toInt64(v)
- return reflect.ValueOf(uint32(i)).Convert(typ), nil
- case reflect.Uint16:
- i, _ := toInt64(v)
- return reflect.ValueOf(uint16(i)).Convert(typ), nil
- case reflect.Uint8:
- i, _ := toInt64(v)
- return reflect.ValueOf(uint8(i)).Convert(typ), nil
- }
- if typ == typeCallable {
- if fn, ok := AssertFunction(v); ok {
- return reflect.ValueOf(fn), nil
- }
- }
- if typ == typeValue {
- return reflect.ValueOf(v), nil
- }
- if typ == typeObject {
- if obj, ok := v.(*Object); ok {
- return reflect.ValueOf(obj), nil
- }
- }
- et := v.ExportType()
- if et == nil || et == reflectTypeNil {
- return reflect.Zero(typ), nil
- }
- if et.AssignableTo(typ) {
- return reflect.ValueOf(v.Export()), nil
- } else if et.ConvertibleTo(typ) {
- return reflect.ValueOf(v.Export()).Convert(typ), nil
- }
- if typ == typeTime && et.Kind() == reflect.String {
- tme, ok := dateParse(v.String())
- if !ok {
- return reflect.Value{}, fmt.Errorf("Could not convert string %v to %v", v, typ)
- }
- return reflect.ValueOf(tme), nil
- }
- switch typ.Kind() {
- case reflect.Slice:
- if o, ok := v.(*Object); ok {
- if o.self.className() == classArray {
- l := int(toLength(o.self.getStr("length", nil)))
- s := reflect.MakeSlice(typ, l, l)
- elemTyp := typ.Elem()
- for i := 0; i < l; i++ {
- item := o.self.getIdx(valueInt(int64(i)), nil)
- itemval, err := r.toReflectValue(item, elemTyp)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert array element %v to %v at %d: %s", v, typ, i, err)
- }
- s.Index(i).Set(itemval)
- }
- return s, nil
- }
- }
- case reflect.Map:
- if o, ok := v.(*Object); ok {
- m := reflect.MakeMap(typ)
- keyTyp := typ.Key()
- elemTyp := typ.Elem()
- needConvertKeys := !reflect.ValueOf("").Type().AssignableTo(keyTyp)
- for _, itemName := range o.self.ownKeys(false, nil) {
- var kv reflect.Value
- var err error
- if needConvertKeys {
- kv, err = r.toReflectValue(itemName, keyTyp)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert map key %s to %v", itemName.String(), typ)
- }
- } else {
- kv = reflect.ValueOf(itemName.String())
- }
- ival := o.get(itemName, nil)
- if ival != nil {
- vv, err := r.toReflectValue(ival, elemTyp)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert map value %v to %v at key %s", ival, typ, itemName.String())
- }
- m.SetMapIndex(kv, vv)
- } else {
- m.SetMapIndex(kv, reflect.Zero(elemTyp))
- }
- }
- return m, nil
- }
- case reflect.Struct:
- if o, ok := v.(*Object); ok {
- s := reflect.New(typ).Elem()
- 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 {
- vv, err := r.toReflectValue(v, field.Type)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert struct value %v to %v for field %s: %s", v, field.Type, field.Name, err)
- }
- s.Field(i).Set(vv)
- }
- }
- }
- return s, nil
- }
- case reflect.Func:
- if fn, ok := AssertFunction(v); ok {
- return reflect.MakeFunc(typ, r.wrapJSFunc(fn, typ)), nil
- }
- case reflect.Ptr:
- elemTyp := typ.Elem()
- v, err := r.toReflectValue(v, elemTyp)
- if err != nil {
- return reflect.Value{}, err
- }
- ptrVal := reflect.New(v.Type())
- ptrVal.Elem().Set(v)
- return ptrVal, nil
- }
- return reflect.Value{}, 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) {
- jsArgs := make([]Value, len(args))
- for i, arg := range args {
- jsArgs[i] = r.ToValue(arg.Interface())
- }
- results = make([]reflect.Value, typ.NumOut())
- res, err := fn(_undefined, jsArgs...)
- if err == nil {
- if typ.NumOut() > 0 {
- results[0], err = r.toReflectValue(res, typ.Out(0))
- }
- }
- if err != nil {
- if typ.NumOut() == 2 && typ.Out(1).Name() == "error" {
- results[1] = reflect.ValueOf(err).Convert(typ.Out(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.
- 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")
- }
- vv, err := r.toReflectValue(v, tval.Elem().Type())
- if err != nil {
- return err
- }
- tval.Elem().Set(vv)
- return nil
- }
- // GlobalObject returns the global object.
- func (r *Runtime) GlobalObject() *Object {
- return r.globalObject
- }
- // Set the specified value as a property of the global object.
- // The value is first converted using ToValue()
- func (r *Runtime) Set(name string, value interface{}) {
- r.globalObject.self.setOwnStr(unistring.NewFromString(name), r.ToValue(value), false)
- }
- // Get the specified property of the global object.
- func (r *Runtime) Get(name string) Value {
- return r.globalObject.self.getStr(unistring.NewFromString(name), 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
- }
- // 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) {
- defer func() {
- if x := recover(); x != nil {
- switch x := x.(type) {
- case *Exception:
- err = x
- case *InterruptedError:
- err = x
- default:
- panic(x)
- }
- }
- }()
- return r.builtin_new(r.toObject(construct), args), nil
- }
- // 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.
- 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) {
- defer func() {
- if x := recover(); x != nil {
- if ex, ok := x.(*InterruptedError); ok {
- err = ex
- } else {
- panic(x)
- }
- }
- }()
- ex := obj.runtime.vm.try(func() {
- ret = f(FunctionCall{
- This: this,
- Arguments: args,
- })
- })
- if ex != nil {
- err = ex
- }
- obj.runtime.vm.clearStack()
- return
- }, true
- }
- }
- return nil, false
- }
- // 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
- }
- // 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()) (err error) {
- defer func() {
- if x := recover(); x != nil {
- switch x := x.(type) {
- case *Exception:
- err = x
- case *InterruptedError:
- err = x
- case Value:
- err = &Exception{
- val: x,
- }
- default:
- panic(x)
- }
- }
- }()
- f()
- 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) toNumber(v Value) Value {
- switch o := v.(type) {
- case valueInt, valueFloat:
- return v
- case *Object:
- if pvo, ok := o.self.(*primitiveValueObject); ok {
- return r.toNumber(pvo.pValue)
- }
- }
- panic(r.NewTypeError("Value is not a number: %s", v))
- }
- 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
- }
- return r.toObject(c)
- }
- func (r *Runtime) returnThis(call FunctionCall) Value {
- return call.This
- }
- 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)
- }
- func (r *Runtime) getIterator(obj Value, method func(FunctionCall) Value) *Object {
- if method == nil {
- method = toMethod(r.getV(obj, symIterator))
- if method == nil {
- panic(r.NewTypeError("object is not iterable"))
- }
- }
- return r.toObject(method(FunctionCall{
- This: obj,
- }))
- }
- func returnIter(iter *Object) {
- retMethod := toMethod(iter.self.getStr("return", nil))
- if retMethod != nil {
- _ = tryFunc(func() {
- retMethod(FunctionCall{This: iter})
- })
- }
- }
- func (r *Runtime) iterate(iter *Object, step func(Value)) {
- for {
- res := r.toObject(toMethod(iter.self.getStr("next", nil))(FunctionCall{This: iter}))
- if nilSafe(res.self.getStr("done", nil)).ToBoolean() {
- break
- }
- err := tryFunc(func() {
- step(nilSafe(res.self.getStr("value", nil)))
- })
- if err != nil {
- returnIter(iter)
- panic(err)
- }
- }
- }
- 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) newLazyObject(create func(*Object) objectImpl) *Object {
- val := &Object{runtime: r}
- o := &lazyObject{
- val: val,
- create: create,
- }
- val.self = o
- return val
- }
- func (r *Runtime) getHash() *maphash.Hash {
- if r.hash == nil {
- r.hash = &maphash.Hash{}
- }
- return r.hash
- }
- 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
- }
- }
|