123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470 |
- package goja
- import (
- "bytes"
- "errors"
- "fmt"
- "go/ast"
- "math"
- "math/rand"
- "reflect"
- "strconv"
- js_ast "github.com/dop251/goja/ast"
- "github.com/dop251/goja/parser"
- )
- const (
- sqrt1_2 float64 = math.Sqrt2 / 2
- )
- var (
- typeCallable = reflect.TypeOf(Callable(nil))
- typeValue = reflect.TypeOf((*Value)(nil)).Elem()
- )
- type global struct {
- Object *Object
- Array *Object
- Function *Object
- String *Object
- Number *Object
- Boolean *Object
- RegExp *Object
- Date *Object
- ArrayBuffer *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
- ArrayBufferPrototype *Object
- ErrorPrototype *Object
- TypeErrorPrototype *Object
- SyntaxErrorPrototype *Object
- RangeErrorPrototype *Object
- ReferenceErrorPrototype *Object
- EvalErrorPrototype *Object
- URIErrorPrototype *Object
- GoErrorPrototype *Object
- Eval *Object
- thrower *Object
- throwerProperty Value
- }
- 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 Runtime struct {
- global global
- globalObject *Object
- stringSingleton *stringObject
- rand RandSource
- typeInfoCache map[reflect.Type]*reflectTypeInfo
- fieldNameMapper FieldNameMapper
- vm *vm
- }
- type stackFrame struct {
- prg *Program
- funcName string
- pc int
- }
- func (f *stackFrame) position() Position {
- 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)
- 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)
- 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(name, value, true, false, true)
- }
- func (r *Runtime) init() {
- r.rand = rand.Float64
- 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(nil, nil, "Empty", nil, 0)
- r.initObject()
- r.initFunction()
- r.initArray()
- r.initString()
- r.initNumber()
- r.initRegExp()
- r.initDate()
- r.initBoolean()
- r.initErrors()
- r.global.Eval = r.newNativeFunc(r.builtin_eval, nil, "eval", nil, 1)
- r.addToGlobal("eval", r.global.Eval)
- r.initGlobalObject()
- r.initMath()
- r.initJSON()
- //r.initTypedArrays()
- 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 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 (r *Runtime) newArray(prototype *Object) (a *arrayObject) {
- v := &Object{runtime: r}
- a = &arrayObject{}
- a.class = classArray
- a.val = v
- a.extensible = true
- v.self = a
- a.prototype = prototype
- a.init()
- return
- }
- func (r *Runtime) newArrayObject() *arrayObject {
- return r.newArray(r.global.ArrayPrototype)
- }
- func (r *Runtime) newArrayValues(values []Value) *Object {
- v := &Object{runtime: r}
- a := &arrayObject{}
- a.class = classArray
- a.val = v
- a.extensible = true
- v.self = a
- a.prototype = r.global.ArrayPrototype
- a.init()
- a.values = values
- a.length = int64(len(values))
- a.objCount = a.length
- return v
- }
- func (r *Runtime) newArrayLength(l int64) *Object {
- a := r.newArrayValues(nil)
- a.self.putStr("length", intToValue(l), true)
- return a
- }
- func (r *Runtime) newBaseObject(proto *Object, class string) (o *baseObject) {
- v := &Object{runtime: r}
- o = &baseObject{}
- o.class = class
- o.val = v
- o.extensible = true
- v.self = o
- o.prototype = proto
- 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 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) *Object, name 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: construct,
- }
- 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 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) *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) newNativeFunc(call func(FunctionCall) Value, construct func(args []Value) *Object, name 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: construct,
- }
- 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 string, proto *Object, length int) *nativeFuncObject {
- f := &nativeFuncObject{
- baseFuncObject: baseFuncObject{
- baseObject: baseObject{
- class: classFunction,
- val: v,
- extensible: true,
- prototype: r.global.FunctionPrototype,
- },
- },
- f: r.constructWrap(construct, proto),
- construct: func(args []Value) *Object {
- return construct(args, 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 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 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.constructWrap(construct, prototype)
- f.construct = func(args []Value) *Object {
- return construct(args, 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 intToValue(0)
- }
- }
- func (r *Runtime) builtin_newNumber(args []Value) *Object {
- var v Value
- if len(args) > 0 {
- v = args[0].ToNumber()
- } else {
- v = intToValue(0)
- }
- return r.newPrimitiveObject(v, r.global.NumberPrototype, 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) *Object {
- var v Value
- if len(args) > 0 {
- if args[0].ToBoolean() {
- v = valueTrue
- } else {
- v = valueFalse
- }
- } else {
- v = valueFalse
- }
- return r.newPrimitiveObject(v, r.global.BooleanPrototype, classBoolean)
- }
- func (r *Runtime) error_toString(call FunctionCall) Value {
- obj := call.This.ToObject(r).self
- msg := obj.getStr("message")
- name := obj.getStr("name")
- var nameStr, msgStr string
- if name != nil && name != _undefined {
- nameStr = name.String()
- }
- if msg != nil && msg != _undefined {
- msgStr = msg.String()
- }
- if nameStr != "" && msgStr != "" {
- return newStringValue(fmt.Sprintf("%s: %s", name.String(), msgStr))
- } else {
- if nameStr != "" {
- return name.ToString()
- } else {
- return msg.ToString()
- }
- }
- }
- 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 {
- repeat:
- switch f := construct.self.(type) {
- case *nativeFuncObject:
- if f.construct != nil {
- return f.construct(args)
- } else {
- panic("Not a constructor")
- }
- case *boundFuncObject:
- if f.construct != nil {
- return f.construct(args)
- } else {
- panic("Not a constructor")
- }
- case *funcObject:
- // TODO: implement
- panic("Not implemented")
- case *lazyObject:
- construct.self = f.create(construct)
- goto repeat
- default:
- panic("Not a constructor")
- }
- }
- func (r *Runtime) throw(e Value) {
- panic(e)
- }
- func (r *Runtime) builtin_thrower(call 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(src string, direct, strict bool, this Value) Value {
- 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].assertString(); ok {
- return r.eval(str.String(), false, false, r.globalObject)
- }
- return call.Arguments[0]
- }
- func (r *Runtime) constructWrap(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) 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 toUInt32(v Value) uint32 {
- v = v.ToNumber()
- if i, ok := v.assertInt(); ok {
- return uint32(i)
- }
- if f, ok := v.assertFloat(); ok {
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint32(int64(f))
- }
- }
- return 0
- }
- func toUInt16(v Value) uint16 {
- v = v.ToNumber()
- if i, ok := v.assertInt(); ok {
- return uint16(i)
- }
- if f, ok := v.assertFloat(); ok {
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return uint16(int64(f))
- }
- }
- return 0
- }
- 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 toInt32(v Value) int32 {
- v = v.ToNumber()
- if i, ok := v.assertInt(); ok {
- return int32(i)
- }
- if f, ok := v.assertFloat(); ok {
- if !math.IsNaN(f) && !math.IsInf(f, 0) {
- return int32(int64(f))
- }
- }
- return 0
- }
- 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
- }
- // 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).
- func (r *Runtime) Interrupt(v interface{}) {
- r.vm.Interrupt(v)
- }
- /*
- ToValue converts a Go value into JavaScript value.
- Primitive types (ints and uints, floats, string, bool) are converted to the corresponding JavaScript primitives.
- func(FunctionCall) Value is treated as a native JavaScript function.
- map[string]interface{} is converted into a host object that largely behaves like a JavaScript Object.
- []interface{} is converted into a host object that behaves largely like a JavaScript Array, however it's not extensible
- because extending it can change the pointer so it becomes detached from the original.
- *[]interface{} same as above, but the array becomes extensible.
- A function is wrapped within a native JavaScript function. When called the arguments are automatically converted to
- the appropriate Go types. If conversion is not possible, a TypeError is thrown.
- A slice type is converted into a generic reflect based host object that behaves similar to an unexpandable Array.
- 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 Value:
- // TODO: prevent importing Objects from a different runtime
- return i
- case string:
- return newStringValue(i)
- case bool:
- if i {
- return valueTrue
- } else {
- return valueFalse
- }
- case func(FunctionCall) Value:
- return r.newNativeFunc(i, nil, "", nil, 0)
- case func(ConstructorCall) *Object:
- return r.newNativeConstructor(i, "", 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 int64(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{}:
- obj := &Object{runtime: r}
- m := &objectGoMapSimple{
- baseObject: baseObject{
- val: obj,
- extensible: true,
- },
- data: i,
- }
- obj.self = m
- m.init()
- return obj
- case []interface{}:
- obj := &Object{runtime: r}
- a := &objectGoSlice{
- baseObject: baseObject{
- val: obj,
- },
- data: &i,
- }
- obj.self = a
- a.init()
- return obj
- case *[]interface{}:
- 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:
- return r.newNativeFunc(r.wrapReflectFunc(value), nil, "", 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, _ := toInt(v)
- return reflect.ValueOf(int(i)).Convert(typ), nil
- case reflect.Int64:
- i, _ := toInt(v)
- return reflect.ValueOf(i).Convert(typ), nil
- case reflect.Int32:
- i, _ := toInt(v)
- return reflect.ValueOf(int32(i)).Convert(typ), nil
- case reflect.Int16:
- i, _ := toInt(v)
- return reflect.ValueOf(int16(i)).Convert(typ), nil
- case reflect.Int8:
- i, _ := toInt(v)
- return reflect.ValueOf(int8(i)).Convert(typ), nil
- case reflect.Uint:
- i, _ := toInt(v)
- return reflect.ValueOf(uint(i)).Convert(typ), nil
- case reflect.Uint64:
- i, _ := toInt(v)
- return reflect.ValueOf(uint64(i)).Convert(typ), nil
- case reflect.Uint32:
- i, _ := toInt(v)
- return reflect.ValueOf(uint32(i)).Convert(typ), nil
- case reflect.Uint16:
- i, _ := toInt(v)
- return reflect.ValueOf(uint16(i)).Convert(typ), nil
- case reflect.Uint8:
- i, _ := toInt(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.Implements(typeValue) {
- return reflect.ValueOf(v), nil
- }
- et := v.ExportType()
- if et == nil {
- 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
- }
- switch typ.Kind() {
- case reflect.Slice:
- if o, ok := v.(*Object); ok {
- if o.self.className() == classArray {
- l := int(toLength(o.self.getStr("length")))
- s := reflect.MakeSlice(typ, l, l)
- elemTyp := typ.Elem()
- for i := 0; i < l; i++ {
- item := o.self.get(intToValue(int64(i)))
- itemval, err := r.toReflectValue(item, elemTyp)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert array element %v to %v at %d", v, typ, i)
- }
- 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 item, f := o.self.enumerate(false, false)(); f != nil; item, f = f() {
- var kv reflect.Value
- var err error
- if needConvertKeys {
- kv, err = r.toReflectValue(newStringValue(item.name), keyTyp)
- if err != nil {
- return reflect.Value{}, fmt.Errorf("Could not convert map key %s to %v", item.name, typ)
- }
- } else {
- kv = reflect.ValueOf(item.name)
- }
- ival := item.value
- if ival == nil {
- ival = o.self.getStr(item.name)
- }
- 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, item.name)
- }
- 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) {
- v := o.self.getStr(field.Name)
- 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", v, field.Type, field.Name)
- }
- 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
- }
- }
- 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.putStr(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(name)
- }
- // 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
- }
- // 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
- }
- // 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 _undefined
- }
- 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
- }
|