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