| /* |
| * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) |
| * Copyright (C) 2001 Peter Kelly (pmk@post.com) |
| * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. |
| * Copyright (C) 2007 Eric Seidel (eric@webkit.org) |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "JSObject.h" |
| |
| #include "DatePrototype.h" |
| #include "ErrorConstructor.h" |
| #include "GetterSetter.h" |
| #include "JSGlobalObject.h" |
| #include "NativeErrorConstructor.h" |
| #include "ObjectPrototype.h" |
| #include "PropertyDescriptor.h" |
| #include "PropertyNameArray.h" |
| #include "Lookup.h" |
| #include "Nodes.h" |
| #include "Operations.h" |
| #include <math.h> |
| #include <wtf/Assertions.h> |
| |
| namespace JSC { |
| |
| ASSERT_CLASS_FITS_IN_CELL(JSObject); |
| |
| static inline void getEnumerablePropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames) |
| { |
| // Add properties from the static hashtables of properties |
| for (; classInfo; classInfo = classInfo->parentClass) { |
| const HashTable* table = classInfo->propHashTable(exec); |
| if (!table) |
| continue; |
| table->initializeIfNeeded(exec); |
| ASSERT(table->table); |
| |
| int hashSizeMask = table->compactSize - 1; |
| const HashEntry* entry = table->table; |
| for (int i = 0; i <= hashSizeMask; ++i, ++entry) { |
| if (entry->key() && !(entry->attributes() & DontEnum)) |
| propertyNames.add(entry->key()); |
| } |
| } |
| } |
| |
| void JSObject::markChildren(MarkStack& markStack) |
| { |
| #ifndef NDEBUG |
| bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation; |
| markStack.m_isCheckingForDefaultMarkViolation = false; |
| #endif |
| |
| markChildrenDirect(markStack); |
| |
| #ifndef NDEBUG |
| markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; |
| #endif |
| } |
| |
| UString JSObject::className() const |
| { |
| const ClassInfo* info = classInfo(); |
| if (info) |
| return info->className; |
| return "Object"; |
| } |
| |
| bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) |
| { |
| return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); |
| } |
| |
| static void throwSetterError(ExecState* exec) |
| { |
| throwError(exec, TypeError, "setting a property that has only a getter"); |
| } |
| |
| // ECMA 8.6.2.2 |
| void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
| { |
| ASSERT(value); |
| ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); |
| |
| if (propertyName == exec->propertyNames().underscoreProto) { |
| // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. |
| if (!value.isObject() && !value.isNull()) |
| return; |
| |
| JSValue nextPrototypeValue = value; |
| while (nextPrototypeValue && nextPrototypeValue.isObject()) { |
| JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); |
| if (nextPrototype == this) { |
| throwError(exec, GeneralError, "cyclic __proto__ value"); |
| return; |
| } |
| nextPrototypeValue = nextPrototype->prototype(); |
| } |
| |
| setPrototype(value); |
| return; |
| } |
| |
| // Check if there are any setters or getters in the prototype chain |
| JSValue prototype; |
| for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { |
| prototype = obj->prototype(); |
| if (prototype.isNull()) { |
| putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); |
| return; |
| } |
| } |
| |
| unsigned attributes; |
| JSCell* specificValue; |
| if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) |
| return; |
| |
| for (JSObject* obj = this; ; obj = asObject(prototype)) { |
| if (JSValue gs = obj->getDirect(propertyName)) { |
| if (gs.isGetterSetter()) { |
| JSObject* setterFunc = asGetterSetter(gs)->setter(); |
| if (!setterFunc) { |
| throwSetterError(exec); |
| return; |
| } |
| |
| CallData callData; |
| CallType callType = setterFunc->getCallData(callData); |
| MarkedArgumentBuffer args; |
| args.append(value); |
| call(exec, setterFunc, callType, callData, this, args); |
| return; |
| } |
| |
| // If there's an existing property on the object or one of its |
| // prototypes it should be replaced, so break here. |
| break; |
| } |
| |
| prototype = obj->prototype(); |
| if (prototype.isNull()) |
| break; |
| } |
| |
| putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot); |
| return; |
| } |
| |
| void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value) |
| { |
| PutPropertySlot slot; |
| put(exec, Identifier::from(exec, propertyName), value, slot); |
| } |
| |
| void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) |
| { |
| putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot); |
| } |
| |
| void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) |
| { |
| putDirectInternal(exec->globalData(), propertyName, value, attributes); |
| } |
| |
| void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) |
| { |
| putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); |
| } |
| |
| bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const |
| { |
| PropertySlot slot; |
| return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); |
| } |
| |
| bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const |
| { |
| PropertySlot slot; |
| return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); |
| } |
| |
| // ECMA 8.6.2.5 |
| bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) |
| { |
| unsigned attributes; |
| JSCell* specificValue; |
| if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) { |
| if ((attributes & DontDelete)) |
| return false; |
| removeDirect(propertyName); |
| return true; |
| } |
| |
| // Look in the static hashtable of properties |
| const HashEntry* entry = findPropertyHashEntry(exec, propertyName); |
| if (entry && entry->attributes() & DontDelete) |
| return false; // this builtin property can't be deleted |
| |
| // FIXME: Should the code here actually do some deletion? |
| return true; |
| } |
| |
| bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const |
| { |
| PropertySlot slot; |
| return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot); |
| } |
| |
| bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName) |
| { |
| return deleteProperty(exec, Identifier::from(exec, propertyName)); |
| } |
| |
| static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) |
| { |
| JSValue function = object->get(exec, propertyName); |
| CallData callData; |
| CallType callType = function.getCallData(callData); |
| if (callType == CallTypeNone) |
| return exec->exception(); |
| |
| // Prevent "toString" and "valueOf" from observing execution if an exception |
| // is pending. |
| if (exec->hadException()) |
| return exec->exception(); |
| |
| JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList()); |
| ASSERT(!result.isGetterSetter()); |
| if (exec->hadException()) |
| return exec->exception(); |
| if (result.isObject()) |
| return JSValue(); |
| return result; |
| } |
| |
| bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) |
| { |
| result = defaultValue(exec, PreferNumber); |
| number = result.toNumber(exec); |
| return !result.isString(); |
| } |
| |
| // ECMA 8.6.2.6 |
| JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const |
| { |
| // Must call toString first for Date objects. |
| if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) { |
| JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); |
| if (value) |
| return value; |
| value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); |
| if (value) |
| return value; |
| } else { |
| JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); |
| if (value) |
| return value; |
| value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); |
| if (value) |
| return value; |
| } |
| |
| ASSERT(!exec->hadException()); |
| |
| return throwError(exec, TypeError, "No default value"); |
| } |
| |
| const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const |
| { |
| for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { |
| if (const HashTable* propHashTable = info->propHashTable(exec)) { |
| if (const HashEntry* entry = propHashTable->entry(exec, propertyName)) |
| return entry; |
| } |
| } |
| return 0; |
| } |
| |
| void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) |
| { |
| JSValue object = getDirect(propertyName); |
| if (object && object.isGetterSetter()) { |
| ASSERT(m_structure->hasGetterSetterProperties()); |
| asGetterSetter(object)->setGetter(getterFunction); |
| return; |
| } |
| |
| PutPropertySlot slot; |
| GetterSetter* getterSetter = new (exec) GetterSetter(exec); |
| 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 |
| // if we override an existing non-getter or non-setter. |
| if (slot.type() != PutPropertySlot::NewProperty) { |
| if (!m_structure->isDictionary()) { |
| RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); |
| setStructure(structure.release()); |
| } |
| } |
| |
| m_structure->setHasGetterSetterProperties(true); |
| getterSetter->setGetter(getterFunction); |
| } |
| |
| void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) |
| { |
| JSValue object = getDirect(propertyName); |
| if (object && object.isGetterSetter()) { |
| ASSERT(m_structure->hasGetterSetterProperties()); |
| asGetterSetter(object)->setSetter(setterFunction); |
| return; |
| } |
| |
| PutPropertySlot slot; |
| GetterSetter* getterSetter = new (exec) GetterSetter(exec); |
| 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 |
| // if we override an existing non-getter or non-setter. |
| if (slot.type() != PutPropertySlot::NewProperty) { |
| if (!m_structure->isDictionary()) { |
| RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); |
| setStructure(structure.release()); |
| } |
| } |
| |
| m_structure->setHasGetterSetterProperties(true); |
| getterSetter->setSetter(setterFunction); |
| } |
| |
| JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName) |
| { |
| JSObject* object = this; |
| while (true) { |
| if (JSValue value = object->getDirect(propertyName)) { |
| if (!value.isGetterSetter()) |
| return jsUndefined(); |
| JSObject* functionObject = asGetterSetter(value)->getter(); |
| if (!functionObject) |
| return jsUndefined(); |
| return functionObject; |
| } |
| |
| if (!object->prototype() || !object->prototype().isObject()) |
| return jsUndefined(); |
| object = asObject(object->prototype()); |
| } |
| } |
| |
| JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName) |
| { |
| JSObject* object = this; |
| while (true) { |
| if (JSValue value = object->getDirect(propertyName)) { |
| if (!value.isGetterSetter()) |
| return jsUndefined(); |
| JSObject* functionObject = asGetterSetter(value)->setter(); |
| if (!functionObject) |
| return jsUndefined(); |
| return functionObject; |
| } |
| |
| if (!object->prototype() || !object->prototype().isObject()) |
| return jsUndefined(); |
| object = asObject(object->prototype()); |
| } |
| } |
| |
| bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) |
| { |
| if (!value.isObject()) |
| return false; |
| |
| if (!proto.isObject()) { |
| throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property."); |
| return false; |
| } |
| |
| JSObject* object = asObject(value); |
| while ((object = object->prototype().getObject())) { |
| if (proto == object) |
| return true; |
| } |
| return false; |
| } |
| |
| bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const |
| { |
| unsigned attributes; |
| if (!getPropertyAttributes(exec, propertyName, attributes)) |
| return false; |
| return !(attributes & DontEnum); |
| } |
| |
| bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const |
| { |
| JSCell* specificValue; |
| if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) |
| return true; |
| |
| // Look in the static hashtable of properties |
| const HashEntry* entry = findPropertyHashEntry(exec, propertyName); |
| if (entry) { |
| attributes = entry->attributes(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const |
| { |
| unsigned attributes; |
| if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) |
| return true; |
| |
| // This could be a function within the static table? - should probably |
| // also look in the hash? This currently should not be a problem, since |
| // we've currently always call 'get' first, which should have populated |
| // the normal storage. |
| return false; |
| } |
| |
| void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) |
| { |
| getOwnPropertyNames(exec, propertyNames); |
| |
| if (prototype().isNull()) |
| return; |
| |
| JSObject* prototype = asObject(this->prototype()); |
| while(1) { |
| if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { |
| prototype->getPropertyNames(exec, propertyNames); |
| break; |
| } |
| prototype->getOwnPropertyNames(exec, propertyNames); |
| JSValue nextProto = prototype->prototype(); |
| if (nextProto.isNull()) |
| break; |
| prototype = asObject(nextProto); |
| } |
| } |
| |
| void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) |
| { |
| m_structure->getEnumerablePropertyNames(propertyNames); |
| getEnumerablePropertyNames(exec, classInfo(), propertyNames); |
| } |
| |
| bool JSObject::toBoolean(ExecState*) const |
| { |
| return true; |
| } |
| |
| double JSObject::toNumber(ExecState* exec) const |
| { |
| JSValue primitive = toPrimitive(exec, PreferNumber); |
| if (exec->hadException()) // should be picked up soon in Nodes.cpp |
| return 0.0; |
| return primitive.toNumber(exec); |
| } |
| |
| UString JSObject::toString(ExecState* exec) const |
| { |
| JSValue primitive = toPrimitive(exec, PreferString); |
| if (exec->hadException()) |
| return ""; |
| return primitive.toString(exec); |
| } |
| |
| JSObject* JSObject::toObject(ExecState*) const |
| { |
| return const_cast<JSObject*>(this); |
| } |
| |
| JSObject* JSObject::toThisObject(ExecState*) const |
| { |
| return const_cast<JSObject*>(this); |
| } |
| |
| JSObject* JSObject::unwrappedObject() |
| { |
| return this; |
| } |
| |
| void JSObject::removeDirect(const Identifier& propertyName) |
| { |
| size_t offset; |
| if (m_structure->isUncacheableDictionary()) { |
| offset = m_structure->removePropertyWithoutTransition(propertyName); |
| if (offset != WTF::notFound) |
| putDirectOffset(offset, jsUndefined()); |
| return; |
| } |
| |
| RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset); |
| setStructure(structure.release()); |
| if (offset != WTF::notFound) |
| putDirectOffset(offset, jsUndefined()); |
| } |
| |
| void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) |
| { |
| putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr); |
| } |
| |
| void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) |
| { |
| putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr); |
| } |
| |
| NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) |
| { |
| if (JSObject* getterFunction = asGetterSetter(*location)->getter()) |
| slot.setGetterSlot(getterFunction); |
| else |
| slot.setUndefined(); |
| } |
| |
| Structure* JSObject::createInheritorID() |
| { |
| m_inheritorID = JSObject::createStructure(this); |
| return m_inheritorID.get(); |
| } |
| |
| void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) |
| { |
| allocatePropertyStorageInline(oldSize, newSize); |
| } |
| |
| bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor) |
| { |
| unsigned attributes = 0; |
| JSCell* cell = 0; |
| size_t offset = m_structure->get(propertyName, attributes, cell); |
| if (offset == WTF::notFound) |
| return false; |
| descriptor.setDescriptor(getDirectOffset(offset), attributes); |
| return true; |
| } |
| |
| bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) |
| { |
| JSObject* object = this; |
| while (true) { |
| if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) |
| return true; |
| JSValue prototype = object->prototype(); |
| if (!prototype.isObject()) |
| return false; |
| 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 |