|  | /* | 
|  | *  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 getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) | 
|  | { | 
|  | // 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) || (mode == IncludeDontEnumProperties))) | 
|  | 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 | 
|  | { | 
|  | PropertyDescriptor descriptor; | 
|  | if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) | 
|  | return false; | 
|  | return descriptor.enumerable(); | 
|  | } | 
|  |  | 
|  | 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, EnumerationMode mode) | 
|  | { | 
|  | getOwnPropertyNames(exec, propertyNames, mode); | 
|  |  | 
|  | if (prototype().isNull()) | 
|  | return; | 
|  |  | 
|  | JSObject* prototype = asObject(this->prototype()); | 
|  | while(1) { | 
|  | if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { | 
|  | prototype->getPropertyNames(exec, propertyNames, mode); | 
|  | break; | 
|  | } | 
|  | prototype->getOwnPropertyNames(exec, propertyNames, mode); | 
|  | JSValue nextProto = prototype->prototype(); | 
|  | if (nextProto.isNull()) | 
|  | break; | 
|  | prototype = asObject(nextProto); | 
|  | } | 
|  | } | 
|  |  | 
|  | void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) | 
|  | { | 
|  | m_structure->getPropertyNames(propertyNames, mode); | 
|  | getClassPropertyNames(exec, classInfo(), propertyNames, mode); | 
|  | } | 
|  |  | 
|  | 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)), function, attr); | 
|  | } | 
|  |  | 
|  | void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) | 
|  | { | 
|  | putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); | 
|  | } | 
|  |  | 
|  | NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) | 
|  | { | 
|  | if (JSObject* getterFunction = asGetterSetter(*location)->getter()) { | 
|  | if (!structure()->isDictionary()) | 
|  | slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); | 
|  | else | 
|  | 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(exec, 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(exec, 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(exec, 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(exec, 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 |