plugeth/vendor/github.com/robertkrimen/otto/object_class.go

494 lines
11 KiB
Go
Raw Normal View History

2015-02-16 13:28:33 +00:00
package otto
import (
"encoding/json"
)
type _objectClass struct {
getOwnProperty func(*_object, string) *_property
getProperty func(*_object, string) *_property
get func(*_object, string) Value
canPut func(*_object, string) bool
put func(*_object, string, Value, bool)
hasProperty func(*_object, string) bool
hasOwnProperty func(*_object, string) bool
defineOwnProperty func(*_object, string, _property, bool) bool
delete func(*_object, string, bool) bool
enumerate func(*_object, bool, func(string) bool)
clone func(*_object, *_object, *_clone) *_object
marshalJSON func(*_object) json.Marshaler
}
func objectEnumerate(self *_object, all bool, each func(string) bool) {
for _, name := range self.propertyOrder {
if all || self.property[name].enumerable() {
if !each(name) {
return
}
}
}
}
var (
_classObject,
_classArray,
_classString,
_classArguments,
_classGoStruct,
_classGoMap,
_classGoArray,
_classGoSlice,
_ *_objectClass
)
func init() {
_classObject = &_objectClass{
objectGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
objectEnumerate,
objectClone,
nil,
}
_classArray = &_objectClass{
objectGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
arrayDefineOwnProperty,
objectDelete,
objectEnumerate,
objectClone,
nil,
}
_classString = &_objectClass{
stringGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
stringEnumerate,
objectClone,
nil,
}
_classArguments = &_objectClass{
argumentsGetOwnProperty,
objectGetProperty,
argumentsGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
argumentsDefineOwnProperty,
argumentsDelete,
objectEnumerate,
objectClone,
nil,
}
_classGoStruct = &_objectClass{
goStructGetOwnProperty,
objectGetProperty,
objectGet,
goStructCanPut,
goStructPut,
objectHasProperty,
objectHasOwnProperty,
objectDefineOwnProperty,
objectDelete,
goStructEnumerate,
objectClone,
goStructMarshalJSON,
}
_classGoMap = &_objectClass{
goMapGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goMapDefineOwnProperty,
goMapDelete,
goMapEnumerate,
objectClone,
nil,
}
_classGoArray = &_objectClass{
goArrayGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goArrayDefineOwnProperty,
goArrayDelete,
goArrayEnumerate,
objectClone,
nil,
}
_classGoSlice = &_objectClass{
goSliceGetOwnProperty,
objectGetProperty,
objectGet,
objectCanPut,
objectPut,
objectHasProperty,
objectHasOwnProperty,
goSliceDefineOwnProperty,
goSliceDelete,
goSliceEnumerate,
objectClone,
nil,
}
}
// Allons-y
// 8.12.1
func objectGetOwnProperty(self *_object, name string) *_property {
// Return a _copy_ of the property
property, exists := self._read(name)
if !exists {
return nil
}
return &property
}
// 8.12.2
func objectGetProperty(self *_object, name string) *_property {
property := self.getOwnProperty(name)
if property != nil {
return property
}
if self.prototype != nil {
return self.prototype.getProperty(name)
}
return nil
}
// 8.12.3
func objectGet(self *_object, name string) Value {
property := self.getProperty(name)
if property != nil {
return property.get(self)
}
return Value{}
2015-02-16 13:28:33 +00:00
}
// 8.12.4
func objectCanPut(self *_object, name string) bool {
canPut, _, _ := _objectCanPut(self, name)
return canPut
}
func _objectCanPut(self *_object, name string) (canPut bool, property *_property, setter *_object) {
property = self.getOwnProperty(name)
if property != nil {
switch propertyValue := property.value.(type) {
case Value:
canPut = property.writable()
return
case _propertyGetSet:
setter = propertyValue[1]
canPut = setter != nil
return
default:
panic(self.runtime.panicTypeError())
2015-02-16 13:28:33 +00:00
}
}
if self.prototype == nil {
return self.extensible, nil, nil
}
property = self.prototype.getProperty(name)
if property == nil {
return self.extensible, nil, nil
}
switch propertyValue := property.value.(type) {
case Value:
if !self.extensible {
return false, nil, nil
}
return property.writable(), nil, nil
case _propertyGetSet:
setter = propertyValue[1]
canPut = setter != nil
return
default:
panic(self.runtime.panicTypeError())
2015-02-16 13:28:33 +00:00
}
}
// 8.12.5
func objectPut(self *_object, name string, value Value, throw bool) {
if true {
// Shortcut...
//
// So, right now, every class is using objectCanPut and every class
// is using objectPut.
//
// If that were to no longer be the case, we would have to have
// something to detect that here, so that we do not use an
// incompatible canPut routine
canPut, property, setter := _objectCanPut(self, name)
if !canPut {
self.runtime.typeErrorResult(throw)
2015-02-16 13:28:33 +00:00
} else if setter != nil {
setter.call(toValue(self), []Value{value}, false, nativeFrame)
2015-02-16 13:28:33 +00:00
} else if property != nil {
property.value = value
self.defineOwnProperty(name, *property, throw)
} else {
self.defineProperty(name, value, 0111, throw)
}
return
}
// The long way...
//
// Right now, code should never get here, see above
if !self.canPut(name) {
self.runtime.typeErrorResult(throw)
2015-02-16 13:28:33 +00:00
return
}
property := self.getOwnProperty(name)
if property == nil {
property = self.getProperty(name)
if property != nil {
if getSet, isAccessor := property.value.(_propertyGetSet); isAccessor {
getSet[1].call(toValue(self), []Value{value}, false, nativeFrame)
2015-02-16 13:28:33 +00:00
return
}
}
self.defineProperty(name, value, 0111, throw)
} else {
switch propertyValue := property.value.(type) {
case Value:
property.value = value
self.defineOwnProperty(name, *property, throw)
case _propertyGetSet:
if propertyValue[1] != nil {
propertyValue[1].call(toValue(self), []Value{value}, false, nativeFrame)
2015-02-16 13:28:33 +00:00
return
}
if throw {
panic(self.runtime.panicTypeError())
2015-02-16 13:28:33 +00:00
}
default:
panic(self.runtime.panicTypeError())
2015-02-16 13:28:33 +00:00
}
}
}
// 8.12.6
func objectHasProperty(self *_object, name string) bool {
return self.getProperty(name) != nil
}
func objectHasOwnProperty(self *_object, name string) bool {
return self.getOwnProperty(name) != nil
}
// 8.12.9
func objectDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
property, exists := self._read(name)
{
if !exists {
if !self.extensible {
goto Reject
}
if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
if newGetSet[0] == &_nilGetSetObject {
newGetSet[0] = nil
}
if newGetSet[1] == &_nilGetSetObject {
newGetSet[1] = nil
}
descriptor.value = newGetSet
}
self._write(name, descriptor.value, descriptor.mode)
return true
}
if descriptor.isEmpty() {
return true
}
// TODO Per 8.12.9.6 - We should shortcut here (returning true) if
// the current and new (define) properties are the same
configurable := property.configurable()
if !configurable {
if descriptor.configurable() {
goto Reject
}
// Test that, if enumerable is set on the property descriptor, then it should
// be the same as the existing property
if descriptor.enumerateSet() && descriptor.enumerable() != property.enumerable() {
goto Reject
}
}
value, isDataDescriptor := property.value.(Value)
getSet, _ := property.value.(_propertyGetSet)
if descriptor.isGenericDescriptor() {
// GenericDescriptor
} else if isDataDescriptor != descriptor.isDataDescriptor() {
// DataDescriptor <=> AccessorDescriptor
if !configurable {
goto Reject
}
} else if isDataDescriptor && descriptor.isDataDescriptor() {
// DataDescriptor <=> DataDescriptor
if !configurable {
if !property.writable() && descriptor.writable() {
goto Reject
}
if !property.writable() {
if descriptor.value != nil && !sameValue(value, descriptor.value.(Value)) {
goto Reject
}
}
}
} else {
// AccessorDescriptor <=> AccessorDescriptor
newGetSet, _ := descriptor.value.(_propertyGetSet)
presentGet, presentSet := true, true
if newGetSet[0] == &_nilGetSetObject {
// Present, but nil
newGetSet[0] = nil
} else if newGetSet[0] == nil {
// Missing, not even nil
newGetSet[0] = getSet[0]
presentGet = false
}
if newGetSet[1] == &_nilGetSetObject {
// Present, but nil
newGetSet[1] = nil
} else if newGetSet[1] == nil {
// Missing, not even nil
newGetSet[1] = getSet[1]
presentSet = false
}
if !configurable {
if (presentGet && (getSet[0] != newGetSet[0])) || (presentSet && (getSet[1] != newGetSet[1])) {
goto Reject
}
}
descriptor.value = newGetSet
}
{
// This section will preserve attributes of
// the original property, if necessary
value1 := descriptor.value
if value1 == nil {
value1 = property.value
} else if newGetSet, isAccessor := descriptor.value.(_propertyGetSet); isAccessor {
if newGetSet[0] == &_nilGetSetObject {
newGetSet[0] = nil
}
if newGetSet[1] == &_nilGetSetObject {
newGetSet[1] = nil
}
value1 = newGetSet
}
mode1 := descriptor.mode
if mode1&0222 != 0 {
// TODO Factor this out into somewhere testable
// (Maybe put into switch ...)
mode0 := property.mode
if mode1&0200 != 0 {
if descriptor.isDataDescriptor() {
mode1 &= ^0200 // Turn off "writable" missing
mode1 |= (mode0 & 0100)
}
}
if mode1&020 != 0 {
mode1 |= (mode0 & 010)
}
if mode1&02 != 0 {
mode1 |= (mode0 & 01)
}
mode1 &= 0311 // 0311 to preserve the non-setting on "writable"
}
self._write(name, value1, mode1)
}
return true
}
Reject:
if throw {
panic(self.runtime.panicTypeError())
2015-02-16 13:28:33 +00:00
}
return false
}
func objectDelete(self *_object, name string, throw bool) bool {
property_ := self.getOwnProperty(name)
if property_ == nil {
return true
}
if property_.configurable() {
self._delete(name)
return true
}
return self.runtime.typeErrorResult(throw)
2015-02-16 13:28:33 +00:00
}
func objectClone(in *_object, out *_object, clone *_clone) *_object {
*out = *in
2015-02-16 13:28:33 +00:00
out.runtime = clone.runtime
if out.prototype != nil {
out.prototype = clone.object(in.prototype)
2015-02-16 13:28:33 +00:00
}
out.property = make(map[string]_property, len(in.property))
out.propertyOrder = make([]string, len(in.propertyOrder))
copy(out.propertyOrder, in.propertyOrder)
for index, property := range in.property {
out.property[index] = clone.property(property)
2015-02-16 13:28:33 +00:00
}
switch value := in.value.(type) {
case _nativeFunctionObject:
out.value = value
case _bindFunctionObject:
out.value = _bindFunctionObject{
target: clone.object(value.target),
this: clone.value(value.this),
argumentList: clone.valueArray(value.argumentList),
}
case _nodeFunctionObject:
out.value = _nodeFunctionObject{
node: value.node,
stash: clone.stash(value.stash),
}
2015-02-16 13:28:33 +00:00
case _argumentsObject:
out.value = value.clone(clone)
2015-02-16 13:28:33 +00:00
}
return out
2015-02-16 13:28:33 +00:00
}