Commit 34803ec6 authored by oliver@apple.com's avatar oliver@apple.com

Implement ES5 Object.defineProperty function

https://bugs.webkit.org/show_bug.cgi?id=29503

Reviewed by Geoff Garen

Implement Object.defineProperty.  This requires adding the API to
ObjectConstructor, along with a helper function that implements the
ES5 internal [[ToPropertyDescriptor]] function.  It then adds
JSObject::defineOwnProperty that implements the appropriate ES5 semantics.
Currently defineOwnProperty uses a delete followed by a put to redefine
attributes of a property, clearly this is less efficient than it could be
but we can improve this if it needs to be possible in future.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@48542 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent effcf9ce
2009-09-18 Oliver Hunt <oliver@apple.com>
Reviewed by Geoff Garen.
Implement ES5 Object.defineProperty function
https://bugs.webkit.org/show_bug.cgi?id=29503
Implement Object.defineProperty. This requires adding the API to
ObjectConstructor, along with a helper function that implements the
ES5 internal [[ToPropertyDescriptor]] function. It then adds
JSObject::defineOwnProperty that implements the appropriate ES5 semantics.
Currently defineOwnProperty uses a delete followed by a put to redefine
attributes of a property, clearly this is less efficient than it could be
but we can improve this if it needs to be possible in future.
* JavaScriptCore.exp:
* debugger/DebuggerActivation.cpp:
(JSC::DebuggerActivation::defineGetter):
(JSC::DebuggerActivation::defineSetter):
* debugger/DebuggerActivation.h:
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
* jit/JITStubs.cpp:
Update defineGetter/Setter calls
* runtime/CommonIdentifiers.h:
* runtime/JSArray.cpp:
(JSC::JSArray::getOwnPropertySlot):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::defineGetter):
(JSC::JSGlobalObject::defineSetter):
* runtime/JSGlobalObject.h:
* runtime/JSObject.cpp:
(JSC::JSObject::defineGetter):
(JSC::JSObject::defineSetter):
(JSC::putDescriptor):
(JSC::JSObject::defineOwnProperty):
* runtime/JSObject.h:
* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructor::ObjectConstructor):
(JSC::objectConstructorGetOwnPropertyDescriptor):
(JSC::toPropertyDescriptor):
(JSC::objectConstructorDefineProperty):
* runtime/ObjectPrototype.cpp:
(JSC::objectProtoFuncDefineGetter):
(JSC::objectProtoFuncDefineSetter):
* runtime/PropertyDescriptor.cpp:
(JSC::PropertyDescriptor::writable):
(JSC::PropertyDescriptor::enumerable):
(JSC::PropertyDescriptor::configurable):
(JSC::PropertyDescriptor::isDataDescriptor):
(JSC::PropertyDescriptor::isGenericDescriptor):
(JSC::PropertyDescriptor::isAccessorDescriptor):
(JSC::PropertyDescriptor::getter):
(JSC::PropertyDescriptor::setter):
(JSC::PropertyDescriptor::setDescriptor):
(JSC::PropertyDescriptor::setAccessorDescriptor):
(JSC::PropertyDescriptor::setWritable):
(JSC::PropertyDescriptor::setEnumerable):
(JSC::PropertyDescriptor::setConfigurable):
(JSC::PropertyDescriptor::setSetter):
(JSC::PropertyDescriptor::setGetter):
(JSC::PropertyDescriptor::equalTo):
(JSC::PropertyDescriptor::attributesEqual):
(JSC::PropertyDescriptor::attributesWithOverride):
* runtime/PropertyDescriptor.h:
(JSC::PropertyDescriptor::PropertyDescriptor):
(JSC::PropertyDescriptor::value):
(JSC::PropertyDescriptor::setValue):
(JSC::PropertyDescriptor::isEmpty):
(JSC::PropertyDescriptor::writablePresent):
(JSC::PropertyDescriptor::enumerablePresent):
(JSC::PropertyDescriptor::configurablePresent):
(JSC::PropertyDescriptor::setterPresent):
(JSC::PropertyDescriptor::getterPresent):
(JSC::PropertyDescriptor::operator==):
(JSC::PropertyDescriptor::):
2009-09-18 Gabor Loki <loki@inf.u-szeged.hu>
Reviewed by Gavin Barraclough.
......
......@@ -128,8 +128,8 @@ __ZN3JSC13SamplingFlags7s_flagsE
__ZN3JSC13StatementNode6setLocEii
__ZN3JSC13jsOwnedStringEPNS_12JSGlobalDataERKNS_7UStringE
__ZN3JSC14JSGlobalObject10globalExecEv
__ZN3JSC14JSGlobalObject12defineGetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectE
__ZN3JSC14JSGlobalObject12defineSetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectE
__ZN3JSC14JSGlobalObject12defineGetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectEj
__ZN3JSC14JSGlobalObject12defineSetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectEj
__ZN3JSC14JSGlobalObject12markChildrenERNS_9MarkStackE
__ZN3JSC14JSGlobalObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueEj
__ZN3JSC14JSGlobalObject3putEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueERNS_15PutPropertySlotE
......@@ -155,11 +155,15 @@ __ZN3JSC17PrototypeFunctionC1EPNS_9ExecStateEN3WTF10PassRefPtrINS_9StructureEEEi
__ZN3JSC17PrototypeFunctionC1EPNS_9ExecStateEiRKNS_10IdentifierEPFNS_7JSValueES2_PNS_8JSObjectES6_RKNS_7ArgListEE
__ZN3JSC17constructFunctionEPNS_9ExecStateERKNS_7ArgListERKNS_10IdentifierERKNS_7UStringEi
__ZN3JSC18DebuggerActivationC1EPNS_8JSObjectE
__ZN3JSC18PropertyDescriptor11setWritableEb
__ZN3JSC18PropertyDescriptor12setUndefinedEv
__ZN3JSC18PropertyDescriptor13setDescriptorENS_7JSValueEj
__ZN3JSC18PropertyDescriptor13setEnumerableEb
__ZN3JSC18PropertyDescriptor15setConfigurableEb
__ZN3JSC18PropertyDescriptor17defaultAttributesE
__ZN3JSC18PropertyDescriptor21setAccessorDescriptorENS_7JSValueES1_j
__ZNK3JSC18PropertyDescriptor6setterEv
__ZNK3JSC18PropertyDescriptor6getterEv
__ZN3JSC18PropertyDescriptor9setGetterENS_7JSValueE
__ZN3JSC18PropertyDescriptor9setSetterENS_7JSValueE
__ZN3JSC19constructEmptyArrayEPNS_9ExecStateE
__ZN3JSC19initializeThreadingEv
__ZN3JSC20MarkedArgumentBuffer10slowAppendENS_7JSValueE
......@@ -234,8 +238,8 @@ __ZN3JSC8Debugger6attachEPNS_14JSGlobalObjectE
__ZN3JSC8Debugger6detachEPNS_14JSGlobalObjectE
__ZN3JSC8DebuggerD2Ev
__ZN3JSC8JSObject11hasInstanceEPNS_9ExecStateENS_7JSValueES3_
__ZN3JSC8JSObject12defineGetterEPNS_9ExecStateERKNS_10IdentifierEPS0_
__ZN3JSC8JSObject12defineSetterEPNS_9ExecStateERKNS_10IdentifierEPS0_
__ZN3JSC8JSObject12defineGetterEPNS_9ExecStateERKNS_10IdentifierEPS0_j
__ZN3JSC8JSObject12defineSetterEPNS_9ExecStateERKNS_10IdentifierEPS0_j
__ZN3JSC8JSObject12lookupGetterEPNS_9ExecStateERKNS_10IdentifierE
__ZN3JSC8JSObject12lookupSetterEPNS_9ExecStateERKNS_10IdentifierE
__ZN3JSC8JSObject12markChildrenERNS_9MarkStackE
......@@ -244,6 +248,7 @@ __ZN3JSC8JSObject14deletePropertyEPNS_9ExecStateEj
__ZN3JSC8JSObject15unwrappedObjectEv
__ZN3JSC8JSObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
__ZN3JSC8JSObject17createInheritorIDEv
__ZN3JSC8JSObject17defineOwnPropertyEPNS_9ExecStateERKNS_10IdentifierERNS_18PropertyDescriptorEb
__ZN3JSC8JSObject17putDirectFunctionEPNS_9ExecStateEPNS_16InternalFunctionEj
__ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueEj
__ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueEjbRNS_15PutPropertySlotE
......@@ -346,7 +351,13 @@ __ZNK3JSC17DebuggerCallFrame12functionNameEv
__ZNK3JSC17DebuggerCallFrame22calculatedFunctionNameEv
__ZNK3JSC17DebuggerCallFrame4typeEv
__ZNK3JSC17DebuggerCallFrame8evaluateERKNS_7UStringERNS_7JSValueE
__ZNK3JSC18PropertyDescriptor12hasAccessorsEv
__ZNK3JSC18PropertyDescriptor10enumerableEv
__ZNK3JSC18PropertyDescriptor12configurableEv
__ZNK3JSC18PropertyDescriptor16isDataDescriptorEv
__ZNK3JSC18PropertyDescriptor20isAccessorDescriptorEv
__ZNK3JSC18PropertyDescriptor6setterEv
__ZNK3JSC18PropertyDescriptor6getterEv
__ZNK3JSC18PropertyDescriptor8writableEv
__ZNK3JSC4Heap10statisticsEv
__ZNK3JSC6JSCell11toPrimitiveEPNS_9ExecStateENS_22PreferredPrimitiveTypeE
__ZNK3JSC6JSCell12toThisObjectEPNS_9ExecStateE
......
......@@ -81,14 +81,14 @@ bool DebuggerActivation::getPropertyAttributes(JSC::ExecState* exec, const Ident
return m_activation->getPropertyAttributes(exec, propertyName, attributes);
}
void DebuggerActivation::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
void DebuggerActivation::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
{
m_activation->defineGetter(exec, propertyName, getterFunction);
m_activation->defineGetter(exec, propertyName, getterFunction, attributes);
}
void DebuggerActivation::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
void DebuggerActivation::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
{
m_activation->defineSetter(exec, propertyName, setterFunction);
m_activation->defineSetter(exec, propertyName, setterFunction, attributes);
}
JSValue DebuggerActivation::lookupGetter(ExecState* exec, const Identifier& propertyName)
......
......@@ -44,8 +44,8 @@ namespace JSC {
virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const;
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes);
virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
......
......@@ -3731,7 +3731,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
JSObject* baseObj = asObject(callFrame->r(base).jsValue());
Identifier& ident = callFrame->codeBlock()->identifier(property);
ASSERT(callFrame->r(function).jsValue().isObject());
baseObj->defineSetter(callFrame, ident, asObject(callFrame->r(function).jsValue()));
baseObj->defineSetter(callFrame, ident, asObject(callFrame->r(function).jsValue()), 0);
++vPC;
NEXT_INSTRUCTION();
......
......@@ -39,6 +39,7 @@
macro(compile) \
macro(configurable) \
macro(constructor) \
macro(defineProperty) \
macro(enumerable) \
macro(eval) \
macro(exec) \
......
......@@ -223,7 +223,7 @@ bool JSArray::getOwnPropertySlot(ExecState* exec, unsigned i, PropertySlot& slot
}
}
return false;
return JSObject::getOwnPropertySlot(exec, Identifier::from(exec, i), slot);
}
bool JSArray::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
......
......@@ -175,18 +175,18 @@ void JSGlobalObject::putWithAttributes(ExecState* exec, const Identifier& proper
}
}
void JSGlobalObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc)
void JSGlobalObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes)
{
PropertySlot slot;
if (!symbolTableGet(propertyName, slot))
JSVariableObject::defineGetter(exec, propertyName, getterFunc);
JSVariableObject::defineGetter(exec, propertyName, getterFunc, attributes);
}
void JSGlobalObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc)
void JSGlobalObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes)
{
PropertySlot slot;
if (!symbolTableGet(propertyName, slot))
JSVariableObject::defineSetter(exec, propertyName, setterFunc);
JSVariableObject::defineSetter(exec, propertyName, setterFunc, attributes);
}
static inline JSObject* lastInPrototypeChain(JSObject* object)
......
......@@ -175,8 +175,8 @@ namespace JSC {
virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&);
virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunc, unsigned attributes);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunc, unsigned attributes);
// Linked list of all global objects that use the same JSGlobalData.
JSGlobalObject*& head() { return d()->globalData->head; }
......
......@@ -275,7 +275,7 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifi
return 0;
}
void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
{
JSValue object = getDirect(propertyName);
if (object && object.isGetterSetter()) {
......@@ -286,7 +286,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO
PutPropertySlot slot;
GetterSetter* getterSetter = new (exec) GetterSetter(exec);
putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, true, slot);
putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
// putDirect will change our Structure if we add a new property. For
// getters and setters, though, we also need to change our Structure
......@@ -302,7 +302,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO
getterSetter->setGetter(getterFunction);
}
void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
{
JSValue object = getDirect(propertyName);
if (object && object.isGetterSetter()) {
......@@ -313,7 +313,7 @@ void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSO
PutPropertySlot slot;
GetterSetter* getterSetter = new (exec) GetterSetter(exec);
putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, true, slot);
putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
// putDirect will change our Structure if we add a new property. For
// getters and setters, though, we also need to change our Structure
......@@ -541,4 +541,131 @@ bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& property
object = asObject(prototype);
}
}
static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
{
if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
return true;
}
attributes &= ~ReadOnly;
if (descriptor.getter() && descriptor.getter().isObject())
target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
if (exec->hadException())
return false;
if (descriptor.setter() && descriptor.setter().isObject())
target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
return !exec->hadException();
}
bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
{
// If we have a new property we can just put it on normally
PropertyDescriptor current;
if (!getOwnPropertyDescriptor(exec, propertyName, current))
return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
if (descriptor.isEmpty())
return true;
if (current.equalTo(descriptor))
return true;
// Filter out invalid changes
if (!current.configurable()) {
if (descriptor.configurable()) {
if (throwException)
throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
return false;
}
if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
if (throwException)
throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
return false;
}
}
// A generic descriptor is simply changing the attributes of an existing property
if (descriptor.isGenericDescriptor()) {
if (!current.attributesEqual(descriptor)) {
deleteProperty(exec, propertyName);
putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
}
return true;
}
// Changing between a normal property or an accessor property
if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
if (!current.configurable()) {
if (throwException)
throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
return false;
}
deleteProperty(exec, propertyName);
return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
}
// Changing the value and attributes of an existing property
if (descriptor.isDataDescriptor()) {
if (!current.configurable()) {
if (!current.writable() && descriptor.writable()) {
if (throwException)
throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
return false;
}
if (!current.writable()) {
if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) {
if (throwException)
throwError(exec, TypeError, "Attempting to change value of a readonly property.");
return false;
}
}
} else if (current.attributesEqual(descriptor)) {
if (!descriptor.value())
return true;
PutPropertySlot slot;
put(exec, propertyName, descriptor.value(), slot);
if (exec->hadException())
return false;
return true;
}
deleteProperty(exec, propertyName);
return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
}
// Changing the accessor functions of an existing accessor property
ASSERT(descriptor.isAccessorDescriptor());
if (!current.configurable()) {
if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) {
if (throwException)
throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
return false;
}
if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) {
if (throwException)
throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
return false;
}
}
JSValue accessor = getDirect(propertyName);
if (!accessor)
return false;
GetterSetter* getterSetter = asGetterSetter(accessor);
if (current.attributesEqual(descriptor)) {
if (descriptor.setter())
getterSetter->setSetter(asObject(descriptor.setter()));
if (descriptor.getter())
getterSetter->setGetter(asObject(descriptor.getter()));
return true;
}
deleteProperty(exec, propertyName);
unsigned attrs = current.attributesWithOverride(descriptor);
if (descriptor.setter())
attrs |= Setter;
if (descriptor.getter())
attrs |= Getter;
putDirect(propertyName, getterSetter, attrs);
return true;
}
} // namespace JSC
......@@ -186,10 +186,11 @@ namespace JSC {
void fillGetterPropertySlot(PropertySlot&, JSValue* location);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction);
virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
virtual bool isGlobalObject() const { return false; }
virtual bool isVariableObject() const { return false; }
......
......@@ -37,6 +37,7 @@ ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor);
static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&);
static JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState*, JSObject*, JSValue, const ArgList&);
ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure)
: InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object"))
......@@ -50,6 +51,7 @@ ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> stru
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum);
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum);
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum);
putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 3, exec->propertyNames().defineProperty, objectConstructorDefineProperty), DontEnum);
}
// ECMA 15.2.2
......@@ -103,15 +105,14 @@ JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec,
return jsUndefined();
if (exec->hadException())
return jsUndefined();
ASSERT(descriptor.isValid());
JSObject* description = constructEmptyObject(exec);
if (!descriptor.hasAccessors()) {
description->putDirect(exec->propertyNames().value, descriptor.value(), 0);
if (!descriptor.isAccessorDescriptor()) {
description->putDirect(exec->propertyNames().value, descriptor.value() ? descriptor.value() : jsUndefined(), 0);
description->putDirect(exec->propertyNames().writable, jsBoolean(descriptor.writable()), 0);
} else {
description->putDirect(exec->propertyNames().get, descriptor.getter(), 0);
description->putDirect(exec->propertyNames().set, descriptor.setter(), 0);
description->putDirect(exec->propertyNames().get, descriptor.getter() ? descriptor.getter() : jsUndefined(), 0);
description->putDirect(exec->propertyNames().set, descriptor.setter() ? descriptor.setter() : jsUndefined(), 0);
}
description->putDirect(exec->propertyNames().enumerable, jsBoolean(descriptor.enumerable()), 0);
......@@ -133,4 +134,107 @@ JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue,
return keys;
}
// ES5 8.10.5 ToPropertyDescriptor
static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor& desc)
{
if (!in.isObject()) {
throwError(exec, TypeError, "Property description must be an object.");
return false;
}
JSObject* description = asObject(in);
PropertySlot enumerableSlot;
if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) {
desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec));
if (exec->hadException())
return false;
}
PropertySlot configurableSlot;
if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) {
desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec));
if (exec->hadException())
return false;
}
JSValue value;
PropertySlot valueSlot;
if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) {
desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value));
if (exec->hadException())
return false;
}
PropertySlot writableSlot;
if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) {
desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec));
if (exec->hadException())
return false;
}
PropertySlot getSlot;
if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) {
JSValue get = getSlot.getValue(exec, exec->propertyNames().get);
if (exec->hadException())
return false;
if (!get.isUndefined()) {
CallData callData;
if (get.getCallData(callData) == CallTypeNone) {
throwError(exec, TypeError, "Getter must be a function.");
return false;
}
} else
get = JSValue();
desc.setGetter(get);
}
PropertySlot setSlot;
if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) {
JSValue set = setSlot.getValue(exec, exec->propertyNames().set);
if (exec->hadException())
return false;
if (!set.isUndefined()) {
CallData callData;
if (set.getCallData(callData) == CallTypeNone) {
throwError(exec, TypeError, "Setter must be a function.");
return false;
}
} else
set = JSValue();
desc.setSetter(set);
}
if (!desc.isAccessorDescriptor())
return true;
if (desc.value()) {
throwError(exec, TypeError, "Invalid property. 'value' present on property with getter or setter.");
return false;
}
if (desc.writablePresent()) {
throwError(exec, TypeError, "Invalid property. 'writable' present on property with getter or setter.");
return false;
}
return true;
}
JSValue JSC_HOST_CALL objectConstructorDefineProperty(ExecState* exec, JSObject*, JSValue, const ArgList& args)
{
if (!args.at(0).isObject())
return throwError(exec, TypeError, "Properties can only be defined on Objects.");
JSObject* O = asObject(args.at(0));
UString propertyName = args.at(1).toString(exec);
if (exec->hadException())
return jsNull();
PropertyDescriptor descriptor;
if (!toPropertyDescriptor(exec, args.at(2), descriptor))
return jsNull();
ASSERT((descriptor.attributes() & (Getter | Setter)) || (!descriptor.isAccessorDescriptor()));
ASSERT(!exec->hadException());
O->defineOwnProperty(exec, Identifier(exec, propertyName), descriptor, true);
return O;
}
} // namespace JSC
......@@ -30,29 +30,40 @@
#include "GetterSetter.h"
#include "JSObject.h"
#include "Operations.h"
namespace JSC {
unsigned PropertyDescriptor::defaultAttributes = (DontDelete << 1) - 1;
bool PropertyDescriptor::writable() const
{
ASSERT(!hasAccessors());
ASSERT(!isAccessorDescriptor());
return !(m_attributes & ReadOnly);
}
bool PropertyDescriptor::enumerable() const
{
ASSERT(isValid());
return !(m_attributes & DontEnum);
}
bool PropertyDescriptor::configurable() const
{
ASSERT(isValid());
return !(m_attributes & DontDelete);
}
bool PropertyDescriptor::hasAccessors() const
bool PropertyDescriptor::isDataDescriptor() const
{
return !!(m_attributes & (Getter | Setter));
return m_value || (m_seenAttributes & WritablePresent);
}
bool PropertyDescriptor::isGenericDescriptor() const
{
return !isAccessorDescriptor() && !isDataDescriptor();
}
bool PropertyDescriptor::isAccessorDescriptor() const
{
return m_getter || m_setter;
}
void PropertyDescriptor::setUndefined()
......@@ -63,32 +74,31 @@ void PropertyDescriptor::setUndefined()
JSValue PropertyDescriptor::getter() const
{
ASSERT(hasAccessors());
if (!m_getter)
return jsUndefined();
ASSERT(isAccessorDescriptor());
return m_getter;
}
JSValue PropertyDescriptor::setter() const
{
ASSERT(hasAccessors());
if (!m_setter)
return jsUndefined();
ASSERT(isAccessorDescriptor());
return m_setter;
}
void PropertyDescriptor::setDescriptor(JSValue value, unsigned attributes)
{
ASSERT(value);
m_attributes = attributes;
if (attributes & (Getter | Setter)) {
GetterSetter* accessor = asGetterSetter(value);
m_getter = accessor->getter();
m_setter = accessor->setter();
ASSERT(m_getter || m_setter);
m_seenAttributes = EnumerablePresent | ConfigurablePresent;
m_attributes &= ~ReadOnly;
} else {
m_value = value;
m_seenAttributes = EnumerablePresent | ConfigurablePresent | WritablePresent;
}
m_attributes = attributes;
}