Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2001 Harri Porten (porten (at) kde.org)
      3  *  Copyright (C) 2001 Peter Kelly (pmk (at) post.com)
      4  *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
      5  *  Copyright (C) 2007 Eric Seidel (eric (at) webkit.org)
      6  *
      7  *  This library is free software; you can redistribute it and/or
      8  *  modify it under the terms of the GNU Library General Public
      9  *  License as published by the Free Software Foundation; either
     10  *  version 2 of the License, or (at your option) any later version.
     11  *
     12  *  This library is distributed in the hope that it will be useful,
     13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  *  Library General Public License for more details.
     16  *
     17  *  You should have received a copy of the GNU Library General Public License
     18  *  along with this library; see the file COPYING.LIB.  If not, write to
     19  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  *  Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "JSObject.h"
     26 
     27 #include "DatePrototype.h"
     28 #include "ErrorConstructor.h"
     29 #include "GetterSetter.h"
     30 #include "JSGlobalObject.h"
     31 #include "NativeErrorConstructor.h"
     32 #include "ObjectPrototype.h"
     33 #include "PropertyDescriptor.h"
     34 #include "PropertyNameArray.h"
     35 #include "Lookup.h"
     36 #include "Nodes.h"
     37 #include "Operations.h"
     38 #include <math.h>
     39 #include <wtf/Assertions.h>
     40 
     41 namespace JSC {
     42 
     43 ASSERT_CLASS_FITS_IN_CELL(JSObject);
     44 
     45 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
     46 {
     47     // Add properties from the static hashtables of properties
     48     for (; classInfo; classInfo = classInfo->parentClass) {
     49         const HashTable* table = classInfo->propHashTable(exec);
     50         if (!table)
     51             continue;
     52         table->initializeIfNeeded(exec);
     53         ASSERT(table->table);
     54 
     55         int hashSizeMask = table->compactSize - 1;
     56         const HashEntry* entry = table->table;
     57         for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
     58             if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
     59                 propertyNames.add(entry->key());
     60         }
     61     }
     62 }
     63 
     64 void JSObject::markChildren(MarkStack& markStack)
     65 {
     66 #ifndef NDEBUG
     67     bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
     68     markStack.m_isCheckingForDefaultMarkViolation = false;
     69 #endif
     70 
     71     markChildrenDirect(markStack);
     72 
     73 #ifndef NDEBUG
     74     markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
     75 #endif
     76 }
     77 
     78 UString JSObject::className() const
     79 {
     80     const ClassInfo* info = classInfo();
     81     if (info)
     82         return info->className;
     83     return "Object";
     84 }
     85 
     86 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
     87 {
     88     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
     89 }
     90 
     91 static void throwSetterError(ExecState* exec)
     92 {
     93     throwError(exec, TypeError, "setting a property that has only a getter");
     94 }
     95 
     96 // ECMA 8.6.2.2
     97 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
     98 {
     99     ASSERT(value);
    100     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
    101 
    102     if (propertyName == exec->propertyNames().underscoreProto) {
    103         // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
    104         if (!value.isObject() && !value.isNull())
    105             return;
    106 
    107         JSValue nextPrototypeValue = value;
    108         while (nextPrototypeValue && nextPrototypeValue.isObject()) {
    109             JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
    110             if (nextPrototype == this) {
    111                 throwError(exec, GeneralError, "cyclic __proto__ value");
    112                 return;
    113             }
    114             nextPrototypeValue = nextPrototype->prototype();
    115         }
    116 
    117         setPrototype(value);
    118         return;
    119     }
    120 
    121     // Check if there are any setters or getters in the prototype chain
    122     JSValue prototype;
    123     for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
    124         prototype = obj->prototype();
    125         if (prototype.isNull()) {
    126             putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
    127             return;
    128         }
    129     }
    130 
    131     unsigned attributes;
    132     JSCell* specificValue;
    133     if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
    134         return;
    135 
    136     for (JSObject* obj = this; ; obj = asObject(prototype)) {
    137         if (JSValue gs = obj->getDirect(propertyName)) {
    138             if (gs.isGetterSetter()) {
    139                 JSObject* setterFunc = asGetterSetter(gs)->setter();
    140                 if (!setterFunc) {
    141                     throwSetterError(exec);
    142                     return;
    143                 }
    144 
    145                 CallData callData;
    146                 CallType callType = setterFunc->getCallData(callData);
    147                 MarkedArgumentBuffer args;
    148                 args.append(value);
    149                 call(exec, setterFunc, callType, callData, this, args);
    150                 return;
    151             }
    152 
    153             // If there's an existing property on the object or one of its
    154             // prototypes it should be replaced, so break here.
    155             break;
    156         }
    157 
    158         prototype = obj->prototype();
    159         if (prototype.isNull())
    160             break;
    161     }
    162 
    163     putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
    164     return;
    165 }
    166 
    167 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
    168 {
    169     PutPropertySlot slot;
    170     put(exec, Identifier::from(exec, propertyName), value, slot);
    171 }
    172 
    173 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
    174 {
    175     putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
    176 }
    177 
    178 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
    179 {
    180     putDirectInternal(exec->globalData(), propertyName, value, attributes);
    181 }
    182 
    183 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
    184 {
    185     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
    186 }
    187 
    188 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
    189 {
    190     PropertySlot slot;
    191     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
    192 }
    193 
    194 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
    195 {
    196     PropertySlot slot;
    197     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
    198 }
    199 
    200 // ECMA 8.6.2.5
    201 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
    202 {
    203     unsigned attributes;
    204     JSCell* specificValue;
    205     if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
    206         if ((attributes & DontDelete))
    207             return false;
    208         removeDirect(propertyName);
    209         return true;
    210     }
    211 
    212     // Look in the static hashtable of properties
    213     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
    214     if (entry && entry->attributes() & DontDelete)
    215         return false; // this builtin property can't be deleted
    216 
    217     // FIXME: Should the code here actually do some deletion?
    218     return true;
    219 }
    220 
    221 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
    222 {
    223     PropertySlot slot;
    224     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
    225 }
    226 
    227 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
    228 {
    229     return deleteProperty(exec, Identifier::from(exec, propertyName));
    230 }
    231 
    232 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
    233 {
    234     JSValue function = object->get(exec, propertyName);
    235     CallData callData;
    236     CallType callType = function.getCallData(callData);
    237     if (callType == CallTypeNone)
    238         return exec->exception();
    239 
    240     // Prevent "toString" and "valueOf" from observing execution if an exception
    241     // is pending.
    242     if (exec->hadException())
    243         return exec->exception();
    244 
    245     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
    246     ASSERT(!result.isGetterSetter());
    247     if (exec->hadException())
    248         return exec->exception();
    249     if (result.isObject())
    250         return JSValue();
    251     return result;
    252 }
    253 
    254 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
    255 {
    256     result = defaultValue(exec, PreferNumber);
    257     number = result.toNumber(exec);
    258     return !result.isString();
    259 }
    260 
    261 // ECMA 8.6.2.6
    262 JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
    263 {
    264     // Must call toString first for Date objects.
    265     if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
    266         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
    267         if (value)
    268             return value;
    269         value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
    270         if (value)
    271             return value;
    272     } else {
    273         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
    274         if (value)
    275             return value;
    276         value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
    277         if (value)
    278             return value;
    279     }
    280 
    281     ASSERT(!exec->hadException());
    282 
    283     return throwError(exec, TypeError, "No default value");
    284 }
    285 
    286 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
    287 {
    288     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
    289         if (const HashTable* propHashTable = info->propHashTable(exec)) {
    290             if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
    291                 return entry;
    292         }
    293     }
    294     return 0;
    295 }
    296 
    297 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
    298 {
    299     JSValue object = getDirect(propertyName);
    300     if (object && object.isGetterSetter()) {
    301         ASSERT(m_structure->hasGetterSetterProperties());
    302         asGetterSetter(object)->setGetter(getterFunction);
    303         return;
    304     }
    305 
    306     PutPropertySlot slot;
    307     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
    308     putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
    309 
    310     // putDirect will change our Structure if we add a new property. For
    311     // getters and setters, though, we also need to change our Structure
    312     // if we override an existing non-getter or non-setter.
    313     if (slot.type() != PutPropertySlot::NewProperty) {
    314         if (!m_structure->isDictionary()) {
    315             RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
    316             setStructure(structure.release());
    317         }
    318     }
    319 
    320     m_structure->setHasGetterSetterProperties(true);
    321     getterSetter->setGetter(getterFunction);
    322 }
    323 
    324 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
    325 {
    326     JSValue object = getDirect(propertyName);
    327     if (object && object.isGetterSetter()) {
    328         ASSERT(m_structure->hasGetterSetterProperties());
    329         asGetterSetter(object)->setSetter(setterFunction);
    330         return;
    331     }
    332 
    333     PutPropertySlot slot;
    334     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
    335     putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
    336 
    337     // putDirect will change our Structure if we add a new property. For
    338     // getters and setters, though, we also need to change our Structure
    339     // if we override an existing non-getter or non-setter.
    340     if (slot.type() != PutPropertySlot::NewProperty) {
    341         if (!m_structure->isDictionary()) {
    342             RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
    343             setStructure(structure.release());
    344         }
    345     }
    346 
    347     m_structure->setHasGetterSetterProperties(true);
    348     getterSetter->setSetter(setterFunction);
    349 }
    350 
    351 JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
    352 {
    353     JSObject* object = this;
    354     while (true) {
    355         if (JSValue value = object->getDirect(propertyName)) {
    356             if (!value.isGetterSetter())
    357                 return jsUndefined();
    358             JSObject* functionObject = asGetterSetter(value)->getter();
    359             if (!functionObject)
    360                 return jsUndefined();
    361             return functionObject;
    362         }
    363 
    364         if (!object->prototype() || !object->prototype().isObject())
    365             return jsUndefined();
    366         object = asObject(object->prototype());
    367     }
    368 }
    369 
    370 JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
    371 {
    372     JSObject* object = this;
    373     while (true) {
    374         if (JSValue value = object->getDirect(propertyName)) {
    375             if (!value.isGetterSetter())
    376                 return jsUndefined();
    377             JSObject* functionObject = asGetterSetter(value)->setter();
    378             if (!functionObject)
    379                 return jsUndefined();
    380             return functionObject;
    381         }
    382 
    383         if (!object->prototype() || !object->prototype().isObject())
    384             return jsUndefined();
    385         object = asObject(object->prototype());
    386     }
    387 }
    388 
    389 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
    390 {
    391     if (!value.isObject())
    392         return false;
    393 
    394     if (!proto.isObject()) {
    395         throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
    396         return false;
    397     }
    398 
    399     JSObject* object = asObject(value);
    400     while ((object = object->prototype().getObject())) {
    401         if (proto == object)
    402             return true;
    403     }
    404     return false;
    405 }
    406 
    407 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
    408 {
    409     PropertyDescriptor descriptor;
    410     if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
    411         return false;
    412     return descriptor.enumerable();
    413 }
    414 
    415 bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
    416 {
    417     unsigned attributes;
    418     if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
    419         return true;
    420 
    421     // This could be a function within the static table? - should probably
    422     // also look in the hash?  This currently should not be a problem, since
    423     // we've currently always call 'get' first, which should have populated
    424     // the normal storage.
    425     return false;
    426 }
    427 
    428 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    429 {
    430     getOwnPropertyNames(exec, propertyNames, mode);
    431 
    432     if (prototype().isNull())
    433         return;
    434 
    435     JSObject* prototype = asObject(this->prototype());
    436     while(1) {
    437         if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
    438             prototype->getPropertyNames(exec, propertyNames, mode);
    439             break;
    440         }
    441         prototype->getOwnPropertyNames(exec, propertyNames, mode);
    442         JSValue nextProto = prototype->prototype();
    443         if (nextProto.isNull())
    444             break;
    445         prototype = asObject(nextProto);
    446     }
    447 }
    448 
    449 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    450 {
    451     m_structure->getPropertyNames(propertyNames, mode);
    452     getClassPropertyNames(exec, classInfo(), propertyNames, mode);
    453 }
    454 
    455 bool JSObject::toBoolean(ExecState*) const
    456 {
    457     return true;
    458 }
    459 
    460 double JSObject::toNumber(ExecState* exec) const
    461 {
    462     JSValue primitive = toPrimitive(exec, PreferNumber);
    463     if (exec->hadException()) // should be picked up soon in Nodes.cpp
    464         return 0.0;
    465     return primitive.toNumber(exec);
    466 }
    467 
    468 UString JSObject::toString(ExecState* exec) const
    469 {
    470     JSValue primitive = toPrimitive(exec, PreferString);
    471     if (exec->hadException())
    472         return "";
    473     return primitive.toString(exec);
    474 }
    475 
    476 JSObject* JSObject::toObject(ExecState*) const
    477 {
    478     return const_cast<JSObject*>(this);
    479 }
    480 
    481 JSObject* JSObject::toThisObject(ExecState*) const
    482 {
    483     return const_cast<JSObject*>(this);
    484 }
    485 
    486 JSObject* JSObject::unwrappedObject()
    487 {
    488     return this;
    489 }
    490 
    491 void JSObject::removeDirect(const Identifier& propertyName)
    492 {
    493     size_t offset;
    494     if (m_structure->isUncacheableDictionary()) {
    495         offset = m_structure->removePropertyWithoutTransition(propertyName);
    496         if (offset != WTF::notFound)
    497             putDirectOffset(offset, jsUndefined());
    498         return;
    499     }
    500 
    501     RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
    502     setStructure(structure.release());
    503     if (offset != WTF::notFound)
    504         putDirectOffset(offset, jsUndefined());
    505 }
    506 
    507 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
    508 {
    509     putDirectFunction(Identifier(exec, function->name(exec)), function, attr);
    510 }
    511 
    512 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
    513 {
    514     putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr);
    515 }
    516 
    517 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
    518 {
    519     if (JSObject* getterFunction = asGetterSetter(*location)->getter())
    520         slot.setGetterSlot(getterFunction);
    521     else
    522         slot.setUndefined();
    523 }
    524 
    525 Structure* JSObject::createInheritorID()
    526 {
    527     m_inheritorID = JSObject::createStructure(this);
    528     return m_inheritorID.get();
    529 }
    530 
    531 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
    532 {
    533     allocatePropertyStorageInline(oldSize, newSize);
    534 }
    535 
    536 bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
    537 {
    538     unsigned attributes = 0;
    539     JSCell* cell = 0;
    540     size_t offset = m_structure->get(propertyName, attributes, cell);
    541     if (offset == WTF::notFound)
    542         return false;
    543     descriptor.setDescriptor(getDirectOffset(offset), attributes);
    544     return true;
    545 }
    546 
    547 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    548 {
    549     JSObject* object = this;
    550     while (true) {
    551         if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
    552             return true;
    553         JSValue prototype = object->prototype();
    554         if (!prototype.isObject())
    555             return false;
    556         object = asObject(prototype);
    557     }
    558 }
    559 
    560 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
    561 {
    562     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
    563         target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
    564         return true;
    565     }
    566     attributes &= ~ReadOnly;
    567     if (descriptor.getter() && descriptor.getter().isObject())
    568         target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
    569     if (exec->hadException())
    570         return false;
    571     if (descriptor.setter() && descriptor.setter().isObject())
    572         target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
    573     return !exec->hadException();
    574 }
    575 
    576 bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
    577 {
    578     // If we have a new property we can just put it on normally
    579     PropertyDescriptor current;
    580     if (!getOwnPropertyDescriptor(exec, propertyName, current))
    581         return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
    582 
    583     if (descriptor.isEmpty())
    584         return true;
    585 
    586     if (current.equalTo(exec, descriptor))
    587         return true;
    588 
    589     // Filter out invalid changes
    590     if (!current.configurable()) {
    591         if (descriptor.configurable()) {
    592             if (throwException)
    593                 throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
    594             return false;
    595         }
    596         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
    597             if (throwException)
    598                 throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
    599             return false;
    600         }
    601     }
    602 
    603     // A generic descriptor is simply changing the attributes of an existing property
    604     if (descriptor.isGenericDescriptor()) {
    605         if (!current.attributesEqual(descriptor)) {
    606             deleteProperty(exec, propertyName);
    607             putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
    608         }
    609         return true;
    610     }
    611 
    612     // Changing between a normal property or an accessor property
    613     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
    614         if (!current.configurable()) {
    615             if (throwException)
    616                 throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
    617             return false;
    618         }
    619         deleteProperty(exec, propertyName);
    620         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
    621     }
    622 
    623     // Changing the value and attributes of an existing property
    624     if (descriptor.isDataDescriptor()) {
    625         if (!current.configurable()) {
    626             if (!current.writable() && descriptor.writable()) {
    627                 if (throwException)
    628                     throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
    629                 return false;
    630             }
    631             if (!current.writable()) {
    632                 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
    633                     if (throwException)
    634                         throwError(exec, TypeError, "Attempting to change value of a readonly property.");
    635                     return false;
    636                 }
    637             }
    638         } else if (current.attributesEqual(descriptor)) {
    639             if (!descriptor.value())
    640                 return true;
    641             PutPropertySlot slot;
    642             put(exec, propertyName, descriptor.value(), slot);
    643             if (exec->hadException())
    644                 return false;
    645             return true;
    646         }
    647         deleteProperty(exec, propertyName);
    648         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
    649     }
    650 
    651     // Changing the accessor functions of an existing accessor property
    652     ASSERT(descriptor.isAccessorDescriptor());
    653     if (!current.configurable()) {
    654         if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
    655             if (throwException)
    656                 throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
    657             return false;
    658         }
    659         if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
    660             if (throwException)
    661                 throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
    662             return false;
    663         }
    664     }
    665     JSValue accessor = getDirect(propertyName);
    666     if (!accessor)
    667         return false;
    668     GetterSetter* getterSetter = asGetterSetter(accessor);
    669     if (current.attributesEqual(descriptor)) {
    670         if (descriptor.setter())
    671             getterSetter->setSetter(asObject(descriptor.setter()));
    672         if (descriptor.getter())
    673             getterSetter->setGetter(asObject(descriptor.getter()));
    674         return true;
    675     }
    676     deleteProperty(exec, propertyName);
    677     unsigned attrs = current.attributesWithOverride(descriptor);
    678     if (descriptor.setter())
    679         attrs |= Setter;
    680     if (descriptor.getter())
    681         attrs |= Getter;
    682     putDirect(propertyName, getterSetter, attrs);
    683     return true;
    684 }
    685 
    686 } // namespace JSC
    687