12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223 |
- package goja
- import (
- "bytes"
- "errors"
- "fmt"
- "github.com/dop251/goja/parser"
- "go/ast"
- "math"
- "math/rand"
- "reflect"
- "strconv"
- )
- const (
- sqrt1_2 float64 = math.Sqrt2 / 2
- )
- 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 RandSource func() float64
- type Runtime struct {
- global global
- globalObject *Object
- stringSingleton *stringObject
- rand RandSource
- 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))
- }
- type Exception struct {
- val Value
- stack []stackFrame
- }
- type InterruptedError struct {
- Exception
- iface interface{}
- }
- func (e *InterruptedError) Value() interface{} {
- return e.iface
- }
- 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')
- for _, frame := range e.stack {
- b.WriteString("\tat ")
- if frame.prg != nil {
- if n := frame.prg.funcName; n != "" {
- b.WriteString(n)
- b.WriteString(" (")
- }
- if n := frame.prg.src.name; n != "" {
- b.WriteString(n)
- } else {
- b.WriteString("<eval>")
- }
- b.WriteByte(':')
- b.WriteString(frame.position().String())
- b.WriteByte('(')
- b.WriteString(strconv.Itoa(frame.pc))
- b.WriteByte(')')
- if frame.prg.funcName != "" {
- b.WriteByte(')')
- }
- } else {
- if frame.funcName != "" {
- b.WriteString(frame.funcName)
- b.WriteString(" (")
- }
- b.WriteString("native")
- if frame.funcName != "" {
- b.WriteByte(')')
- }
- }
- b.WriteByte('\n')
- }
- return b.String()
- }
- func (e *Exception) Error() string {
- if e == nil || e.val == nil {
- return "<nil>"
- }
- return e.val.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
- }
- 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{
- 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) newNativeFunc(call func(FunctionCall) Value, construct func(args []Value) *Object, name string, proto *Object, length int) *Object {
- v := &Object{runtime: r}
- f := &nativeFuncObject{
- 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) objectImpl {
- f := &nativeFuncObject{
- 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 {
- 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 {
- 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) (p *Program, err error) {
- return compile(name, src, strict, false)
- }
- 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
- }
- 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
- } 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 _undefined
- 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 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 uint64:
- if 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 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)
- }
- switch value.Kind() {
- case reflect.Map:
- if value.Type().Name() == "" {
- 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()
- if len(call.Arguments) != nargs {
- if typ.IsVariadic() {
- if len(call.Arguments) < nargs-1 {
- panic(r.newError(r.global.TypeError, "expected at least %d arguments; got %d", nargs-1, len(call.Arguments)))
- }
- } else {
- panic(r.newError(r.global.TypeError, "expected %d argument(s); got %d", nargs, len(call.Arguments)))
- }
- }
- in := make([]reflect.Value, len(call.Arguments))
- 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 {
- 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
- }
- et := v.ExportType()
- 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
- }
- }
- return reflect.Value{}, fmt.Errorf("Could not convert %v to %v", v, typ)
- }
- // 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
- }
- // 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) {
- ex := obj.runtime.vm.try(func() {
- ret = f(FunctionCall{
- This: this,
- Arguments: args,
- })
- })
- if ex != nil {
- err = ex
- }
- 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
- }
|