1
0
mirror of https://github.com/robertkrimen/otto synced 2025-09-28 18:45:22 +08:00
otto/global.go
2012-10-07 19:16:58 -07:00

803 lines
21 KiB
Go

package otto
import (
"math"
time_ "time"
)
type _globalCallFunction _nativeFunction
type _globalConstructFunction _constructFunction
func (self *_runtime) newGlobalFunction(
length int,
callFunction _globalCallFunction,
constructFunction _globalConstructFunction,
prototype *_object,
nameAndValue... interface{}) *_object {
//
// TODO We're overwriting the prototype of newNativeFunction with this one,
// what is going on?
target := self.newNativeFunction(_nativeFunction(callFunction), length)
target.Function.Construct = _constructFunction(constructFunction)
target.define(_propertyMode(0), "prototype", toValue(prototype))
nameAndValue = append(
[]interface{}{
_functionSignature("builtin"),
_propertyMode(propertyModeWrite | propertyModeConfigure),
"constructor", toValue(target),
},
nameAndValue...,
)
// This actually may be slower than Define
// Benchmark?
prototype.define(nameAndValue...)
return target
}
func (self *_runtime) newGlobalObject(
class string,
nameAndValue... interface{}) *_object {
//
target := self.newClassObject(class)
nameAndValue = append(
[]interface{}{
_functionSignature("builtin"),
_propertyMode(propertyModeWrite | propertyModeConfigure),
},
nameAndValue...,
)
// This actually may be slower than Define
// Benchmark?
target.define(nameAndValue...)
return target
}
func builtinDefine(target *_object, nameAndValue... interface{}) {
nameAndValue = append(
[]interface{}{
_functionSignature("builtin"),
_propertyMode(propertyModeWrite | propertyModeConfigure),
},
nameAndValue...,
)
target.define(nameAndValue)
}
func newContext() *_runtime {
self := &_runtime{}
self._newError = make(map[string] func(Value) *_object)
self.GlobalEnvironment = self.newObjectEnvironment()
self.GlobalObject = self.GlobalEnvironment.Object
self.EnterGlobalExecutionContext()
{
ObjectPrototype := self.newObject()
ObjectPrototype.Prototype = nil
self.Global.ObjectPrototype = ObjectPrototype
}
{
FunctionPrototype := self.newNativeFunctionObject(func(FunctionCall) Value {
return UndefinedValue()
}, 0)
FunctionPrototype.Prototype = self.Global.ObjectPrototype
self.Global.FunctionPrototype = FunctionPrototype
}
{
ArrayPrototype := self.newArray([]Value{})
ArrayPrototype.Prototype = self.Global.ObjectPrototype
self.Global.ArrayPrototype = ArrayPrototype
}
{
StringPrototype := self.newString(toValue(""))
StringPrototype.Prototype = self.Global.ObjectPrototype
self.Global.StringPrototype = StringPrototype
}
{
BooleanPrototype := self.newBoolean(FalseValue())
BooleanPrototype.Prototype = self.Global.ObjectPrototype
self.Global.BooleanPrototype = BooleanPrototype
}
{
NumberPrototype := self.newNumber(toValue(0))
NumberPrototype.Prototype = self.Global.ObjectPrototype
self.Global.NumberPrototype = NumberPrototype
}
{
DatePrototype := self.newDate(0)
DatePrototype.Prototype = self.Global.ObjectPrototype
self.Global.DatePrototype = DatePrototype
}
{
RegExpPrototype := self.newRegExp(UndefinedValue(), UndefinedValue())
RegExpPrototype.Prototype = self.Global.ObjectPrototype
self.Global.RegExpPrototype = RegExpPrototype
}
{
ErrorPrototype := self.newErrorObject(UndefinedValue())
ErrorPrototype.Prototype = self.Global.ObjectPrototype
self.Global.ErrorPrototype = ErrorPrototype
}
self.Global.Object = self.newGlobalFunction(
1,
builtinObject,
builtinNewObject,
self.Global.ObjectPrototype,
"valueOf", func(call FunctionCall) Value {
return toValue(call.thisObject())
},
"toString", builtinObject_toString,
"hasOwnProperty", func(call FunctionCall) Value {
propertyName := toString(call.Argument(0))
thisObject := call.thisObject()
return toValue(thisObject.HasOwnProperty(propertyName))
},
"isPrototypeOf", func(call FunctionCall) Value {
value := call.Argument(0)
if !value.IsObject() {
return FalseValue()
}
prototype := call.toObject(value).Prototype
thisObject := call.thisObject()
for prototype != nil {
if thisObject == prototype {
return TrueValue()
}
prototype = prototype.Prototype
}
return FalseValue()
},
"propertyIsEnumerable", func(call FunctionCall) Value {
propertyName := toString(call.Argument(0))
thisObject := call.thisObject()
property := thisObject.GetOwnProperty(propertyName)
if property != nil && property.CanEnumerate() {
return TrueValue()
}
return FalseValue()
},
)
self.Global.Function = self.newGlobalFunction(
1,
builtinFunction,
builtinNewFunction,
self.Global.FunctionPrototype,
"toString", func(FunctionCall) Value {
return toValue("[function]")
},
"apply", 2, builtinFunction_apply,
"call", 2, builtinFunction_call,
)
self.Global.Array = self.newGlobalFunction(
1,
builtinArray,
builtinNewArray,
self.Global.ArrayPrototype,
"toString", func(call FunctionCall) Value {
thisObject := call.thisObject()
join := thisObject.Get("join")
if join.isCallable() {
join := join._object()
if join.Function.Call.Signature() == "builtin" {
if stash, isArray := thisObject._propertyStash.(*_arrayStash); isArray {
return toValue(builtinArray_joinNative(stash.valueArray, ","))
}
}
return join.Call(call.This, call.ArgumentList)
}
return builtinObject_toString(call)
},
"concat", 1, builtinArray_concat,
"join", 1, builtinArray_join,
"splice", 2, builtinArray_splice,
"shift", 0, builtinArray_shift,
"pop", 0, builtinArray_pop,
"push", 1, builtinArray_push,
"slice", 2, builtinArray_slice,
"unshift", 1, builtinArray_unshift,
"reverse", 0, builtinArray_reverse,
"sort", 0, builtinArray_sort,
)
self.Global.String = self.newGlobalFunction(
1,
builtinString,
builtinNewString,
self.Global.StringPrototype,
"toString", func(call FunctionCall) Value {
return *call.thisClassObject("String").Primitive
},
"valueOf", func(call FunctionCall) Value {
return *call.thisClassObject("String").Primitive
},
"charAt", 1, builtinString_charAt,
"charCodeAt", 1, builtinString_charCodeAt,
"concat", 1, builtinString_concat,
"indexOf", 1, builtinString_indexOf,
"lastIndexOf", 1, builtinString_lastIndexOf,
"match", 1, builtinString_match,
"replace", 2, builtinString_replace,
"search", 1, builtinString_search,
"split", 2, builtinString_split,
"slice", 2, builtinString_slice,
"substring", 2, builtinString_substring,
"toLowerCase", 0, builtinString_toLowerCase,
"toUpperCase", 0, builtinString_toUpperCase,
)
self.Global.Boolean = self.newGlobalFunction(
1,
builtinBoolean,
builtinNewBoolean,
self.Global.BooleanPrototype,
"toString", func(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
// Will throw a TypeError if ThisObject is not a Boolean
value = call.thisClassObject("Boolean").PrimitiveValue()
}
return toValue(toString(value))
},
"valueOf", func(call FunctionCall) Value {
value := call.This
if !value.IsBoolean() {
value = call.thisClassObject("Boolean").PrimitiveValue()
}
return value
},
)
self.Global.Number = self.newGlobalFunction(
1,
builtinNumber,
builtinNewNumber,
self.Global.NumberPrototype,
"valueOf", func(call FunctionCall) Value {
return *call.thisClassObject("Number").Primitive
},
// TODO toFixed
// TODO toExponential
// TODO toPrecision
)
self.Global.Number.Define(
_propertyMode(0),
"MAX_VALUE", toValue(math.MaxFloat64),
"MIN_VALUE", toValue(math.SmallestNonzeroFloat64),
"NaN", NaNValue(),
"NEGATIVE_INFINITY", negativeInfinityValue(),
"POSITIVE_INFINITY", positiveInfinityValue(),
)
self.Global.Math = self.newGlobalObject(
"Math",
"max", 2, builtinMath_max,
"min", 2, builtinMath_min,
"ceil", 1, builtinMath_ceil,
"floor", 1, builtinMath_floor,
"random", 0, builtinMath_random,
)
self.Global.Date = self.newGlobalFunction(
7,
builtinDate,
builtinNewDate,
self.Global.DatePrototype,
"toString", 0, builtinDate_toString,
"valueOf", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return date.Value()
},
// getTime, ...
"getTime", 0, builtinDate_getTime,
"getFullYear", 0, func(call FunctionCall) Value {
// Will throw a TypeError is ThisObject is nil or
// does not have Class of "Date"
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Year())
},
"getUTCFullYear", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Year())
},
"getMonth", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(dateFromGoMonth(date.Time().Local().Month()))
},
"getUTCMonth", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(dateFromGoMonth(date.Time().Month()))
},
"getDate", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Day())
},
"getUTCDate", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Day())
},
// Actually day of the week
"getDay", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(dateFromGoDay(date.Time().Local().Weekday()))
},
"getUTCDay", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(dateFromGoDay(date.Time().Weekday()))
},
"getHours", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Hour())
},
"getUTCHours", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Hour())
},
"getMinutes", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Minute())
},
"getUTCMinutes", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Minute())
},
"getSeconds", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Second())
},
"getUTCSeconds", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Second())
},
"getMilliseconds", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Local().Nanosecond() / (100 * 100 * 100))
},
"getUTCMilliseconds", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
return toValue(date.Time().Nanosecond() / (100 * 100 * 100))
},
"getTimezoneOffset", 0, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
timeLocal := date.Time().Local()
// Is this kosher?
timeLocalAsUTC := time_.Date(
timeLocal.Year(),
timeLocal.Month(),
timeLocal.Day(),
timeLocal.Hour(),
timeLocal.Minute(),
timeLocal.Second(),
timeLocal.Nanosecond(),
time_.UTC,
)
return toValue(date.Time().Sub(timeLocalAsUTC).Seconds() / 60)
},
// setTime, ...
"setTime", 1, builtinDate_setTime,
"setMilliseconds", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().Local()
setTime := time_.Date(
baseTime.Year(),
baseTime.Month(),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
int(toInteger(value)) * 100 * 100 * 100,
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
"setUTCMilliseconds", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().UTC()
setTime := time_.Date(
baseTime.Year(),
baseTime.Month(),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
int(toInteger(value)) * 100 * 100 * 100,
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
// setSeconds
// setUTCSeconds
// setMinutes
// setUTCMinutes
// setHours
// setUTCHours
// setDate
// setUTCDate
"setMonth", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().Local()
setTime := time_.Date(
baseTime.Year(),
dateToGoMonth(int(toInteger(value))),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
baseTime.Nanosecond(),
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
"setUTCMonth", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().UTC()
setTime := time_.Date(
baseTime.Year(),
dateToGoMonth(int(toInteger(value))),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
baseTime.Nanosecond(),
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
"setFullYear", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().Local()
setTime := time_.Date(
int(toInteger(value)),
baseTime.Month(),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
baseTime.Nanosecond(),
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
"setUTCFullYear", 1, func(call FunctionCall) Value {
date := dateObjectOf(call.thisObject())
if date.isNaN {
return NaNValue()
}
value := call.Argument(0)
if value.IsNaN() {
date.SetNaN()
return NaNValue()
}
baseTime := date.Time().UTC()
setTime := time_.Date(
int(toInteger(value)),
baseTime.Month(),
baseTime.Day(),
baseTime.Hour(),
baseTime.Minute(),
baseTime.Second(),
baseTime.Nanosecond(),
baseTime.Location(),
)
date.SetTime(setTime)
return date.Value()
},
// toUTCString
// toISOString
// toJSONString
// toJSON
)
self.Global.RegExp = self.newGlobalFunction(
2,
builtinRegExp,
builtinNewRegExp,
self.Global.RegExpPrototype,
"toString", 0, builtinRegExp_toString,
"exec", 1, builtinRegExp_exec,
"test", 1, builtinRegExp_test,
)
self.Global.Error = self.newGlobalFunction(
1,
builtinError,
builtinNewError,
self.Global.ErrorPrototype,
"name", toValue("Error"),
"toString", 0, builtinError_toString,
)
self.GlobalObject.Define(
"Object", toValue(self.Global.Object),
"Function", toValue(self.Global.Function),
"Array", toValue(self.Global.Array),
"String", toValue(self.Global.String),
"Boolean", toValue(self.Global.Boolean),
"Number", toValue(self.Global.Number),
"Math", toValue(self.Global.Math),
"RegExp", toValue(self.Global.RegExp),
"Date", toValue(self.Global.Date),
"Error", toValue(self.Global.Error),
// TODO JSON
// TODO Is _propertyMode(0) compatible with 3?
// _propertyMode(0),
"undefined", UndefinedValue(),
"NaN", NaNValue(),
"Infinity", positiveInfinityValue(),
"eval", builtinGlobal_eval,
"parseInt", builtinGlobal_parseInt,
"parseFloat", builtinGlobal_parseFloat,
"isNaN", builtinGlobal_isNaN,
"isFinite", builtinGlobal_isFinite,
"decodeURI", builtinGlobal_decodeURI_decodeURIComponent,
"decodeURIComponent", builtinGlobal_decodeURI_decodeURIComponent,
"encodeURI", builtinGlobal_encodeURI,
"encodeURIComponent", builtinGlobal_encodeURIComponent,
)
self._newError["EvalError"] = self.defineError("EvalError")
self._newError["TypeError"] = self.defineError("TypeError")
self._newError["RangeError"] = self.defineError("RangeError")
self._newError["ReferenceError"] = self.defineError("ReferenceError")
self._newError["SyntaxError"] = self.defineError("SyntaxError")
self._newError["URIError"] = self.defineError("URIError")
return self
}
func (runtime *_runtime) newBaseObject() *_object {
self := newObject(runtime, "")
return self
}
func (runtime *_runtime) newClassObject(class string) *_object {
return newObject(runtime, class)
}
func (runtime *_runtime) newPrimitiveObject(class string, value Value) *_object {
self := runtime.newClassObject(class)
self.Primitive = &value
return self
}
func (runtime *_runtime) newObject() *_object {
self := runtime.newClassObject("Object")
self.Prototype = runtime.Global.ObjectPrototype
return self
}
func (runtime *_runtime) newArray(valueArray []Value) *_object {
self := runtime.newArrayObject(valueArray)
self.Prototype = runtime.Global.ArrayPrototype
return self
}
func (runtime *_runtime) newString(value Value) *_object {
self := runtime.newStringObject(value)
self.Prototype = runtime.Global.StringPrototype
return self
}
func (runtime *_runtime) newBoolean(value Value) *_object {
self := runtime.newBooleanObject(value)
self.Prototype = runtime.Global.BooleanPrototype
return self
}
func (runtime *_runtime) newNumber(value Value) *_object {
self := runtime.newNumberObject(value)
self.Prototype = runtime.Global.NumberPrototype
return self
}
func (runtime *_runtime) newRegExp(patternValue Value, flagsValue Value) *_object {
pattern := ""
if patternValue.IsDefined() {
pattern = toString(patternValue)
}
flags := ""
if flagsValue.IsDefined() {
flags = toString(flagsValue)
}
return runtime._newRegExp(pattern, flags)
}
func (runtime *_runtime) _newRegExp(pattern string, flags string) *_object {
self := runtime.newRegExpObject(pattern, flags)
self.Prototype = runtime.Global.RegExpPrototype
return self
}
// TODO Should (probably) be one argument, right? This is redundant
func (runtime *_runtime) newDate(epoch float64) *_object {
self := runtime.newDateObject(epoch)
self.Prototype = runtime.Global.DatePrototype
return self
}
func (runtime *_runtime) newError(name string, message Value) *_object {
var self *_object
_newError := runtime._newError[name]
if _newError != nil {
self = _newError(message)
} else {
self = runtime.newErrorObject(message)
self.Prototype = runtime.Global.ErrorPrototype
if name != "" {
self.WriteValue("name", toValue(name), false)
}
}
return self
}
func (runtime *_runtime) newNativeFunction(_nativeFunction _nativeFunction, length int) *_object {
self := runtime.newNativeFunctionObject(_nativeFunction, length)
self.Prototype = runtime.Global.FunctionPrototype
prototype := runtime.newObject()
self.define(_propertyMode(0), "prototype", toValue(prototype))
prototype.define(
_propertyMode(propertyModeWrite | propertyModeConfigure),
"constructor", toValue(self),
)
return self
}
func (runtime *_runtime) newNodeFunction(node *_functionNode, scopeEnvironment _environment) *_object {
// TODO Implement 13.2 fully
self := runtime.newNodeFunctionObject(node, scopeEnvironment)
self.Prototype = runtime.Global.FunctionPrototype
prototype := runtime.newObject()
self.define(_propertyMode(0), "prototype", toValue(prototype))
prototype.define(
_propertyMode(propertyModeWrite | propertyModeConfigure),
"constructor", toValue(self),
)
return self
}
func (runtime *_runtime) newErrorPrototype(name string) *_object {
prototype := runtime.newClassObject("Error")
prototype.WriteValue("name", toValue(name), false)
prototype.Prototype = runtime.Global.ErrorPrototype
return prototype
}
func (runtime *_runtime) defineError(name string) func(Value) *_object {
prototype := runtime.newErrorPrototype(name) // "TypeError"
errorFunction := func(message Value) *_object {
error := runtime.newErrorObject(message)
error.Prototype = prototype
return error
}
runtime.GlobalObject.WriteValue(name, toValue(runtime.newGlobalFunction(
1,
// e.g. TypeError( ... )
func (call FunctionCall) Value { // TypeError( ... )
return toValue(errorFunction(call.Argument(0)))
},
// e.g. new TypeError( ... )
func (self *_object, _ Value, argumentList []Value) Value {
return toValue(errorFunction(valueOfArrayIndex(argumentList, 0)))
},
prototype,
)), false)
return errorFunction
}