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 "JSFunction.h"
     31 #include "JSGlobalObject.h"
     32 #include "NativeErrorConstructor.h"
     33 #include "ObjectPrototype.h"
     34 #include "PropertyDescriptor.h"
     35 #include "PropertyNameArray.h"
     36 #include "Lookup.h"
     37 #include "Nodes.h"
     38 #include "Operations.h"
     39 #include <math.h>
     40 #include <wtf/Assertions.h>
     41 
     42 namespace JSC {
     43 
     44 ASSERT_CLASS_FITS_IN_CELL(JSObject);
     45 ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject);
     46 ASSERT_CLASS_FITS_IN_CELL(JSFinalObject);
     47 
     48 const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
     49 
     50 const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 };
     51 
     52 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
     53 {
     54     // Add properties from the static hashtables of properties
     55     for (; classInfo; classInfo = classInfo->parentClass) {
     56         const HashTable* table = classInfo->propHashTable(exec);
     57         if (!table)
     58             continue;
     59         table->initializeIfNeeded(exec);
     60         ASSERT(table->table);
     61 
     62         int hashSizeMask = table->compactSize - 1;
     63         const HashEntry* entry = table->table;
     64         for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
     65             if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
     66                 propertyNames.add(entry->key());
     67         }
     68     }
     69 }
     70 
     71 void JSObject::markChildren(MarkStack& markStack)
     72 {
     73 #ifndef NDEBUG
     74     bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
     75     markStack.m_isCheckingForDefaultMarkViolation = false;
     76 #endif
     77 
     78     markChildrenDirect(markStack);
     79 
     80 #ifndef NDEBUG
     81     markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
     82 #endif
     83 }
     84 
     85 UString JSObject::className() const
     86 {
     87     const ClassInfo* info = classInfo();
     88     ASSERT(info);
     89     return info->className;
     90 }
     91 
     92 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
     93 {
     94     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
     95 }
     96 
     97 static void throwSetterError(ExecState* exec)
     98 {
     99     throwError(exec, createTypeError(exec, "setting a property that has only a getter"));
    100 }
    101 
    102 // ECMA 8.6.2.2
    103 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
    104 {
    105     ASSERT(value);
    106     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
    107 
    108     if (propertyName == exec->propertyNames().underscoreProto) {
    109         // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
    110         if (!value.isObject() && !value.isNull())
    111             return;
    112         if (!setPrototypeWithCycleCheck(exec->globalData(), value))
    113             throwError(exec, createError(exec, "cyclic __proto__ value"));
    114         return;
    115     }
    116 
    117     // Check if there are any setters or getters in the prototype chain
    118     JSValue prototype;
    119     for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
    120         prototype = obj->prototype();
    121         if (prototype.isNull()) {
    122             if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
    123                 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
    124             return;
    125         }
    126     }
    127 
    128     unsigned attributes;
    129     JSCell* specificValue;
    130     if ((m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) {
    131         if (slot.isStrictMode())
    132             throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
    133         return;
    134     }
    135 
    136     for (JSObject* obj = this; ; obj = asObject(prototype)) {
    137         if (JSValue gs = obj->getDirect(exec->globalData(), 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     if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
    164         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
    165     return;
    166 }
    167 
    168 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
    169 {
    170     PutPropertySlot slot;
    171     put(exec, Identifier::from(exec, propertyName), value, slot);
    172 }
    173 
    174 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
    175 {
    176     putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot);
    177 }
    178 
    179 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
    180 {
    181     putDirectInternal(*globalData, propertyName, value, attributes);
    182 }
    183 
    184 void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes)
    185 {
    186     putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes);
    187 }
    188 
    189 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
    190 {
    191     putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
    192 }
    193 
    194 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
    195 {
    196     putDirectInternal(exec->globalData(), propertyName, value, attributes);
    197 }
    198 
    199 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
    200 {
    201     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
    202 }
    203 
    204 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
    205 {
    206     PropertySlot slot;
    207     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
    208 }
    209 
    210 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
    211 {
    212     PropertySlot slot;
    213     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
    214 }
    215 
    216 // ECMA 8.6.2.5
    217 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
    218 {
    219     unsigned attributes;
    220     JSCell* specificValue;
    221     if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
    222         if ((attributes & DontDelete))
    223             return false;
    224         removeDirect(exec->globalData(), propertyName);
    225         return true;
    226     }
    227 
    228     // Look in the static hashtable of properties
    229     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
    230     if (entry && entry->attributes() & DontDelete)
    231         return false; // this builtin property can't be deleted
    232 
    233     // FIXME: Should the code here actually do some deletion?
    234     return true;
    235 }
    236 
    237 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
    238 {
    239     PropertySlot slot;
    240     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
    241 }
    242 
    243 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
    244 {
    245     return deleteProperty(exec, Identifier::from(exec, propertyName));
    246 }
    247 
    248 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
    249 {
    250     JSValue function = object->get(exec, propertyName);
    251     CallData callData;
    252     CallType callType = getCallData(function, callData);
    253     if (callType == CallTypeNone)
    254         return exec->exception();
    255 
    256     // Prevent "toString" and "valueOf" from observing execution if an exception
    257     // is pending.
    258     if (exec->hadException())
    259         return exec->exception();
    260 
    261     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
    262     ASSERT(!result.isGetterSetter());
    263     if (exec->hadException())
    264         return exec->exception();
    265     if (result.isObject())
    266         return JSValue();
    267     return result;
    268 }
    269 
    270 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
    271 {
    272     result = defaultValue(exec, PreferNumber);
    273     number = result.toNumber(exec);
    274     return !result.isString();
    275 }
    276 
    277 // ECMA 8.6.2.6
    278 JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
    279 {
    280     // Must call toString first for Date objects.
    281     if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
    282         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
    283         if (value)
    284             return value;
    285         value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
    286         if (value)
    287             return value;
    288     } else {
    289         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
    290         if (value)
    291             return value;
    292         value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
    293         if (value)
    294             return value;
    295     }
    296 
    297     ASSERT(!exec->hadException());
    298 
    299     return throwError(exec, createTypeError(exec, "No default value"));
    300 }
    301 
    302 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
    303 {
    304     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
    305         if (const HashTable* propHashTable = info->propHashTable(exec)) {
    306             if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
    307                 return entry;
    308         }
    309     }
    310     return 0;
    311 }
    312 
    313 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
    314 {
    315     JSValue object = getDirect(exec->globalData(), propertyName);
    316     if (object && object.isGetterSetter()) {
    317         ASSERT(m_structure->hasGetterSetterProperties());
    318         asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
    319         return;
    320     }
    321 
    322     JSGlobalData& globalData = exec->globalData();
    323     PutPropertySlot slot;
    324     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
    325     putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot);
    326 
    327     // putDirect will change our Structure if we add a new property. For
    328     // getters and setters, though, we also need to change our Structure
    329     // if we override an existing non-getter or non-setter.
    330     if (slot.type() != PutPropertySlot::NewProperty) {
    331         if (!m_structure->isDictionary())
    332             setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get()));
    333     }
    334 
    335     m_structure->setHasGetterSetterProperties(true);
    336     getterSetter->setGetter(globalData, getterFunction);
    337 }
    338 
    339 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
    340 {
    341     JSValue object = getDirect(exec->globalData(), propertyName);
    342     if (object && object.isGetterSetter()) {
    343         ASSERT(m_structure->hasGetterSetterProperties());
    344         asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
    345         return;
    346     }
    347 
    348     PutPropertySlot slot;
    349     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
    350     putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
    351 
    352     // putDirect will change our Structure if we add a new property. For
    353     // getters and setters, though, we also need to change our Structure
    354     // if we override an existing non-getter or non-setter.
    355     if (slot.type() != PutPropertySlot::NewProperty) {
    356         if (!m_structure->isDictionary())
    357             setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get()));
    358     }
    359 
    360     m_structure->setHasGetterSetterProperties(true);
    361     getterSetter->setSetter(exec->globalData(), setterFunction);
    362 }
    363 
    364 JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName)
    365 {
    366     JSObject* object = this;
    367     while (true) {
    368         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
    369             if (!value.isGetterSetter())
    370                 return jsUndefined();
    371             JSObject* functionObject = asGetterSetter(value)->getter();
    372             if (!functionObject)
    373                 return jsUndefined();
    374             return functionObject;
    375         }
    376 
    377         if (!object->prototype() || !object->prototype().isObject())
    378             return jsUndefined();
    379         object = asObject(object->prototype());
    380     }
    381 }
    382 
    383 JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName)
    384 {
    385     JSObject* object = this;
    386     while (true) {
    387         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
    388             if (!value.isGetterSetter())
    389                 return jsUndefined();
    390             JSObject* functionObject = asGetterSetter(value)->setter();
    391             if (!functionObject)
    392                 return jsUndefined();
    393             return functionObject;
    394         }
    395 
    396         if (!object->prototype() || !object->prototype().isObject())
    397             return jsUndefined();
    398         object = asObject(object->prototype());
    399     }
    400 }
    401 
    402 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
    403 {
    404     if (!value.isObject())
    405         return false;
    406 
    407     if (!proto.isObject()) {
    408         throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
    409         return false;
    410     }
    411 
    412     JSObject* object = asObject(value);
    413     while ((object = object->prototype().getObject())) {
    414         if (proto == object)
    415             return true;
    416     }
    417     return false;
    418 }
    419 
    420 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
    421 {
    422     PropertyDescriptor descriptor;
    423     if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
    424         return false;
    425     return descriptor.enumerable();
    426 }
    427 
    428 bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const
    429 {
    430     unsigned attributes;
    431     if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
    432         return true;
    433 
    434     // This could be a function within the static table? - should probably
    435     // also look in the hash?  This currently should not be a problem, since
    436     // we've currently always call 'get' first, which should have populated
    437     // the normal storage.
    438     return false;
    439 }
    440 
    441 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    442 {
    443     getOwnPropertyNames(exec, propertyNames, mode);
    444 
    445     if (prototype().isNull())
    446         return;
    447 
    448     JSObject* prototype = asObject(this->prototype());
    449     while(1) {
    450         if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
    451             prototype->getPropertyNames(exec, propertyNames, mode);
    452             break;
    453         }
    454         prototype->getOwnPropertyNames(exec, propertyNames, mode);
    455         JSValue nextProto = prototype->prototype();
    456         if (nextProto.isNull())
    457             break;
    458         prototype = asObject(nextProto);
    459     }
    460 }
    461 
    462 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
    463 {
    464     m_structure->getPropertyNames(exec->globalData(), propertyNames, mode);
    465     getClassPropertyNames(exec, classInfo(), propertyNames, mode);
    466 }
    467 
    468 bool JSObject::toBoolean(ExecState*) const
    469 {
    470     return true;
    471 }
    472 
    473 double JSObject::toNumber(ExecState* exec) const
    474 {
    475     JSValue primitive = toPrimitive(exec, PreferNumber);
    476     if (exec->hadException()) // should be picked up soon in Nodes.cpp
    477         return 0.0;
    478     return primitive.toNumber(exec);
    479 }
    480 
    481 UString JSObject::toString(ExecState* exec) const
    482 {
    483     JSValue primitive = toPrimitive(exec, PreferString);
    484     if (exec->hadException())
    485         return "";
    486     return primitive.toString(exec);
    487 }
    488 
    489 JSObject* JSObject::toObject(ExecState*, JSGlobalObject*) const
    490 {
    491     return const_cast<JSObject*>(this);
    492 }
    493 
    494 JSObject* JSObject::toThisObject(ExecState*) const
    495 {
    496     return const_cast<JSObject*>(this);
    497 }
    498 
    499 JSValue JSObject::toStrictThisObject(ExecState*) const
    500 {
    501     return const_cast<JSObject*>(this);
    502 }
    503 
    504 JSObject* JSObject::unwrappedObject()
    505 {
    506     return this;
    507 }
    508 
    509 void JSObject::seal(JSGlobalData& globalData)
    510 {
    511     setStructure(globalData, Structure::sealTransition(globalData, m_structure.get()));
    512 }
    513 
    514 void JSObject::freeze(JSGlobalData& globalData)
    515 {
    516     setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get()));
    517 }
    518 
    519 void JSObject::preventExtensions(JSGlobalData& globalData)
    520 {
    521     if (isExtensible())
    522         setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get()));
    523 }
    524 
    525 void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
    526 {
    527     size_t offset;
    528     if (m_structure->isUncacheableDictionary()) {
    529         offset = m_structure->removePropertyWithoutTransition(globalData, propertyName);
    530         if (offset != WTF::notFound)
    531             putUndefinedAtDirectOffset(offset);
    532         return;
    533     }
    534 
    535     setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset));
    536     if (offset != WTF::notFound)
    537         putUndefinedAtDirectOffset(offset);
    538 }
    539 
    540 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
    541 {
    542     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
    543 }
    544 
    545 void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
    546 {
    547     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
    548 }
    549 
    550 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
    551 {
    552     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
    553 }
    554 
    555 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
    556 {
    557     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
    558 }
    559 
    560 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
    561 {
    562     if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
    563         if (!structure()->isDictionary())
    564             slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
    565         else
    566             slot.setGetterSlot(getterFunction);
    567     } else
    568         slot.setUndefined();
    569 }
    570 
    571 Structure* JSObject::createInheritorID(JSGlobalData& globalData)
    572 {
    573     m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this));
    574     ASSERT(m_inheritorID->isEmpty());
    575     return m_inheritorID.get();
    576 }
    577 
    578 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
    579 {
    580     ASSERT(newSize > oldSize);
    581 
    582     // It's important that this function not rely on m_structure, since
    583     // we might be in the middle of a transition.
    584     bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity);
    585 
    586     PropertyStorage oldPropertyStorage = m_propertyStorage;
    587     PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
    588 
    589     for (unsigned i = 0; i < oldSize; ++i)
    590        newPropertyStorage[i] = oldPropertyStorage[i];
    591 
    592     if (!wasInline)
    593         delete [] oldPropertyStorage;
    594 
    595     m_propertyStorage = newPropertyStorage;
    596 }
    597 
    598 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    599 {
    600     unsigned attributes = 0;
    601     JSCell* cell = 0;
    602     size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell);
    603     if (offset == WTF::notFound)
    604         return false;
    605     descriptor.setDescriptor(getDirectOffset(offset), attributes);
    606     return true;
    607 }
    608 
    609 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
    610 {
    611     JSObject* object = this;
    612     while (true) {
    613         if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
    614             return true;
    615         JSValue prototype = object->prototype();
    616         if (!prototype.isObject())
    617             return false;
    618         object = asObject(prototype);
    619     }
    620 }
    621 
    622 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
    623 {
    624     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
    625         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
    626             GetterSetter* accessor = new (exec) GetterSetter(exec);
    627             if (oldDescriptor.getter()) {
    628                 attributes |= Getter;
    629                 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
    630             }
    631             if (oldDescriptor.setter()) {
    632                 attributes |= Setter;
    633                 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
    634             }
    635             target->putWithAttributes(exec, propertyName, accessor, attributes);
    636             return true;
    637         }
    638         JSValue newValue = jsUndefined();
    639         if (descriptor.value())
    640             newValue = descriptor.value();
    641         else if (oldDescriptor.value())
    642             newValue = oldDescriptor.value();
    643         target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
    644         return true;
    645     }
    646     attributes &= ~ReadOnly;
    647     if (descriptor.getter() && descriptor.getter().isObject())
    648         target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
    649     if (exec->hadException())
    650         return false;
    651     if (descriptor.setter() && descriptor.setter().isObject())
    652         target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
    653     return !exec->hadException();
    654 }
    655 
    656 bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
    657 {
    658     // If we have a new property we can just put it on normally
    659     PropertyDescriptor current;
    660     if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
    661         // unless extensions are prevented!
    662         if (!isExtensible()) {
    663             if (throwException)
    664                 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
    665             return false;
    666         }
    667         PropertyDescriptor oldDescriptor;
    668         oldDescriptor.setValue(jsUndefined());
    669         return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
    670     }
    671 
    672     if (descriptor.isEmpty())
    673         return true;
    674 
    675     if (current.equalTo(exec, descriptor))
    676         return true;
    677 
    678     // Filter out invalid changes
    679     if (!current.configurable()) {
    680         if (descriptor.configurable()) {
    681             if (throwException)
    682                 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
    683             return false;
    684         }
    685         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
    686             if (throwException)
    687                 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
    688             return false;
    689         }
    690     }
    691 
    692     // A generic descriptor is simply changing the attributes of an existing property
    693     if (descriptor.isGenericDescriptor()) {
    694         if (!current.attributesEqual(descriptor)) {
    695             deleteProperty(exec, propertyName);
    696             putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
    697         }
    698         return true;
    699     }
    700 
    701     // Changing between a normal property or an accessor property
    702     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
    703         if (!current.configurable()) {
    704             if (throwException)
    705                 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
    706             return false;
    707         }
    708         deleteProperty(exec, propertyName);
    709         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
    710     }
    711 
    712     // Changing the value and attributes of an existing property
    713     if (descriptor.isDataDescriptor()) {
    714         if (!current.configurable()) {
    715             if (!current.writable() && descriptor.writable()) {
    716                 if (throwException)
    717                     throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
    718                 return false;
    719             }
    720             if (!current.writable()) {
    721                 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
    722                     if (throwException)
    723                         throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
    724                     return false;
    725                 }
    726             }
    727         } else if (current.attributesEqual(descriptor)) {
    728             if (!descriptor.value())
    729                 return true;
    730             PutPropertySlot slot;
    731             put(exec, propertyName, descriptor.value(), slot);
    732             if (exec->hadException())
    733                 return false;
    734             return true;
    735         }
    736         deleteProperty(exec, propertyName);
    737         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
    738     }
    739 
    740     // Changing the accessor functions of an existing accessor property
    741     ASSERT(descriptor.isAccessorDescriptor());
    742     if (!current.configurable()) {
    743         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
    744             if (throwException)
    745                 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
    746             return false;
    747         }
    748         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
    749             if (throwException)
    750                 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
    751             return false;
    752         }
    753     }
    754     JSValue accessor = getDirect(exec->globalData(), propertyName);
    755     if (!accessor)
    756         return false;
    757     GetterSetter* getterSetter = asGetterSetter(accessor);
    758     if (current.attributesEqual(descriptor)) {
    759         if (descriptor.setter())
    760             getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
    761         if (descriptor.getter())
    762             getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
    763         return true;
    764     }
    765     deleteProperty(exec, propertyName);
    766     unsigned attrs = current.attributesWithOverride(descriptor);
    767     if (descriptor.setter())
    768         attrs |= Setter;
    769     if (descriptor.getter())
    770         attrs |= Getter;
    771     putDirect(exec->globalData(), propertyName, getterSetter, attrs);
    772     return true;
    773 }
    774 
    775 JSObject* throwTypeError(ExecState* exec, const UString& message)
    776 {
    777     return throwError(exec, createTypeError(exec, message));
    778 }
    779 
    780 } // namespace JSC
    781