218 lines
5.3 KiB
Go
218 lines
5.3 KiB
Go
package otto
|
|
|
|
import (
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
prototypeValueObject = interface{}(nil)
|
|
prototypeValueFunction = nativeFunctionObject{
|
|
call: func(_ FunctionCall) Value {
|
|
return Value{}
|
|
},
|
|
}
|
|
prototypeValueString = stringASCII("")
|
|
// TODO Make this just false?
|
|
prototypeValueBoolean = Value{
|
|
kind: valueBoolean,
|
|
value: false,
|
|
}
|
|
prototypeValueNumber = Value{
|
|
kind: valueNumber,
|
|
value: 0,
|
|
}
|
|
prototypeValueDate = dateObject{
|
|
epoch: 0,
|
|
isNaN: false,
|
|
time: time.Unix(0, 0).UTC(),
|
|
value: Value{
|
|
kind: valueNumber,
|
|
value: 0,
|
|
},
|
|
}
|
|
prototypeValueRegExp = regExpObject{
|
|
regularExpression: nil,
|
|
global: false,
|
|
ignoreCase: false,
|
|
multiline: false,
|
|
source: "",
|
|
flags: "",
|
|
}
|
|
)
|
|
|
|
func newContext() *runtime {
|
|
rt := &runtime{}
|
|
|
|
rt.globalStash = rt.newObjectStash(nil, nil)
|
|
rt.globalObject = rt.globalStash.object
|
|
|
|
rt.newContext()
|
|
|
|
rt.eval = rt.globalObject.property["eval"].value.(Value).value.(*object)
|
|
rt.globalObject.prototype = rt.global.ObjectPrototype
|
|
|
|
return rt
|
|
}
|
|
|
|
func (rt *runtime) newBaseObject() *object {
|
|
return newObject(rt, "")
|
|
}
|
|
|
|
func (rt *runtime) newClassObject(class string) *object {
|
|
return newObject(rt, class)
|
|
}
|
|
|
|
func (rt *runtime) newPrimitiveObject(class string, value Value) *object {
|
|
o := rt.newClassObject(class)
|
|
o.value = value
|
|
return o
|
|
}
|
|
|
|
func (o *object) primitiveValue() Value {
|
|
switch value := o.value.(type) {
|
|
case Value:
|
|
return value
|
|
case stringObjecter:
|
|
return stringValue(value.String())
|
|
}
|
|
return Value{}
|
|
}
|
|
|
|
func (o *object) hasPrimitive() bool { //nolint: unused
|
|
switch o.value.(type) {
|
|
case Value, stringObjecter:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (rt *runtime) newObject() *object {
|
|
o := rt.newClassObject(classObjectName)
|
|
o.prototype = rt.global.ObjectPrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newArray(length uint32) *object {
|
|
o := rt.newArrayObject(length)
|
|
o.prototype = rt.global.ArrayPrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newArrayOf(valueArray []Value) *object {
|
|
o := rt.newArray(uint32(len(valueArray)))
|
|
for index, value := range valueArray {
|
|
if value.isEmpty() {
|
|
continue
|
|
}
|
|
o.defineProperty(strconv.FormatInt(int64(index), 10), value, 0o111, false)
|
|
}
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newString(value Value) *object {
|
|
o := rt.newStringObject(value)
|
|
o.prototype = rt.global.StringPrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newBoolean(value Value) *object {
|
|
o := rt.newBooleanObject(value)
|
|
o.prototype = rt.global.BooleanPrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newNumber(value Value) *object {
|
|
o := rt.newNumberObject(value)
|
|
o.prototype = rt.global.NumberPrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newRegExp(patternValue Value, flagsValue Value) *object {
|
|
pattern := ""
|
|
flags := ""
|
|
if obj := patternValue.object(); obj != nil && obj.class == classRegExpName {
|
|
if flagsValue.IsDefined() {
|
|
panic(rt.panicTypeError("Cannot supply flags when constructing one RegExp from another"))
|
|
}
|
|
regExp := obj.regExpValue()
|
|
pattern = regExp.source
|
|
flags = regExp.flags
|
|
} else {
|
|
if patternValue.IsDefined() {
|
|
pattern = patternValue.string()
|
|
}
|
|
if flagsValue.IsDefined() {
|
|
flags = flagsValue.string()
|
|
}
|
|
}
|
|
|
|
return rt.newRegExpDirect(pattern, flags)
|
|
}
|
|
|
|
func (rt *runtime) newRegExpDirect(pattern string, flags string) *object {
|
|
o := rt.newRegExpObject(pattern, flags)
|
|
o.prototype = rt.global.RegExpPrototype
|
|
return o
|
|
}
|
|
|
|
// TODO Should (probably) be one argument, right? This is redundant.
|
|
func (rt *runtime) newDate(epoch float64) *object {
|
|
o := rt.newDateObject(epoch)
|
|
o.prototype = rt.global.DatePrototype
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newError(name string, message Value, stackFramesToPop int) *object {
|
|
switch name {
|
|
case "EvalError":
|
|
return rt.newEvalError(message)
|
|
case "TypeError":
|
|
return rt.newTypeError(message)
|
|
case "RangeError":
|
|
return rt.newRangeError(message)
|
|
case "ReferenceError":
|
|
return rt.newReferenceError(message)
|
|
case "SyntaxError":
|
|
return rt.newSyntaxError(message)
|
|
case "URIError":
|
|
return rt.newURIError(message)
|
|
}
|
|
|
|
obj := rt.newErrorObject(name, message, stackFramesToPop)
|
|
obj.prototype = rt.global.ErrorPrototype
|
|
if name != "" {
|
|
obj.defineProperty("name", stringValue(name), 0o111, false)
|
|
}
|
|
return obj
|
|
}
|
|
|
|
func (rt *runtime) newNativeFunction(name, file string, line int, fn nativeFunction) *object {
|
|
o := rt.newNativeFunctionObject(name, file, line, fn, 0)
|
|
o.prototype = rt.global.FunctionPrototype
|
|
prototype := rt.newObject()
|
|
o.defineProperty("prototype", objectValue(prototype), 0o100, false)
|
|
prototype.defineProperty("constructor", objectValue(o), 0o100, false)
|
|
return o
|
|
}
|
|
|
|
func (rt *runtime) newNodeFunction(node *nodeFunctionLiteral, scopeEnvironment stasher) *object {
|
|
// TODO Implement 13.2 fully
|
|
o := rt.newNodeFunctionObject(node, scopeEnvironment)
|
|
o.prototype = rt.global.FunctionPrototype
|
|
prototype := rt.newObject()
|
|
o.defineProperty("prototype", objectValue(prototype), 0o100, false)
|
|
prototype.defineProperty("constructor", objectValue(o), 0o101, false)
|
|
return o
|
|
}
|
|
|
|
// FIXME Only in one place...
|
|
func (rt *runtime) newBoundFunction(target *object, this Value, argumentList []Value) *object {
|
|
o := rt.newBoundFunctionObject(target, this, argumentList)
|
|
o.prototype = rt.global.FunctionPrototype
|
|
prototype := rt.newObject()
|
|
o.defineProperty("prototype", objectValue(prototype), 0o100, false)
|
|
prototype.defineProperty("constructor", objectValue(o), 0o100, false)
|
|
return o
|
|
}
|