290 lines
7.3 KiB
Go
290 lines
7.3 KiB
Go
package otto
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// Object
|
|
|
|
func builtinObject(call FunctionCall) Value {
|
|
value := call.Argument(0)
|
|
switch value.kind {
|
|
case valueUndefined, valueNull:
|
|
return objectValue(call.runtime.newObject())
|
|
}
|
|
|
|
return objectValue(call.runtime.toObject(value))
|
|
}
|
|
|
|
func builtinNewObject(obj *object, argumentList []Value) Value {
|
|
value := valueOfArrayIndex(argumentList, 0)
|
|
switch value.kind {
|
|
case valueNull, valueUndefined:
|
|
case valueNumber, valueString, valueBoolean:
|
|
return objectValue(obj.runtime.toObject(value))
|
|
case valueObject:
|
|
return value
|
|
default:
|
|
}
|
|
return objectValue(obj.runtime.newObject())
|
|
}
|
|
|
|
func builtinObjectValueOf(call FunctionCall) Value {
|
|
return objectValue(call.thisObject())
|
|
}
|
|
|
|
func builtinObjectHasOwnProperty(call FunctionCall) Value {
|
|
propertyName := call.Argument(0).string()
|
|
thisObject := call.thisObject()
|
|
return boolValue(thisObject.hasOwnProperty(propertyName))
|
|
}
|
|
|
|
func builtinObjectIsPrototypeOf(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
|
|
}
|
|
|
|
func builtinObjectPropertyIsEnumerable(call FunctionCall) Value {
|
|
propertyName := call.Argument(0).string()
|
|
thisObject := call.thisObject()
|
|
prop := thisObject.getOwnProperty(propertyName)
|
|
if prop != nil && prop.enumerable() {
|
|
return trueValue
|
|
}
|
|
return falseValue
|
|
}
|
|
|
|
func builtinObjectToString(call FunctionCall) Value {
|
|
var result string
|
|
switch {
|
|
case call.This.IsUndefined():
|
|
result = "[object Undefined]"
|
|
case call.This.IsNull():
|
|
result = "[object Null]"
|
|
default:
|
|
result = fmt.Sprintf("[object %s]", call.thisObject().class)
|
|
}
|
|
return stringValue(result)
|
|
}
|
|
|
|
func builtinObjectToLocaleString(call FunctionCall) Value {
|
|
toString := call.thisObject().get("toString")
|
|
if !toString.isCallable() {
|
|
panic(call.runtime.panicTypeError("Object.toLocaleString %q is not callable", toString))
|
|
}
|
|
return toString.call(call.runtime, call.This)
|
|
}
|
|
|
|
func builtinObjectGetPrototypeOf(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
obj := val.object()
|
|
if obj == nil {
|
|
panic(call.runtime.panicTypeError("Object.GetPrototypeOf is nil"))
|
|
}
|
|
|
|
if obj.prototype == nil {
|
|
return nullValue
|
|
}
|
|
|
|
return objectValue(obj.prototype)
|
|
}
|
|
|
|
func builtinObjectGetOwnPropertyDescriptor(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
obj := val.object()
|
|
if obj == nil {
|
|
panic(call.runtime.panicTypeError("Object.GetOwnPropertyDescriptor is nil"))
|
|
}
|
|
|
|
name := call.Argument(1).string()
|
|
descriptor := obj.getOwnProperty(name)
|
|
if descriptor == nil {
|
|
return Value{}
|
|
}
|
|
return objectValue(call.runtime.fromPropertyDescriptor(*descriptor))
|
|
}
|
|
|
|
func builtinObjectDefineProperty(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
obj := val.object()
|
|
if obj == nil {
|
|
panic(call.runtime.panicTypeError("Object.DefineProperty is nil"))
|
|
}
|
|
name := call.Argument(1).string()
|
|
descriptor := toPropertyDescriptor(call.runtime, call.Argument(2))
|
|
obj.defineOwnProperty(name, descriptor, true)
|
|
return val
|
|
}
|
|
|
|
func builtinObjectDefineProperties(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
obj := val.object()
|
|
if obj == nil {
|
|
panic(call.runtime.panicTypeError("Object.DefineProperties is nil"))
|
|
}
|
|
|
|
properties := call.runtime.toObject(call.Argument(1))
|
|
properties.enumerate(false, func(name string) bool {
|
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
|
obj.defineOwnProperty(name, descriptor, true)
|
|
return true
|
|
})
|
|
|
|
return val
|
|
}
|
|
|
|
func builtinObjectCreate(call FunctionCall) Value {
|
|
prototypeValue := call.Argument(0)
|
|
if !prototypeValue.IsNull() && !prototypeValue.IsObject() {
|
|
panic(call.runtime.panicTypeError("Object.Create is nil"))
|
|
}
|
|
|
|
obj := call.runtime.newObject()
|
|
obj.prototype = prototypeValue.object()
|
|
|
|
propertiesValue := call.Argument(1)
|
|
if propertiesValue.IsDefined() {
|
|
properties := call.runtime.toObject(propertiesValue)
|
|
properties.enumerate(false, func(name string) bool {
|
|
descriptor := toPropertyDescriptor(call.runtime, properties.get(name))
|
|
obj.defineOwnProperty(name, descriptor, true)
|
|
return true
|
|
})
|
|
}
|
|
|
|
return objectValue(obj)
|
|
}
|
|
|
|
func builtinObjectIsExtensible(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
return boolValue(obj.extensible)
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.IsExtensible is nil"))
|
|
}
|
|
|
|
func builtinObjectPreventExtensions(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
obj.extensible = false
|
|
return val
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.PreventExtensions is nil"))
|
|
}
|
|
|
|
func builtinObjectIsSealed(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
if obj.extensible {
|
|
return boolValue(false)
|
|
}
|
|
result := true
|
|
obj.enumerate(true, func(name string) bool {
|
|
prop := obj.getProperty(name)
|
|
if prop.configurable() {
|
|
result = false
|
|
}
|
|
return true
|
|
})
|
|
return boolValue(result)
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.IsSealed is nil"))
|
|
}
|
|
|
|
func builtinObjectSeal(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
obj.enumerate(true, func(name string) bool {
|
|
if prop := obj.getOwnProperty(name); nil != prop && prop.configurable() {
|
|
prop.configureOff()
|
|
obj.defineOwnProperty(name, *prop, true)
|
|
}
|
|
return true
|
|
})
|
|
obj.extensible = false
|
|
return val
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.Seal is nil"))
|
|
}
|
|
|
|
func builtinObjectIsFrozen(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
if obj.extensible {
|
|
return boolValue(false)
|
|
}
|
|
result := true
|
|
obj.enumerate(true, func(name string) bool {
|
|
prop := obj.getProperty(name)
|
|
if prop.configurable() || prop.writable() {
|
|
result = false
|
|
}
|
|
return true
|
|
})
|
|
return boolValue(result)
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.IsFrozen is nil"))
|
|
}
|
|
|
|
func builtinObjectFreeze(call FunctionCall) Value {
|
|
val := call.Argument(0)
|
|
if obj := val.object(); obj != nil {
|
|
obj.enumerate(true, func(name string) bool {
|
|
if prop, update := obj.getOwnProperty(name), false; nil != prop {
|
|
if prop.isDataDescriptor() && prop.writable() {
|
|
prop.writeOff()
|
|
update = true
|
|
}
|
|
if prop.configurable() {
|
|
prop.configureOff()
|
|
update = true
|
|
}
|
|
if update {
|
|
obj.defineOwnProperty(name, *prop, true)
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
obj.extensible = false
|
|
return val
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.Freeze is nil"))
|
|
}
|
|
|
|
func builtinObjectKeys(call FunctionCall) Value {
|
|
if obj, keys := call.Argument(0).object(), []Value(nil); nil != obj {
|
|
obj.enumerate(false, func(name string) bool {
|
|
keys = append(keys, stringValue(name))
|
|
return true
|
|
})
|
|
return objectValue(call.runtime.newArrayOf(keys))
|
|
}
|
|
panic(call.runtime.panicTypeError("Object.Keys is nil"))
|
|
}
|
|
|
|
func builtinObjectGetOwnPropertyNames(call FunctionCall) Value {
|
|
if obj, propertyNames := call.Argument(0).object(), []Value(nil); nil != obj {
|
|
obj.enumerate(true, func(name string) bool {
|
|
if obj.hasOwnProperty(name) {
|
|
propertyNames = append(propertyNames, stringValue(name))
|
|
}
|
|
return true
|
|
})
|
|
return objectValue(call.runtime.newArrayOf(propertyNames))
|
|
}
|
|
|
|
// Default to empty array for non object types.
|
|
return objectValue(call.runtime.newArray(0))
|
|
}
|