package goja
import (
"bytes"
"errors"
"fmt"
"go/ast"
"hash/maphash"
"math"
"math/big"
"math/bits"
"math/rand"
"reflect"
"runtime"
"strconv"
"time"
"golang.org/x/text/collate"
js_ast "github.com/dop251/goja/ast"
"github.com/dop251/goja/file"
"github.com/dop251/goja/parser"
"github.com/dop251/goja/unistring"
)
const (
sqrt1_2 float64 = math .Sqrt2 / 2
deoptimiseRegexp = false
)
var (
typeCallable = reflect .TypeOf (Callable (nil ))
typeValue = reflect .TypeOf ((*Value )(nil )).Elem ()
typeObject = reflect .TypeOf ((*Object )(nil ))
typeTime = reflect .TypeOf (time .Time {})
typeBigInt = reflect .TypeOf ((*big .Int )(nil ))
typeBytes = reflect .TypeOf (([]byte )(nil ))
)
type iterationKind int
const (
iterationKindKey iterationKind = iota
iterationKindValue
iterationKindKeyValue
)
type global struct {
stash stash
Object *Object
Array *Object
Function *Object
String *Object
Number *Object
BigInt *Object
Boolean *Object
RegExp *Object
Date *Object
Symbol *Object
Proxy *Object
Reflect *Object
Promise *Object
Math *Object
JSON *Object
AsyncFunction *Object
ArrayBuffer *Object
DataView *Object
TypedArray *Object
Uint8Array *Object
Uint8ClampedArray *Object
Int8Array *Object
Uint16Array *Object
Int16Array *Object
Uint32Array *Object
Int32Array *Object
Float32Array *Object
Float64Array *Object
BigInt64Array *Object
BigUint64Array *Object
WeakSet *Object
WeakMap *Object
Map *Object
Set *Object
Error *Object
AggregateError *Object
TypeError *Object
ReferenceError *Object
SyntaxError *Object
RangeError *Object
EvalError *Object
URIError *Object
GoError *Object
ObjectPrototype *Object
ArrayPrototype *Object
NumberPrototype *Object
BigIntPrototype *Object
StringPrototype *Object
BooleanPrototype *Object
FunctionPrototype *Object
RegExpPrototype *Object
DatePrototype *Object
SymbolPrototype *Object
ArrayBufferPrototype *Object
DataViewPrototype *Object
TypedArrayPrototype *Object
WeakSetPrototype *Object
WeakMapPrototype *Object
MapPrototype *Object
SetPrototype *Object
PromisePrototype *Object
GeneratorFunctionPrototype *Object
GeneratorFunction *Object
GeneratorPrototype *Object
AsyncFunctionPrototype *Object
IteratorPrototype *Object
ArrayIteratorPrototype *Object
MapIteratorPrototype *Object
SetIteratorPrototype *Object
StringIteratorPrototype *Object
RegExpStringIteratorPrototype *Object
ErrorPrototype *Object
Eval *Object
thrower *Object
stdRegexpProto *guardedObject
weakSetAdder *Object
weakMapAdder *Object
mapAdder *Object
setAdder *Object
arrayValues *Object
arrayToString *Object
stringproto_trimEnd *Object
stringproto_trimStart *Object
parseFloat, parseInt *Object
typedArrayValues *Object
}
type Flag int
const (
FLAG_NOT_SET Flag = iota
FLAG_FALSE
FLAG_TRUE
)
func (f Flag ) Bool () bool {
return f == FLAG_TRUE
}
func ToFlag (b bool ) Flag {
if b {
return FLAG_TRUE
}
return FLAG_FALSE
}
type RandSource func () float64
type Now func () time .Time
type Runtime struct {
global global
globalObject *Object
stringSingleton *stringObject
rand RandSource
now Now
_collator *collate .Collator
parserOptions []parser .Option
symbolRegistry map [unistring .String ]*Symbol
fieldsInfoCache map [reflect .Type ]*reflectFieldsInfo
methodsInfoCache map [reflect .Type ]*reflectMethodsInfo
fieldNameMapper FieldNameMapper
vm *vm
hash *maphash .Hash
idSeq uint64
jobQueue []func ()
promiseRejectionTracker PromiseRejectionTracker
asyncContextTracker AsyncContextTracker
}
type StackFrame struct {
prg *Program
funcName unistring .String
pc int
}
func (f *StackFrame ) SrcName () string {
if f .prg == nil {
return "<native>"
}
return f .prg .src .Name ()
}
func (f *StackFrame ) FuncName () string {
if f .funcName == "" && f .prg == nil {
return "<native>"
}
if f .funcName == "" {
return "<anonymous>"
}
return f .funcName .String ()
}
func (f *StackFrame ) Position () file .Position {
if f .prg == nil || f .prg .src == nil {
return file .Position {}
}
return f .prg .src .Position (f .prg .sourceOffset (f .pc ))
}
func (f *StackFrame ) WriteToValueBuilder (b *StringBuilder ) {
if f .prg != nil {
if n := f .prg .funcName ; n != "" {
b .WriteString (stringValueFromRaw (n ))
b .writeASCII (" (" )
}
p := f .Position ()
if p .Filename != "" {
b .WriteUTF8String (p .Filename )
} else {
b .writeASCII ("<eval>" )
}
b .WriteRune (':' )
b .writeASCII (strconv .Itoa (p .Line ))
b .WriteRune (':' )
b .writeASCII (strconv .Itoa (p .Column ))
b .WriteRune ('(' )
b .writeASCII (strconv .Itoa (f .pc ))
b .WriteRune (')' )
if f .prg .funcName != "" {
b .WriteRune (')' )
}
} else {
if f .funcName != "" {
b .WriteString (stringValueFromRaw (f .funcName ))
b .writeASCII (" (" )
}
b .writeASCII ("native" )
if f .funcName != "" {
b .WriteRune (')' )
}
}
}
func (f *StackFrame ) Write (b *bytes .Buffer ) {
if f .prg != nil {
if n := f .prg .funcName ; n != "" {
b .WriteString (n .String ())
b .WriteString (" (" )
}
p := f .Position ()
if p .Filename != "" {
b .WriteString (p .Filename )
} else {
b .WriteString ("<eval>" )
}
b .WriteByte (':' )
b .WriteString (strconv .Itoa (p .Line ))
b .WriteByte (':' )
b .WriteString (strconv .Itoa (p .Column ))
b .WriteByte ('(' )
b .WriteString (strconv .Itoa (f .pc ))
b .WriteByte (')' )
if f .prg .funcName != "" {
b .WriteByte (')' )
}
} else {
if f .funcName != "" {
b .WriteString (f .funcName .String ())
b .WriteString (" (" )
}
b .WriteString ("native" )
if f .funcName != "" {
b .WriteByte (')' )
}
}
}
type uncatchableException interface {
error
_uncatchableException()
}
type Exception struct {
val Value
stack []StackFrame
}
type baseUncatchableException struct {
Exception
}
func (e *baseUncatchableException ) _uncatchableException () {}
type InterruptedError struct {
baseUncatchableException
iface interface {}
}
func (e *InterruptedError ) Unwrap () error {
if err , ok := e .iface .(error ); ok {
return err
}
return nil
}
type StackOverflowError struct {
baseUncatchableException
}
func (e *InterruptedError ) Value () interface {} {
return e .iface
}
func (e *InterruptedError ) String () string {
if e == nil {
return "<nil>"
}
var b bytes .Buffer
if e .iface != nil {
b .WriteString (fmt .Sprint (e .iface ))
b .WriteByte ('\n' )
}
e .writeFullStack (&b )
return b .String ()
}
func (e *InterruptedError ) Error () string {
if e == nil || e .iface == nil {
return "<nil>"
}
var b bytes .Buffer
b .WriteString (fmt .Sprint (e .iface ))
e .writeShortStack (&b )
return b .String ()
}
func (e *Exception ) writeFullStack (b *bytes .Buffer ) {
for _ , frame := range e .stack {
b .WriteString ("\tat " )
frame .Write (b )
b .WriteByte ('\n' )
}
}
func (e *Exception ) writeShortStack (b *bytes .Buffer ) {
if len (e .stack ) > 0 && (e .stack [0 ].prg != nil || e .stack [0 ].funcName != "" ) {
b .WriteString (" at " )
e .stack [0 ].Write (b )
}
}
func (e *Exception ) String () string {
if e == nil {
return "<nil>"
}
var b bytes .Buffer
if e .val != nil {
b .WriteString (e .val .String ())
b .WriteByte ('\n' )
}
e .writeFullStack (&b )
return b .String ()
}
func (e *Exception ) Error () string {
if e == nil {
return "<nil>"
}
var b bytes .Buffer
if e .val != nil {
b .WriteString (e .val .String ())
}
e .writeShortStack (&b )
return b .String ()
}
func (e *Exception ) Value () Value {
return e .val
}
func (e *Exception ) Unwrap () error {
if obj , ok := e .val .(*Object ); ok {
if obj .runtime .getGoError ().self .hasInstance (obj ) {
if val := obj .Get ("value" ); val != nil {
e1 , _ := val .Export ().(error )
return e1
}
}
}
return nil
}
func (e *Exception ) Stack () []StackFrame {
return e .stack
}
func (r *Runtime ) createIterProto (val *Object ) objectImpl {
o := newBaseObjectObj (val , r .global .ObjectPrototype , classObject )
o ._putSym (SymIterator , valueProp (r .newNativeFunc (r .returnThis , "[Symbol.iterator]" , 0 ), true , false , true ))
return o
}
func (r *Runtime ) getIteratorPrototype () *Object {
var o *Object
if o = r .global .IteratorPrototype ; o == nil {
o = &Object {runtime : r }
r .global .IteratorPrototype = o
o .self = r .createIterProto (o )
}
return o
}
func (r *Runtime ) init () {
r .rand = rand .Float64
r .now = time .Now
r .global .ObjectPrototype = &Object {runtime : r }
r .newTemplatedObject (getObjectProtoTemplate (), r .global .ObjectPrototype )
r .globalObject = &Object {runtime : r }
r .newTemplatedObject (getGlobalObjectTemplate (), r .globalObject )
r .vm = &vm {
r : r ,
}
r .vm .init ()
}
func (r *Runtime ) typeErrorResult (throw bool , args ...interface {}) {
if throw {
panic (r .NewTypeError (args ...))
}
}
func (r *Runtime ) newError (typ *Object , format string , args ...interface {}) Value {
var msg string
if len (args ) > 0 {
msg = fmt .Sprintf (format , args ...)
} else {
msg = format
}
return r .builtin_new (typ , []Value {newStringValue (msg )})
}
func (r *Runtime ) throwReferenceError (name unistring .String ) {
panic (r .newReferenceError (name ))
}
func (r *Runtime ) newReferenceError (name unistring .String ) Value {
return r .newError (r .getReferenceError (), "%s is not defined" , name )
}
func (r *Runtime ) newSyntaxError (msg string , offset int ) Value {
return r .builtin_new (r .getSyntaxError (), []Value {newStringValue (msg )})
}
func newBaseObjectObj(obj , proto *Object , class string ) *baseObject {
o := &baseObject {
class : class ,
val : obj ,
extensible : true ,
prototype : proto ,
}
obj .self = o
o .init ()
return o
}
func newGuardedObj(proto *Object , class string ) *guardedObject {
return &guardedObject {
baseObject : baseObject {
class : class ,
extensible : true ,
prototype : proto ,
},
}
}
func (r *Runtime ) newBaseObject (proto *Object , class string ) (o *baseObject ) {
v := &Object {runtime : r }
return newBaseObjectObj (v , proto , class )
}
func (r *Runtime ) newGuardedObject (proto *Object , class string ) (o *guardedObject ) {
v := &Object {runtime : r }
o = newGuardedObj (proto , class )
v .self = o
o .val = v
o .init ()
return
}
func (r *Runtime ) NewObject () (v *Object ) {
return r .newBaseObject (r .global .ObjectPrototype , classObject ).val
}
func (r *Runtime ) CreateObject (proto *Object ) *Object {
return r .newBaseObject (proto , classObject ).val
}
func (r *Runtime ) NewArray (items ...interface {}) *Object {
values := make ([]Value , len (items ))
for i , item := range items {
values [i ] = r .ToValue (item )
}
return r .newArrayValues (values )
}
func (r *Runtime ) NewTypeError (args ...interface {}) *Object {
msg := ""
if len (args ) > 0 {
f , _ := args [0 ].(string )
msg = fmt .Sprintf (f , args [1 :]...)
}
return r .builtin_new (r .getTypeError (), []Value {newStringValue (msg )})
}
func (r *Runtime ) NewGoError (err error ) *Object {
e := r .newError (r .getGoError (), err .Error()).(*Object )
e .Set ("value" , err )
return e
}
func (r *Runtime ) newFunc (name unistring .String , length int , strict bool ) (f *funcObject ) {
f = &funcObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) newAsyncFunc (name unistring .String , length int , strict bool ) (f *asyncFuncObject ) {
f = &asyncFuncObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .class = classFunction
f .prototype = r .getAsyncFunctionPrototype ()
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) newGeneratorFunc (name unistring .String , length int , strict bool ) (f *generatorFuncObject ) {
f = &generatorFuncObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .class = classFunction
f .prototype = r .getGeneratorFunctionPrototype ()
f .val .self = f
f .init (name , intToValue (int64 (length )))
f ._putProp ("prototype" , r .newBaseObject (r .getGeneratorPrototype (), classObject ).val , true , false , false )
return
}
func (r *Runtime ) newClassFunc (name unistring .String , length int , proto *Object , derived bool ) (f *classFuncObject ) {
v := &Object {runtime : r }
f = &classFuncObject {}
f .class = classFunction
f .val = v
f .extensible = true
f .strict = true
f .derived = derived
v .self = f
f .prototype = proto
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) initBaseJsFunction (f *baseJsFuncObject , strict bool ) {
v := &Object {runtime : r }
f .class = classFunction
f .val = v
f .extensible = true
f .strict = strict
f .prototype = r .getFunctionPrototype ()
}
func (r *Runtime ) newMethod (name unistring .String , length int , strict bool ) (f *methodFuncObject ) {
f = &methodFuncObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) newGeneratorMethod (name unistring .String , length int , strict bool ) (f *generatorMethodFuncObject ) {
f = &generatorMethodFuncObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .prototype = r .getGeneratorFunctionPrototype ()
f .val .self = f
f .init (name , intToValue (int64 (length )))
f ._putProp ("prototype" , r .newBaseObject (r .getGeneratorPrototype (), classObject ).val , true , false , false )
return
}
func (r *Runtime ) newAsyncMethod (name unistring .String , length int , strict bool ) (f *asyncMethodFuncObject ) {
f = &asyncMethodFuncObject {}
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) initArrowFunc (f *arrowFuncObject , strict bool ) {
r .initBaseJsFunction (&f .baseJsFuncObject , strict )
f .newTarget = r .vm .newTarget
}
func (r *Runtime ) newArrowFunc (name unistring .String , length int , strict bool ) (f *arrowFuncObject ) {
f = &arrowFuncObject {}
r .initArrowFunc (f , strict )
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) newAsyncArrowFunc (name unistring .String , length int , strict bool ) (f *asyncArrowFuncObject ) {
f = &asyncArrowFuncObject {}
r .initArrowFunc (&f .arrowFuncObject , strict )
f .class = classObject
f .prototype = r .getAsyncFunctionPrototype ()
f .val .self = f
f .init (name , intToValue (int64 (length )))
return
}
func (r *Runtime ) newNativeConstructor (call func (ConstructorCall ) *Object , name unistring .String , length int64 ) *Object {
v := &Object {runtime : r }
f := &nativeFuncObject {
baseFuncObject : baseFuncObject {
baseObject : baseObject {
class : classFunction ,
val : v ,
extensible : true ,
prototype : r .getFunctionPrototype (),
},
},
}
f .f = func (c FunctionCall ) Value {
thisObj , _ := c .This .(*Object )
if thisObj != nil {
res := call (ConstructorCall {
This : thisObj ,
Arguments : c .Arguments ,
})
if res == nil {
return _undefined
}
return res
}
return f .defaultConstruct (call , c .Arguments , nil )
}
f .construct = func (args []Value , newTarget *Object ) *Object {
return f .defaultConstruct (call , args , newTarget )
}
v .self = f
f .init (name , intToValue (length ))
proto := r .NewObject ()
proto .self ._putProp ("constructor" , v , true , false , true )
f ._putProp ("prototype" , proto , true , false , false )
return v
}
func (r *Runtime ) newNativeConstructOnly (v *Object , ctor func (args []Value , newTarget *Object ) *Object , defaultProto *Object , name unistring .String , length int64 ) *nativeFuncObject {
return r .newNativeFuncAndConstruct (v , func (call FunctionCall ) Value {
return ctor (call .Arguments , nil )
},
func (args []Value , newTarget *Object ) *Object {
if newTarget == nil {
newTarget = v
}
return ctor (args , newTarget )
}, defaultProto , name , intToValue (length ))
}
func (r *Runtime ) newNativeFuncAndConstruct (v *Object , call func (call FunctionCall ) Value , ctor func (args []Value , newTarget *Object ) *Object , defaultProto *Object , name unistring .String , l Value ) *nativeFuncObject {
if v == nil {
v = &Object {runtime : r }
}
f := &nativeFuncObject {
baseFuncObject : baseFuncObject {
baseObject : baseObject {
class : classFunction ,
val : v ,
extensible : true ,
prototype : r .getFunctionPrototype (),
},
},
f : call ,
construct : ctor ,
}
v .self = f
f .init (name , l )
if defaultProto != nil {
f ._putProp ("prototype" , defaultProto , false , false , false )
}
return f
}
func (r *Runtime ) newNativeFunc (call func (FunctionCall ) Value , name unistring .String , length int ) *Object {
v := &Object {runtime : r }
f := &nativeFuncObject {
baseFuncObject : baseFuncObject {
baseObject : baseObject {
class : classFunction ,
val : v ,
extensible : true ,
prototype : r .getFunctionPrototype (),
},
},
f : call ,
}
v .self = f
f .init (name , intToValue (int64 (length )))
return v
}
func (r *Runtime ) newWrappedFunc (value reflect .Value ) *Object {
v := &Object {runtime : r }
f := &wrappedFuncObject {
nativeFuncObject : nativeFuncObject {
baseFuncObject : baseFuncObject {
baseObject : baseObject {
class : classFunction ,
val : v ,
extensible : true ,
prototype : r .getFunctionPrototype (),
},
},
f : r .wrapReflectFunc (value ),
},
wrapped : value ,
}
v .self = f
name := unistring .NewFromString (runtime .FuncForPC (value .Pointer ()).Name ())
f .init (name , intToValue (int64 (value .Type ().NumIn ())))
return v
}
func (r *Runtime ) newNativeFuncConstructObj (v *Object , construct func (args []Value , proto *Object ) *Object , name unistring .String , proto *Object , length int ) *nativeFuncObject {
f := &nativeFuncObject {
baseFuncObject : baseFuncObject {
baseObject : baseObject {
class : classFunction ,
val : v ,
extensible : true ,
prototype : r .getFunctionPrototype (),
},
},
f : r .constructToCall (construct , proto ),
construct : r .wrapNativeConstruct (construct , v , proto ),
}
f .init (name , intToValue (int64 (length )))
if proto != nil {
f ._putProp ("prototype" , proto , false , false , false )
}
return f
}
func (r *Runtime ) newNativeFuncConstruct (v *Object , construct func (args []Value , proto *Object ) *Object , name unistring .String , prototype *Object , length int64 ) *Object {
return r .newNativeFuncConstructProto (v , construct , name , prototype , r .getFunctionPrototype (), length )
}
func (r *Runtime ) newNativeFuncConstructProto (v *Object , construct func (args []Value , proto *Object ) *Object , name unistring .String , prototype , proto *Object , length int64 ) *Object {
f := &nativeFuncObject {}
f .class = classFunction
f .val = v
f .extensible = true
v .self = f
f .prototype = proto
f .f = r .constructToCall (construct , prototype )
f .construct = r .wrapNativeConstruct (construct , v , prototype )
f .init (name , intToValue (length ))
if prototype != nil {
f ._putProp ("prototype" , prototype , false , false , false )
}
return v
}
func (r *Runtime ) newPrimitiveObject (value Value , proto *Object , class string ) *Object {
v := &Object {runtime : r }
o := &primitiveValueObject {}
o .class = class
o .val = v
o .extensible = true
v .self = o
o .prototype = proto
o .pValue = value
o .init ()
return v
}
func (r *Runtime ) builtin_Number (call FunctionCall ) Value {
if len (call .Arguments ) > 0 {
switch t := call .Arguments [0 ].(type ) {
case *Object :
primValue := t .toPrimitiveNumber ()
if bigint , ok := primValue .(*valueBigInt ); ok {
return intToValue ((*big .Int )(bigint ).Int64 ())
}
return primValue .ToNumber ()
case *valueBigInt :
return intToValue ((*big .Int )(t ).Int64 ())
default :
return t .ToNumber ()
}
} else {
return valueInt (0 )
}
}
func (r *Runtime ) builtin_newNumber (args []Value , proto *Object ) *Object {
var v Value
if len (args ) > 0 {
switch t := args [0 ].(type ) {
case *Object :
primValue := t .toPrimitiveNumber ()
if bigint , ok := primValue .(*valueBigInt ); ok {
v = intToValue ((*big .Int )(bigint ).Int64 ())
} else {
v = primValue .ToNumber ()
}
case *valueBigInt :
v = intToValue ((*big .Int )(t ).Int64 ())
default :
v = t .ToNumber ()
}
} else {
v = intToValue (0 )
}
return r .newPrimitiveObject (v , proto , classNumber )
}
func (r *Runtime ) builtin_Boolean (call FunctionCall ) Value {
if len (call .Arguments ) > 0 {
if call .Arguments [0 ].ToBoolean () {
return valueTrue
} else {
return valueFalse
}
} else {
return valueFalse
}
}
func (r *Runtime ) builtin_newBoolean (args []Value , proto *Object ) *Object {
var v Value
if len (args ) > 0 {
if args [0 ].ToBoolean () {
v = valueTrue
} else {
v = valueFalse
}
} else {
v = valueFalse
}
return r .newPrimitiveObject (v , proto , classBoolean )
}
func (r *Runtime ) builtin_new (construct *Object , args []Value ) *Object {
return r .toConstructor (construct )(args , construct )
}
func (r *Runtime ) builtin_thrower (call FunctionCall ) Value {
obj := r .toObject (call .This )
strict := true
switch fn := obj .self .(type ) {
case *funcObject :
strict = fn .strict
}
r .typeErrorResult (strict , "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them" )
return nil
}
func (r *Runtime ) eval (srcVal String , direct , strict bool ) Value {
src := escapeInvalidUtf16 (srcVal )
vm := r .vm
inGlobal := true
if direct {
for s := vm .stash ; s != nil ; s = s .outer {
if s .isVariable () {
inGlobal = false
break
}
}
}
vm .pushCtx ()
funcObj := _undefined
if !direct {
vm .stash = &r .global .stash
vm .privEnv = nil
} else {
if sb := vm .sb ; sb > 0 {
funcObj = vm .stack [sb -1 ]
}
}
p , err := r .compile ("<eval>" , src , strict , inGlobal , r .vm )
if err != nil {
panic (err )
}
vm .prg = p
vm .pc = 0
vm .args = 0
vm .result = _undefined
vm .push (funcObj )
vm .sb = vm .sp
vm .push (nil )
ex := vm .runTry ()
retval := vm .result
vm .popCtx ()
if ex != nil {
panic (ex )
}
vm .sp -= 2
return retval
}
func (r *Runtime ) builtin_eval (call FunctionCall ) Value {
if len (call .Arguments ) == 0 {
return _undefined
}
if str , ok := call .Arguments [0 ].(String ); ok {
return r .eval (str , false , false )
}
return call .Arguments [0 ]
}
func (r *Runtime ) constructToCall (construct func (args []Value , proto *Object ) *Object , proto *Object ) func (call FunctionCall ) Value {
return func (call FunctionCall ) Value {
return construct (call .Arguments , proto )
}
}
func (r *Runtime ) wrapNativeConstruct (c func (args []Value , proto *Object ) *Object , ctorObj , defProto *Object ) func (args []Value , newTarget *Object ) *Object {
if c == nil {
return nil
}
return func (args []Value , newTarget *Object ) *Object {
var proto *Object
if newTarget != nil {
proto = r .getPrototypeFromCtor (newTarget , ctorObj , defProto )
} else {
proto = defProto
}
return c (args , proto )
}
}
func (r *Runtime ) toCallable (v Value ) func (FunctionCall ) Value {
if call , ok := r .toObject (v ).self .assertCallable (); ok {
return call
}
r .typeErrorResult (true , "Value is not callable: %s" , v .toString ())
return nil
}
func (r *Runtime ) checkObjectCoercible (v Value ) {
switch v .(type ) {
case valueUndefined , valueNull :
r .typeErrorResult (true , "Value is not object coercible" )
}
}
func toInt8(v Value ) int8 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return int8 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return int8 (int64 (f ))
}
}
return 0
}
func toUint8(v Value ) uint8 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return uint8 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return uint8 (int64 (f ))
}
}
return 0
}
func toUint8Clamp(v Value ) uint8 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
if i < 0 {
return 0
}
if i <= 255 {
return uint8 (i )
}
return 255
}
if num , ok := v .(valueFloat ); ok {
num := float64 (num )
if !math .IsNaN (num ) {
if num < 0 {
return 0
}
if num > 255 {
return 255
}
f := math .Floor (num )
f1 := f + 0.5
if f1 < num {
return uint8 (f + 1 )
}
if f1 > num {
return uint8 (f )
}
r := uint8 (f )
if r &1 != 0 {
return r + 1
}
return r
}
}
return 0
}
func toInt16(v Value ) int16 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return int16 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return int16 (int64 (f ))
}
}
return 0
}
func toUint16(v Value ) uint16 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return uint16 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return uint16 (int64 (f ))
}
}
return 0
}
func toInt32(v Value ) int32 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return int32 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return int32 (int64 (f ))
}
}
return 0
}
func toUint32(v Value ) uint32 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return uint32 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return uint32 (int64 (f ))
}
}
return 0
}
func toInt64(v Value ) int64 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return int64 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return int64 (f )
}
}
return 0
}
func toUint64(v Value ) uint64 {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return uint64 (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return uint64 (int64 (f ))
}
}
return 0
}
func toInt(v Value ) int {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return int (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return int (f )
}
}
return 0
}
func toUint(v Value ) uint {
v = v .ToNumber ()
if i , ok := v .(valueInt ); ok {
return uint (i )
}
if f , ok := v .(valueFloat ); ok {
f := float64 (f )
if !math .IsNaN (f ) && !math .IsInf (f , 0 ) {
return uint (int64 (f ))
}
}
return 0
}
func toFloat32(v Value ) float32 {
return float32 (v .ToFloat ())
}
func toLength(v Value ) int64 {
if v == nil {
return 0
}
i := v .ToInteger ()
if i < 0 {
return 0
}
if i >= maxInt {
return maxInt - 1
}
return i
}
func (r *Runtime ) toLengthUint32 (v Value ) uint32 {
var intVal int64
repeat :
switch num := v .(type ) {
case valueInt :
intVal = int64 (num )
case valueFloat :
if v != _negativeZero {
if i , ok := floatToInt (float64 (num )); ok {
intVal = i
} else {
goto fail
}
}
case String :
v = num .ToNumber ()
goto repeat
default :
n2 := toUint32 (v )
n1 := v .ToNumber ()
if f , ok := n1 .(valueFloat ); ok {
f := float64 (f )
if f != 0 || !math .Signbit (f ) {
goto fail
}
}
if n1 .ToInteger () != int64 (n2 ) {
goto fail
}
return n2
}
if intVal >= 0 && intVal <= math .MaxUint32 {
return uint32 (intVal )
}
fail :
panic (r .newError (r .getRangeError (), "Invalid array length" ))
}
func toIntStrict(i int64 ) int {
if bits .UintSize == 32 {
if i > math .MaxInt32 || i < math .MinInt32 {
panic (rangeError ("Integer value overflows 32-bit int" ))
}
}
return int (i )
}
func toIntClamp(i int64 ) int {
if bits .UintSize == 32 {
if i > math .MaxInt32 {
return math .MaxInt32
}
if i < math .MinInt32 {
return math .MinInt32
}
}
return int (i )
}
func (r *Runtime ) toIndex (v Value ) int {
num := v .ToInteger ()
if num >= 0 && num < maxInt {
if bits .UintSize == 32 && num >= math .MaxInt32 {
panic (r .newError (r .getRangeError (), "Index %s overflows int" , v .String ()))
}
return int (num )
}
panic (r .newError (r .getRangeError (), "Invalid index %s" , v .String ()))
}
func (r *Runtime ) toBoolean (b bool ) Value {
if b {
return valueTrue
} else {
return valueFalse
}
}
func New () *Runtime {
r := &Runtime {}
r .init ()
return r
}
func Compile (name , src string , strict bool ) (*Program , error ) {
return compile (name , src , strict , true , nil )
}
func CompileAST (prg *js_ast .Program , strict bool ) (*Program , error ) {
return compileAST (prg , strict , true , nil )
}
func MustCompile (name , src string , strict bool ) *Program {
prg , err := Compile (name , src , strict )
if err != nil {
panic (err )
}
return prg
}
func Parse (name , src string , options ...parser .Option ) (prg *js_ast .Program , err error ) {
prg , err1 := parser .ParseFile (nil , name , src , 0 , options ...)
if err1 != nil {
err = &CompilerSyntaxError {
CompilerError : CompilerError {
Message : err1 .Error(),
},
}
}
return
}
func compile(name , src string , strict , inGlobal bool , evalVm *vm , parserOptions ...parser .Option ) (p *Program , err error ) {
prg , err := Parse (name , src , parserOptions ...)
if err != nil {
return
}
return compileAST (prg , strict , inGlobal , evalVm )
}
func compileAST(prg *js_ast .Program , strict , inGlobal bool , evalVm *vm ) (p *Program , err error ) {
c := newCompiler ()
defer func () {
if x := recover (); x != nil {
p = nil
switch x1 := x .(type ) {
case *CompilerSyntaxError :
err = x1
default :
panic (x )
}
}
}()
c .compile (prg , strict , inGlobal , evalVm )
p = c .p
return
}
func (r *Runtime ) compile (name , src string , strict , inGlobal bool , evalVm *vm ) (p *Program , err error ) {
p , err = compile (name , src , strict , inGlobal , evalVm , r .parserOptions ...)
if err != nil {
switch x1 := err .(type ) {
case *CompilerSyntaxError :
err = &Exception {
val : r .builtin_new (r .getSyntaxError (), []Value {newStringValue (x1 .Error ())}),
}
case *CompilerReferenceError :
err = &Exception {
val : r .newError (r .getReferenceError (), x1 .Message ),
}
}
}
return
}
func (r *Runtime ) RunString (str string ) (Value , error ) {
return r .RunScript ("" , str )
}
func (r *Runtime ) RunScript (name , src string ) (Value , error ) {
p , err := r .compile (name , src , false , true , nil )
if err != nil {
return nil , err
}
return r .RunProgram (p )
}
func isUncatchableException(e error ) bool {
for ; e != nil ; e = errors .Unwrap (e ) {
if _ , ok := e .(uncatchableException ); ok {
return true
}
}
return false
}
func asUncatchableException(v interface {}) error {
switch v := v .(type ) {
case uncatchableException :
return v
case error :
if isUncatchableException (v ) {
return v
}
}
return nil
}
func (r *Runtime ) RunProgram (p *Program ) (result Value , err error ) {
vm := r .vm
recursive := len (vm .callStack ) > 0
defer func () {
if recursive {
vm .sp -= 2
vm .popCtx ()
} else {
vm .callStack = vm .callStack [:len (vm .callStack )-1 ]
}
if x := recover (); x != nil {
if ex := asUncatchableException (x ); ex != nil {
err = ex
if len (vm .callStack ) == 0 {
r .leaveAbrupt ()
}
} else {
panic (x )
}
}
}()
if recursive {
vm .pushCtx ()
vm .stash = &r .global .stash
vm .privEnv = nil
vm .newTarget = nil
vm .args = 0
sp := vm .sp
vm .stack .expand (sp + 1 )
vm .stack [sp ] = _undefined
vm .stack [sp +1 ] = nil
vm .sb = sp + 1
vm .sp = sp + 2
} else {
vm .callStack = append (vm .callStack , context {})
}
vm .prg = p
vm .pc = 0
vm .result = _undefined
ex := vm .runTry ()
if ex == nil {
result = r .vm .result
} else {
err = ex
}
if recursive {
vm .clearStack ()
} else {
vm .prg = nil
vm .sb = -1
r .leave ()
}
return
}
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 )
}
func (r *Runtime ) Interrupt (v interface {}) {
r .vm .Interrupt (v )
}
func (r *Runtime ) ClearInterrupt () {
r .vm .ClearInterrupt ()
}
func (r *Runtime ) ToValue (i interface {}) Value {
return r .toValue (i , reflect .Value {})
}
func (r *Runtime ) toValue (i interface {}, origValue reflect .Value ) Value {
switch i := i .(type ) {
case nil :
return _null
case *Object :
if i == nil || i .self == nil {
return _null
}
if i .runtime != nil && i .runtime != r {
panic (r .NewTypeError ("Illegal runtime transition of an Object" ))
}
return i
case valueContainer :
return i .toValue (r )
case Value :
return i
case string :
if len (i ) <= 16 {
if u := unistring .Scan (i ); u != nil {
return &importedString {s : i , u : u , scanned : true }
}
return asciiString (i )
}
return &importedString {s : i }
case bool :
if i {
return valueTrue
} else {
return valueFalse
}
case func (FunctionCall ) Value :
name := unistring .NewFromString (runtime .FuncForPC (reflect .ValueOf (i ).Pointer ()).Name ())
return r .newNativeFunc (i , name , 0 )
case func (FunctionCall , *Runtime ) Value :
name := unistring .NewFromString (runtime .FuncForPC (reflect .ValueOf (i ).Pointer ()).Name ())
return r .newNativeFunc (func (call FunctionCall ) Value {
return i (call , r )
}, name , 0 )
case func (ConstructorCall ) *Object :
name := unistring .NewFromString (runtime .FuncForPC (reflect .ValueOf (i ).Pointer ()).Name ())
return r .newNativeConstructor (i , name , 0 )
case func (ConstructorCall , *Runtime ) *Object :
name := unistring .NewFromString (runtime .FuncForPC (reflect .ValueOf (i ).Pointer ()).Name ())
return r .newNativeConstructor (func (call ConstructorCall ) *Object {
return i (call , r )
}, name , 0 )
case int :
return intToValue (int64 (i ))
case int8 :
return intToValue (int64 (i ))
case int16 :
return intToValue (int64 (i ))
case int32 :
return intToValue (int64 (i ))
case int64 :
return intToValue (i )
case uint :
if uint64 (i ) <= math .MaxInt64 {
return intToValue (int64 (i ))
} else {
return floatToValue (float64 (i ))
}
case uint8 :
return intToValue (int64 (i ))
case uint16 :
return intToValue (int64 (i ))
case uint32 :
return intToValue (int64 (i ))
case uint64 :
if i <= math .MaxInt64 {
return intToValue (int64 (i ))
}
return floatToValue (float64 (i ))
case float32 :
return floatToValue (float64 (i ))
case float64 :
return floatToValue (i )
case *big .Int :
return (*valueBigInt )(new (big .Int ).Set (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 {}:
return r .newObjectGoSlice (&i , false ).val
case *[]interface {}:
if i == nil {
return _null
}
return r .newObjectGoSlice (i , true ).val
}
if !origValue .IsValid () {
origValue = reflect .ValueOf (i )
}
value := origValue
for value .Kind () == reflect .Ptr {
value = value .Elem ()
}
if !value .IsValid () {
return _null
}
switch value .Kind () {
case reflect .Map :
if value .Type ().NumMethod () == 0 {
switch value .Type ().Key ().Kind () {
case reflect .String , reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
reflect .Float64 , reflect .Float32 :
obj := &Object {runtime : r }
m := &objectGoMapReflect {
objectGoReflect : objectGoReflect {
baseObject : baseObject {
val : obj ,
extensible : true ,
},
origValue : origValue ,
fieldsValue : value ,
},
}
m .init ()
obj .self = m
return obj
}
}
case reflect .Array :
obj := &Object {runtime : r }
a := &objectGoArrayReflect {
objectGoReflect : objectGoReflect {
baseObject : baseObject {
val : obj ,
},
origValue : origValue ,
fieldsValue : value ,
},
}
a .init ()
obj .self = a
return obj
case reflect .Slice :
obj := &Object {runtime : r }
a := &objectGoSliceReflect {
objectGoArrayReflect : objectGoArrayReflect {
objectGoReflect : objectGoReflect {
baseObject : baseObject {
val : obj ,
},
origValue : origValue ,
fieldsValue : value ,
},
},
}
a .init ()
obj .self = a
return obj
case reflect .Func :
return r .newWrappedFunc (value )
}
obj := &Object {runtime : r }
o := &objectGoReflect {
baseObject : baseObject {
val : obj ,
},
origValue : origValue ,
fieldsValue : value ,
}
obj .self = o
o .init ()
return obj
}
func (r *Runtime ) wrapReflectFunc (value reflect .Value ) func (FunctionCall ) Value {
return func (call FunctionCall ) Value {
typ := value .Type ()
nargs := typ .NumIn ()
var in []reflect .Value
if l := len (call .Arguments ); l < nargs {
n := nargs
if typ .IsVariadic () {
n --
}
in = make ([]reflect .Value , n )
for i := l ; i < n ; i ++ {
in [i ] = reflect .Zero (typ .In (i ))
}
} else {
if l > nargs && !typ .IsVariadic () {
l = nargs
}
in = make ([]reflect .Value , l )
}
for i , a := range call .Arguments {
var t reflect .Type
n := i
if n >= nargs -1 && typ .IsVariadic () {
if n > nargs -1 {
n = nargs - 1
}
t = typ .In (n ).Elem ()
} else if n > nargs -1 {
break
} else {
t = typ .In (n )
}
v := reflect .New (t ).Elem ()
err := r .toReflectValue (a , v , &objectExportCtx {})
if err != nil {
panic (r .NewTypeError ("could not convert function call parameter %d: %v" , i , err ))
}
in [i ] = v
}
out := value .Call (in )
if len (out ) == 0 {
return _undefined
}
if last := out [len (out )-1 ]; last .Type () == reflectTypeError {
if !last .IsNil () {
err := last .Interface ().(error )
if _ , ok := err .(*Exception ); ok {
panic (err )
}
if isUncatchableException (err ) {
panic (err )
}
panic (r .NewGoError (err ))
}
out = out [:len (out )-1 ]
}
switch len (out ) {
case 0 :
return _undefined
case 1 :
return r .ToValue (out [0 ].Interface ())
default :
s := make ([]interface {}, len (out ))
for i , v := range out {
s [i ] = v .Interface ()
}
return r .ToValue (s )
}
}
}
func (r *Runtime ) toReflectValue (v Value , dst reflect .Value , ctx *objectExportCtx ) error {
typ := dst .Type ()
if typ == typeValue {
dst .Set (reflect .ValueOf (v ))
return nil
}
if typ == typeObject {
if obj , ok := v .(*Object ); ok {
dst .Set (reflect .ValueOf (obj ))
return nil
}
}
if typ == typeCallable {
if fn , ok := AssertFunction (v ); ok {
dst .Set (reflect .ValueOf (fn ))
return nil
}
}
et := v .ExportType ()
if et == nil || et == reflectTypeNil {
dst .Set (reflect .Zero (typ ))
return nil
}
kind := typ .Kind ()
for i := 0 ; ; i ++ {
if et .AssignableTo (typ ) {
ev := reflect .ValueOf (exportValue (v , ctx ))
for ; i > 0 ; i -- {
ev = ev .Elem ()
}
dst .Set (ev )
return nil
}
expKind := et .Kind ()
if expKind == kind && et .ConvertibleTo (typ ) || expKind == reflect .String && typ == typeBytes {
ev := reflect .ValueOf (exportValue (v , ctx ))
for ; i > 0 ; i -- {
ev = ev .Elem ()
}
dst .Set (ev .Convert (typ ))
return nil
}
if expKind == reflect .Ptr {
et = et .Elem ()
} else {
break
}
}
if typ == typeTime {
if obj , ok := v .(*Object ); ok {
if d , ok := obj .self .(*dateObject ); ok {
dst .Set (reflect .ValueOf (d .time ()))
return nil
}
}
if et .Kind () == reflect .String {
tme , ok := dateParse (v .String ())
if !ok {
return fmt .Errorf ("could not convert string %v to %v" , v , typ )
}
dst .Set (reflect .ValueOf (tme ))
return nil
}
}
switch kind {
case reflect .String :
dst .Set (reflect .ValueOf (v .String ()).Convert (typ ))
return nil
case reflect .Bool :
dst .Set (reflect .ValueOf (v .ToBoolean ()).Convert (typ ))
return nil
case reflect .Int :
dst .Set (reflect .ValueOf (toInt (v )).Convert (typ ))
return nil
case reflect .Int64 :
dst .Set (reflect .ValueOf (toInt64 (v )).Convert (typ ))
return nil
case reflect .Int32 :
dst .Set (reflect .ValueOf (toInt32 (v )).Convert (typ ))
return nil
case reflect .Int16 :
dst .Set (reflect .ValueOf (toInt16 (v )).Convert (typ ))
return nil
case reflect .Int8 :
dst .Set (reflect .ValueOf (toInt8 (v )).Convert (typ ))
return nil
case reflect .Uint :
dst .Set (reflect .ValueOf (toUint (v )).Convert (typ ))
return nil
case reflect .Uint64 :
dst .Set (reflect .ValueOf (toUint64 (v )).Convert (typ ))
return nil
case reflect .Uint32 :
dst .Set (reflect .ValueOf (toUint32 (v )).Convert (typ ))
return nil
case reflect .Uint16 :
dst .Set (reflect .ValueOf (toUint16 (v )).Convert (typ ))
return nil
case reflect .Uint8 :
dst .Set (reflect .ValueOf (toUint8 (v )).Convert (typ ))
return nil
case reflect .Float64 :
dst .Set (reflect .ValueOf (v .ToFloat ()).Convert (typ ))
return nil
case reflect .Float32 :
dst .Set (reflect .ValueOf (toFloat32 (v )).Convert (typ ))
return nil
case reflect .Slice , reflect .Array :
if o , ok := v .(*Object ); ok {
if v , exists := ctx .getTyped (o , typ ); exists {
dst .Set (reflect .ValueOf (v ))
return nil
}
return o .self .exportToArrayOrSlice (dst , typ , ctx )
}
case reflect .Map :
if o , ok := v .(*Object ); ok {
if v , exists := ctx .getTyped (o , typ ); exists {
dst .Set (reflect .ValueOf (v ))
return nil
}
return o .self .exportToMap (dst , typ , ctx )
}
case reflect .Struct :
if o , ok := v .(*Object ); ok {
t := reflect .PtrTo (typ )
if v , exists := ctx .getTyped (o , t ); exists {
dst .Set (reflect .ValueOf (v ).Elem ())
return nil
}
s := dst
ctx .putTyped (o , t , s .Addr ().Interface ())
for i := 0 ; i < typ .NumField (); i ++ {
field := typ .Field (i )
if ast .IsExported (field .Name ) {
name := field .Name
if r .fieldNameMapper != nil {
name = r .fieldNameMapper .FieldName (typ , field )
}
var v Value
if field .Anonymous {
v = o
} else {
v = o .self .getStr (unistring .NewFromString (name ), nil )
}
if v != nil {
err := r .toReflectValue (v , s .Field (i ), ctx )
if err != nil {
return fmt .Errorf ("could not convert struct value %v to %v for field %s: %w" , v , field .Type , field .Name , err )
}
}
}
}
return nil
}
case reflect .Func :
if fn , ok := AssertFunction (v ); ok {
dst .Set (reflect .MakeFunc (typ , r .wrapJSFunc (fn , typ )))
return nil
}
case reflect .Ptr :
if o , ok := v .(*Object ); ok {
if v , exists := ctx .getTyped (o , typ ); exists {
dst .Set (reflect .ValueOf (v ))
return nil
}
}
if dst .IsNil () {
dst .Set (reflect .New (typ .Elem ()))
}
return r .toReflectValue (v , dst .Elem (), ctx )
}
return fmt .Errorf ("could not convert %v to %v" , v , typ )
}
func (r *Runtime ) wrapJSFunc (fn Callable , typ reflect .Type ) func (args []reflect .Value ) (results []reflect .Value ) {
return func (args []reflect .Value ) (results []reflect .Value ) {
var jsArgs []Value
if len (args ) > 0 {
if typ .IsVariadic () {
varArg := args [len (args )-1 ]
args = args [:len (args )-1 ]
jsArgs = make ([]Value , 0 , len (args )+varArg .Len ())
for _ , arg := range args {
jsArgs = append (jsArgs , r .ToValue (arg .Interface ()))
}
for i := 0 ; i < varArg .Len (); i ++ {
jsArgs = append (jsArgs , r .ToValue (varArg .Index (i ).Interface ()))
}
} else {
jsArgs = make ([]Value , len (args ))
for i , arg := range args {
jsArgs [i ] = r .ToValue (arg .Interface ())
}
}
}
numOut := typ .NumOut ()
results = make ([]reflect .Value , numOut )
res , err := fn (_undefined , jsArgs ...)
if err == nil {
if numOut > 0 {
v := reflect .New (typ .Out (0 )).Elem ()
err = r .toReflectValue (res , v , &objectExportCtx {})
if err == nil {
results [0 ] = v
}
}
}
if err != nil {
if numOut > 0 && typ .Out (numOut -1 ) == reflectTypeError {
if ex , ok := err .(*Exception ); ok {
if exo , ok := ex .val .(*Object ); ok {
if v := exo .self .getStr ("value" , nil ); v != nil {
if v .ExportType ().AssignableTo (reflectTypeError ) {
err = v .Export ().(error )
}
}
}
}
results [numOut -1 ] = reflect .ValueOf (err ).Convert (typ .Out (numOut - 1 ))
} else {
panic (err )
}
}
for i , v := range results {
if !v .IsValid () {
results [i ] = reflect .Zero (typ .Out (i ))
}
}
return
}
}
func (r *Runtime ) ExportTo (v Value , target interface {}) error {
tval := reflect .ValueOf (target )
if tval .Kind () != reflect .Ptr || tval .IsNil () {
return errors .New ("target must be a non-nil pointer" )
}
return r .toReflectValue (v , tval .Elem (), &objectExportCtx {})
}
func (r *Runtime ) GlobalObject () *Object {
return r .globalObject
}
func (r *Runtime ) Set (name string , value interface {}) error {
return r .try (func () {
name := unistring .NewFromString (name )
v := r .ToValue (value )
if ref := r .global .stash .getRefByName (name , false ); ref != nil {
ref .set (v )
} else {
r .globalObject .self .setOwnStr (name , v , true )
}
})
}
func (r *Runtime ) Get (name string ) Value {
n := unistring .NewFromString (name )
if v , exists := r .global .stash .getByName (n ); exists {
return v
} else {
return r .globalObject .self .getStr (n , nil )
}
}
func (r *Runtime ) SetRandSource (source RandSource ) {
r .rand = source
}
func (r *Runtime ) SetTimeSource (now Now ) {
r .now = now
}
func (r *Runtime ) SetParserOptions (opts ...parser .Option ) {
r .parserOptions = opts
}
func (r *Runtime ) SetMaxCallStackSize (size int ) {
r .vm .maxCallStackSize = size
}
func (r *Runtime ) New (construct Value , args ...Value ) (o *Object , err error ) {
err = r .try (func () {
o = r .builtin_new (r .toObject (construct ), args )
})
return
}
type Callable func (this Value , args ...Value ) (Value , error )
func AssertFunction (v Value ) (Callable , bool ) {
if obj , ok := v .(*Object ); ok {
if f , ok := obj .self .assertCallable (); ok {
return func (this Value , args ...Value ) (ret Value , err error ) {
err = obj .runtime .runWrapped (func () {
ret = f (FunctionCall {
This : this ,
Arguments : args ,
})
})
return
}, true
}
}
return nil , false
}
type Constructor func (newTarget *Object , args ...Value ) (*Object , error )
func AssertConstructor (v Value ) (Constructor , bool ) {
if obj , ok := v .(*Object ); ok {
if ctor := obj .self .assertConstructor (); ctor != nil {
return func (newTarget *Object , args ...Value ) (ret *Object , err error ) {
err = obj .runtime .runWrapped (func () {
ret = ctor (args , newTarget )
})
return
}, true
}
}
return nil , false
}
func (r *Runtime ) runWrapped (f func ()) (err error ) {
defer func () {
if x := recover (); x != nil {
if ex := asUncatchableException (x ); ex != nil {
err = ex
if len (r .vm .callStack ) == 0 {
r .leaveAbrupt ()
}
} else {
panic (x )
}
}
}()
ex := r .vm .try (f )
if ex != nil {
err = ex
}
if len (r .vm .callStack ) == 0 {
r .leave ()
} else {
r .vm .clearStack ()
}
return
}
func IsUndefined (v Value ) bool {
return v == _undefined
}
func IsNull (v Value ) bool {
return v == _null
}
func IsNaN (v Value ) bool {
f , ok := v .(valueFloat )
return ok && math .IsNaN (float64 (f ))
}
func IsInfinity (v Value ) bool {
return v == _positiveInf || v == _negativeInf
}
func Undefined () Value {
return _undefined
}
func Null () Value {
return _null
}
func NaN () Value {
return _NaN
}
func PositiveInf () Value {
return _positiveInf
}
func NegativeInf () Value {
return _negativeInf
}
func tryFunc(f func ()) (ret interface {}) {
defer func () {
ret = recover ()
}()
f ()
return
}
func (r *Runtime ) Try (f func ()) *Exception {
return r .vm .try (f )
}
func (r *Runtime ) try (f func ()) error {
if ex := r .vm .try (f ); ex != nil {
return ex
}
return nil
}
func (r *Runtime ) toObject (v Value , args ...interface {}) *Object {
if obj , ok := v .(*Object ); ok {
return obj
}
if len (args ) > 0 {
panic (r .NewTypeError (args ...))
} else {
var s string
if v == nil {
s = "undefined"
} else {
s = v .String ()
}
panic (r .NewTypeError ("Value is not an object: %s" , s ))
}
}
func (r *Runtime ) speciesConstructor (o , defaultConstructor *Object ) func (args []Value , newTarget *Object ) *Object {
c := o .self .getStr ("constructor" , nil )
if c != nil && c != _undefined {
c = r .toObject (c ).self .getSym (SymSpecies , nil )
}
if c == nil || c == _undefined || c == _null {
c = defaultConstructor
}
return r .toConstructor (c )
}
func (r *Runtime ) speciesConstructorObj (o , defaultConstructor *Object ) *Object {
c := o .self .getStr ("constructor" , nil )
if c != nil && c != _undefined {
c = r .toObject (c ).self .getSym (SymSpecies , nil )
}
if c == nil || c == _undefined || c == _null {
return defaultConstructor
}
obj := r .toObject (c )
if obj .self .assertConstructor () == nil {
panic (r .NewTypeError ("Value is not a constructor" ))
}
return obj
}
func (r *Runtime ) returnThis (call FunctionCall ) Value {
return call .This
}
func createDataProperty(o *Object , p Value , v Value ) {
o .defineOwnProperty (p , PropertyDescriptor {
Writable : FLAG_TRUE ,
Enumerable : FLAG_TRUE ,
Configurable : FLAG_TRUE ,
Value : v ,
}, false )
}
func createDataPropertyOrThrow(o *Object , p Value , v Value ) {
o .defineOwnProperty (p , PropertyDescriptor {
Writable : FLAG_TRUE ,
Enumerable : FLAG_TRUE ,
Configurable : FLAG_TRUE ,
Value : v ,
}, true )
}
func toPropertyKey(key Value ) Value {
return key .ToString ()
}
func (r *Runtime ) getVStr (v Value , p unistring .String ) Value {
o := v .ToObject (r )
return o .self .getStr (p , v )
}
func (r *Runtime ) getV (v Value , p Value ) Value {
o := v .ToObject (r )
return o .get (p , v )
}
type iteratorRecord struct {
iterator *Object
next func (FunctionCall ) Value
}
func (r *Runtime ) getIterator (obj Value , method func (FunctionCall ) Value ) *iteratorRecord {
if method == nil {
method = toMethod (r .getV (obj , SymIterator ))
if method == nil {
panic (r .NewTypeError ("object is not iterable" ))
}
}
iter := r .toObject (method (FunctionCall {
This : obj ,
}))
var next func (FunctionCall ) Value
if obj , ok := iter .self .getStr ("next" , nil ).(*Object ); ok {
if call , ok := obj .self .assertCallable (); ok {
next = call
}
}
return &iteratorRecord {
iterator : iter ,
next : next ,
}
}
func iteratorComplete(iterResult *Object ) bool {
return nilSafe (iterResult .self .getStr ("done" , nil )).ToBoolean ()
}
func iteratorValue(iterResult *Object ) Value {
return nilSafe (iterResult .self .getStr ("value" , nil ))
}
func (ir *iteratorRecord ) iterate (step func (Value )) {
r := ir .iterator .runtime
for {
if ir .next == nil {
panic (r .NewTypeError ("iterator.next is missing or not a function" ))
}
res := r .toObject (ir .next (FunctionCall {This : ir .iterator }))
if iteratorComplete (res ) {
break
}
value := iteratorValue (res )
ret := tryFunc (func () {
step (value )
})
if ret != nil {
_ = tryFunc (func () {
ir .returnIter ()
})
panic (ret )
}
}
}
func (ir *iteratorRecord ) step () (value Value , ex *Exception ) {
r := ir .iterator .runtime
ex = r .vm .try (func () {
res := r .toObject (ir .next (FunctionCall {This : ir .iterator }))
done := iteratorComplete (res )
if !done {
value = iteratorValue (res )
} else {
ir .close ()
}
})
return
}
func (ir *iteratorRecord ) returnIter () {
if ir .iterator == nil {
return
}
retMethod := toMethod (ir .iterator .self .getStr ("return" , nil ))
if retMethod != nil {
ir .iterator .runtime .toObject (retMethod (FunctionCall {This : ir .iterator }))
}
ir .iterator = nil
ir .next = nil
}
func (ir *iteratorRecord ) close () {
ir .iterator = nil
ir .next = nil
}
func (r *Runtime ) ForOf (iterable Value , step func (curValue Value ) (continueIteration bool )) {
iter := r .getIterator (iterable , nil )
for {
value , ex := iter .step ()
if ex != nil {
panic (ex )
}
if value != nil {
var continueIteration bool
ex := r .vm .try (func () {
continueIteration = step (value )
})
if ex != nil {
iter .returnIter ()
panic (ex )
}
if !continueIteration {
iter .returnIter ()
break
}
} else {
break
}
}
}
func (r *Runtime ) createIterResultObject (value Value , done bool ) Value {
o := r .NewObject ()
o .self .setOwnStr ("value" , value , false )
o .self .setOwnStr ("done" , r .toBoolean (done ), false )
return o
}
func (r *Runtime ) getHash () *maphash .Hash {
if r .hash == nil {
r .hash = &maphash .Hash {}
}
return r .hash
}
func (r *Runtime ) leave () {
var jobs []func ()
for len (r .jobQueue ) > 0 {
jobs , r .jobQueue = r .jobQueue , jobs [:0 ]
for _ , job := range jobs {
job ()
}
}
r .jobQueue = nil
r .vm .stack = nil
}
func (r *Runtime ) leaveAbrupt () {
r .jobQueue = nil
r .ClearInterrupt ()
}
func nilSafe(v Value ) Value {
if v != nil {
return v
}
return _undefined
}
func isArray(object *Object ) bool {
self := object .self
if proxy , ok := self .(*proxyObject ); ok {
if proxy .target == nil {
panic (typeError ("Cannot perform 'IsArray' on a proxy that has been revoked" ))
}
return isArray (proxy .target )
}
switch self .className () {
case classArray :
return true
default :
return false
}
}
func isRegexp(v Value ) bool {
if o , ok := v .(*Object ); ok {
matcher := o .self .getSym (SymMatch , nil )
if matcher != nil && matcher != _undefined {
return matcher .ToBoolean ()
}
_ , reg := o .self .(*regexpObject )
return reg
}
return false
}
func limitCallArgs(call FunctionCall , n int ) FunctionCall {
if len (call .Arguments ) > n {
return FunctionCall {This : call .This , Arguments : call .Arguments [:n ]}
} else {
return call
}
}
func shrinkCap(newSize , oldCap int ) int {
if oldCap > 8 {
if cap := oldCap / 2 ; cap >= newSize {
return cap
}
}
return oldCap
}
func growCap(newSize , oldSize , oldCap int ) int {
doublecap := oldCap + oldCap
if newSize > doublecap {
return newSize
} else {
if oldSize < 1024 {
return doublecap
} else {
cap := oldCap
for 0 < cap && cap < newSize {
cap += cap / 4
}
if cap <= 0 {
return newSize
}
return cap
}
}
}
func (r *Runtime ) genId () (ret uint64 ) {
if r .hash == nil {
h := r .getHash ()
r .idSeq = h .Sum64 ()
}
if r .idSeq == 0 {
r .idSeq = 1
}
ret = r .idSeq
r .idSeq ++
return
}
func (r *Runtime ) setGlobal (name unistring .String , v Value , strict bool ) {
if ref := r .global .stash .getRefByName (name , strict ); ref != nil {
ref .set (v )
} else {
o := r .globalObject .self
if strict {
if o .hasOwnPropertyStr (name ) {
o .setOwnStr (name , v , true )
} else {
r .throwReferenceError (name )
}
} else {
o .setOwnStr (name , v , false )
}
}
}
func (r *Runtime ) trackPromiseRejection (p *Promise , operation PromiseRejectionOperation ) {
if r .promiseRejectionTracker != nil {
r .promiseRejectionTracker (p , operation )
}
}
func (r *Runtime ) callJobCallback (job *jobCallback , this Value , args ...Value ) Value {
return job .callback (FunctionCall {This : this , Arguments : args })
}
func (r *Runtime ) invoke (v Value , p unistring .String , args ...Value ) Value {
o := v .ToObject (r )
return r .toCallable (o .self .getStr (p , nil ))(FunctionCall {This : v , Arguments : args })
}
func (r *Runtime ) iterableToList (iterable Value , method func (FunctionCall ) Value ) []Value {
iter := r .getIterator (iterable , method )
var values []Value
iter .iterate (func (item Value ) {
values = append (values , item )
})
return values
}
func (r *Runtime ) putSpeciesReturnThis (o objectImpl ) {
o ._putSym (SymSpecies , &valueProperty {
getterFunc : r .newNativeFunc (r .returnThis , "get [Symbol.species]" , 0 ),
accessor : true ,
configurable : true ,
})
}
func strToArrayIdx(s unistring .String ) uint32 {
if s == "" {
return math .MaxUint32
}
l := len (s )
if s [0 ] == '0' {
if l == 1 {
return 0
}
return math .MaxUint32
}
var n uint32
if l < 10 {
for i := 0 ; i < len (s ); i ++ {
c := s [i ]
if c < '0' || c > '9' {
return math .MaxUint32
}
n = n *10 + uint32 (c -'0' )
}
return n
}
if l > 10 {
return math .MaxUint32
}
c9 := s [9 ]
if c9 < '0' || c9 > '9' {
return math .MaxUint32
}
for i := 0 ; i < 9 ; i ++ {
c := s [i ]
if c < '0' || c > '9' {
return math .MaxUint32
}
n = n *10 + uint32 (c -'0' )
}
if n >= math .MaxUint32 /10 +1 {
return math .MaxUint32
}
n *= 10
n1 := n + uint32 (c9 -'0' )
if n1 < n {
return math .MaxUint32
}
return n1
}
func strToInt32(s unistring .String ) (int32 , bool ) {
if s == "" {
return -1 , false
}
neg := s [0 ] == '-'
if neg {
s = s [1 :]
}
l := len (s )
if s [0 ] == '0' {
if l == 1 {
return 0 , !neg
}
return -1 , false
}
var n uint32
if l < 10 {
for i := 0 ; i < len (s ); i ++ {
c := s [i ]
if c < '0' || c > '9' {
return -1 , false
}
n = n *10 + uint32 (c -'0' )
}
} else if l > 10 {
return -1 , false
} else {
c9 := s [9 ]
if c9 >= '0' {
if !neg && c9 > '7' || c9 > '8' {
return -1 , false
}
for i := 0 ; i < 9 ; i ++ {
c := s [i ]
if c < '0' || c > '9' {
return -1 , false
}
n = n *10 + uint32 (c -'0' )
}
if n >= math .MaxInt32 /10 +1 {
return 0 , false
}
n = n *10 + uint32 (c9 -'0' )
} else {
return -1 , false
}
}
if neg {
return int32 (-n ), true
}
return int32 (n ), true
}
func strToInt64(s unistring .String ) (int64 , bool ) {
if s == "" {
return -1 , false
}
neg := s [0 ] == '-'
if neg {
s = s [1 :]
}
l := len (s )
if s [0 ] == '0' {
if l == 1 {
return 0 , !neg
}
return -1 , false
}
var n uint64
if l < 19 {
for i := 0 ; i < len (s ); i ++ {
c := s [i ]
if c < '0' || c > '9' {
return -1 , false
}
n = n *10 + uint64 (c -'0' )
}
} else if l > 19 {
return -1 , false
} else {
c18 := s [18 ]
if c18 >= '0' {
if !neg && c18 > '7' || c18 > '8' {
return -1 , false
}
for i := 0 ; i < 18 ; i ++ {
c := s [i ]
if c < '0' || c > '9' {
return -1 , false
}
n = n *10 + uint64 (c -'0' )
}
if n >= math .MaxInt64 /10 +1 {
return 0 , false
}
n = n *10 + uint64 (c18 -'0' )
} else {
return -1 , false
}
}
if neg {
return int64 (-n ), true
}
return int64 (n ), true
}
func strToInt(s unistring .String ) (int , bool ) {
if bits .UintSize == 32 {
n , ok := strToInt32 (s )
return int (n ), ok
}
n , ok := strToInt64 (s )
return int (n ), ok
}
func strToIntNum(s unistring .String ) (int , bool ) {
n , ok := strToInt64 (s )
if n == 0 {
return 0 , ok
}
if ok && n >= -maxInt && n <= maxInt {
if bits .UintSize == 32 {
if n > math .MaxInt32 || n < math .MinInt32 {
return 0 , false
}
}
return int (n ), true
}
str := stringValueFromRaw (s )
if str .ToNumber ().toString ().SameAs (str ) {
return 0 , false
}
return -1 , false
}
func strToGoIdx(s unistring .String ) int {
if n , ok := strToInt (s ); ok {
return n
}
return -1
}
func strToIdx64(s unistring .String ) int64 {
if n , ok := strToInt64 (s ); ok {
return n
}
return -1
}
func assertCallable(v Value ) (func (FunctionCall ) Value , bool ) {
if obj , ok := v .(*Object ); ok {
return obj .self .assertCallable ()
}
return nil , false
}
func (r *Runtime ) InstanceOf (left Value , right *Object ) (res bool ) {
return instanceOfOperator (left , right )
}
func (r *Runtime ) methodProp (f func (FunctionCall ) Value , name unistring .String , nArgs int ) Value {
return valueProp (r .newNativeFunc (f , name , nArgs ), true , false , true )
}
func (r *Runtime ) getPrototypeFromCtor (newTarget , defCtor , defProto *Object ) *Object {
if newTarget == defCtor {
return defProto
}
proto := newTarget .self .getStr ("prototype" , nil )
if obj , ok := proto .(*Object ); ok {
return obj
}
return defProto
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .