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