JSObject.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
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 42namespace JSC { 43 44ASSERT_CLASS_FITS_IN_CELL(JSObject); 45ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); 46ASSERT_CLASS_FILLS_CELL(JSFinalObject); 47 48const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; 49 50const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 }; 51 52static 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 71void 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 85UString JSObject::className() const 86{ 87 const ClassInfo* info = classInfo(); 88 ASSERT(info); 89 return info->className; 90} 91 92bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 93{ 94 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); 95} 96 97static 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 103void 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(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(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(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 168void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value) 169{ 170 PutPropertySlot slot; 171 put(exec, Identifier::from(exec, propertyName), value, slot); 172} 173 174void 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 179void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) 180{ 181 putDirectInternal(*globalData, propertyName, value, attributes); 182} 183 184void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) 185{ 186 putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); 187} 188 189void 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 194void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) 195{ 196 putDirectInternal(exec->globalData(), propertyName, value, attributes); 197} 198 199void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) 200{ 201 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); 202} 203 204bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const 205{ 206 PropertySlot slot; 207 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); 208} 209 210bool 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 217bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) 218{ 219 unsigned attributes; 220 JSCell* specificValue; 221 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) { 222 if ((attributes & DontDelete)) 223 return false; 224 removeDirect(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 237bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const 238{ 239 PropertySlot slot; 240 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot); 241} 242 243bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName) 244{ 245 return deleteProperty(exec, Identifier::from(exec, propertyName)); 246} 247 248static 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 270bool 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 278JSValue 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 302const 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 313void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) 314{ 315 JSValue object = getDirect(propertyName); 316 if (object && object.isGetterSetter()) { 317 ASSERT(m_structure->hasGetterSetterProperties()); 318 asGetterSetter(object)->setGetter(exec->globalData(), getterFunction); 319 return; 320 } 321 322 PutPropertySlot slot; 323 GetterSetter* getterSetter = new (exec) GetterSetter(exec); 324 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot); 325 326 // putDirect will change our Structure if we add a new property. For 327 // getters and setters, though, we also need to change our Structure 328 // if we override an existing non-getter or non-setter. 329 if (slot.type() != PutPropertySlot::NewProperty) { 330 if (!m_structure->isDictionary()) { 331 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); 332 setStructure(structure.release()); 333 } 334 } 335 336 m_structure->setHasGetterSetterProperties(true); 337 getterSetter->setGetter(exec->globalData(), getterFunction); 338} 339 340void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) 341{ 342 JSValue object = getDirect(propertyName); 343 if (object && object.isGetterSetter()) { 344 ASSERT(m_structure->hasGetterSetterProperties()); 345 asGetterSetter(object)->setSetter(exec->globalData(), setterFunction); 346 return; 347 } 348 349 PutPropertySlot slot; 350 GetterSetter* getterSetter = new (exec) GetterSetter(exec); 351 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot); 352 353 // putDirect will change our Structure if we add a new property. For 354 // getters and setters, though, we also need to change our Structure 355 // if we override an existing non-getter or non-setter. 356 if (slot.type() != PutPropertySlot::NewProperty) { 357 if (!m_structure->isDictionary()) { 358 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure); 359 setStructure(structure.release()); 360 } 361 } 362 363 m_structure->setHasGetterSetterProperties(true); 364 getterSetter->setSetter(exec->globalData(), setterFunction); 365} 366 367JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName) 368{ 369 JSObject* object = this; 370 while (true) { 371 if (JSValue value = object->getDirect(propertyName)) { 372 if (!value.isGetterSetter()) 373 return jsUndefined(); 374 JSObject* functionObject = asGetterSetter(value)->getter(); 375 if (!functionObject) 376 return jsUndefined(); 377 return functionObject; 378 } 379 380 if (!object->prototype() || !object->prototype().isObject()) 381 return jsUndefined(); 382 object = asObject(object->prototype()); 383 } 384} 385 386JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName) 387{ 388 JSObject* object = this; 389 while (true) { 390 if (JSValue value = object->getDirect(propertyName)) { 391 if (!value.isGetterSetter()) 392 return jsUndefined(); 393 JSObject* functionObject = asGetterSetter(value)->setter(); 394 if (!functionObject) 395 return jsUndefined(); 396 return functionObject; 397 } 398 399 if (!object->prototype() || !object->prototype().isObject()) 400 return jsUndefined(); 401 object = asObject(object->prototype()); 402 } 403} 404 405bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) 406{ 407 if (!value.isObject()) 408 return false; 409 410 if (!proto.isObject()) { 411 throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property.")); 412 return false; 413 } 414 415 JSObject* object = asObject(value); 416 while ((object = object->prototype().getObject())) { 417 if (proto == object) 418 return true; 419 } 420 return false; 421} 422 423bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const 424{ 425 PropertyDescriptor descriptor; 426 if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) 427 return false; 428 return descriptor.enumerable(); 429} 430 431bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const 432{ 433 unsigned attributes; 434 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) 435 return true; 436 437 // This could be a function within the static table? - should probably 438 // also look in the hash? This currently should not be a problem, since 439 // we've currently always call 'get' first, which should have populated 440 // the normal storage. 441 return false; 442} 443 444void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 445{ 446 getOwnPropertyNames(exec, propertyNames, mode); 447 448 if (prototype().isNull()) 449 return; 450 451 JSObject* prototype = asObject(this->prototype()); 452 while(1) { 453 if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { 454 prototype->getPropertyNames(exec, propertyNames, mode); 455 break; 456 } 457 prototype->getOwnPropertyNames(exec, propertyNames, mode); 458 JSValue nextProto = prototype->prototype(); 459 if (nextProto.isNull()) 460 break; 461 prototype = asObject(nextProto); 462 } 463} 464 465void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) 466{ 467 m_structure->getPropertyNames(propertyNames, mode); 468 getClassPropertyNames(exec, classInfo(), propertyNames, mode); 469} 470 471bool JSObject::toBoolean(ExecState*) const 472{ 473 return true; 474} 475 476double JSObject::toNumber(ExecState* exec) const 477{ 478 JSValue primitive = toPrimitive(exec, PreferNumber); 479 if (exec->hadException()) // should be picked up soon in Nodes.cpp 480 return 0.0; 481 return primitive.toNumber(exec); 482} 483 484UString JSObject::toString(ExecState* exec) const 485{ 486 JSValue primitive = toPrimitive(exec, PreferString); 487 if (exec->hadException()) 488 return ""; 489 return primitive.toString(exec); 490} 491 492JSObject* JSObject::toObject(ExecState*) const 493{ 494 return const_cast<JSObject*>(this); 495} 496 497JSObject* JSObject::toThisObject(ExecState*) const 498{ 499 return const_cast<JSObject*>(this); 500} 501 502JSValue JSObject::toStrictThisObject(ExecState*) const 503{ 504 return const_cast<JSObject*>(this); 505} 506 507JSObject* JSObject::unwrappedObject() 508{ 509 return this; 510} 511 512void JSObject::seal() 513{ 514 setStructure(Structure::sealTransition(m_structure)); 515} 516 517void JSObject::freeze() 518{ 519 setStructure(Structure::freezeTransition(m_structure)); 520} 521 522void JSObject::preventExtensions() 523{ 524 if (isExtensible()) 525 setStructure(Structure::preventExtensionsTransition(m_structure)); 526} 527 528void JSObject::removeDirect(const Identifier& propertyName) 529{ 530 size_t offset; 531 if (m_structure->isUncacheableDictionary()) { 532 offset = m_structure->removePropertyWithoutTransition(propertyName); 533 if (offset != WTF::notFound) 534 putUndefinedAtDirectOffset(offset); 535 return; 536 } 537 538 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset); 539 setStructure(structure.release()); 540 if (offset != WTF::notFound) 541 putUndefinedAtDirectOffset(offset); 542} 543 544void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) 545{ 546 putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 547} 548 549void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) 550{ 551 putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 552} 553 554void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) 555{ 556 putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 557} 558 559void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) 560{ 561 putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 562} 563 564NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location) 565{ 566 if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) { 567 if (!structure()->isDictionary()) 568 slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); 569 else 570 slot.setGetterSlot(getterFunction); 571 } else 572 slot.setUndefined(); 573} 574 575Structure* JSObject::createInheritorID() 576{ 577 m_inheritorID = createEmptyObjectStructure(this); 578 return m_inheritorID.get(); 579} 580 581void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) 582{ 583 ASSERT(newSize > oldSize); 584 585 // It's important that this function not rely on m_structure, since 586 // we might be in the middle of a transition. 587 bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity); 588 589 PropertyStorage oldPropertyStorage = m_propertyStorage; 590 PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize]; 591 592 for (unsigned i = 0; i < oldSize; ++i) 593 newPropertyStorage[i] = oldPropertyStorage[i]; 594 595 if (!wasInline) 596 delete [] oldPropertyStorage; 597 598 m_propertyStorage = newPropertyStorage; 599} 600 601bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor) 602{ 603 unsigned attributes = 0; 604 JSCell* cell = 0; 605 size_t offset = m_structure->get(propertyName, attributes, cell); 606 if (offset == WTF::notFound) 607 return false; 608 descriptor.setDescriptor(getDirectOffset(offset), attributes); 609 return true; 610} 611 612bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 613{ 614 JSObject* object = this; 615 while (true) { 616 if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) 617 return true; 618 JSValue prototype = object->prototype(); 619 if (!prototype.isObject()) 620 return false; 621 object = asObject(prototype); 622 } 623} 624 625static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) 626{ 627 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { 628 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { 629 GetterSetter* accessor = new (exec) GetterSetter(exec); 630 if (oldDescriptor.getter()) { 631 attributes |= Getter; 632 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter())); 633 } 634 if (oldDescriptor.setter()) { 635 attributes |= Setter; 636 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter())); 637 } 638 target->putWithAttributes(exec, propertyName, accessor, attributes); 639 return true; 640 } 641 JSValue newValue = jsUndefined(); 642 if (descriptor.value()) 643 newValue = descriptor.value(); 644 else if (oldDescriptor.value()) 645 newValue = oldDescriptor.value(); 646 target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter)); 647 return true; 648 } 649 attributes &= ~ReadOnly; 650 if (descriptor.getter() && descriptor.getter().isObject()) 651 target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes); 652 if (exec->hadException()) 653 return false; 654 if (descriptor.setter() && descriptor.setter().isObject()) 655 target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes); 656 return !exec->hadException(); 657} 658 659bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) 660{ 661 // If we have a new property we can just put it on normally 662 PropertyDescriptor current; 663 if (!getOwnPropertyDescriptor(exec, propertyName, current)) { 664 // unless extensions are prevented! 665 if (!isExtensible()) { 666 if (throwException) 667 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); 668 return false; 669 } 670 PropertyDescriptor oldDescriptor; 671 oldDescriptor.setValue(jsUndefined()); 672 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); 673 } 674 675 if (descriptor.isEmpty()) 676 return true; 677 678 if (current.equalTo(exec, descriptor)) 679 return true; 680 681 // Filter out invalid changes 682 if (!current.configurable()) { 683 if (descriptor.configurable()) { 684 if (throwException) 685 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); 686 return false; 687 } 688 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { 689 if (throwException) 690 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); 691 return false; 692 } 693 } 694 695 // A generic descriptor is simply changing the attributes of an existing property 696 if (descriptor.isGenericDescriptor()) { 697 if (!current.attributesEqual(descriptor)) { 698 deleteProperty(exec, propertyName); 699 putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 700 } 701 return true; 702 } 703 704 // Changing between a normal property or an accessor property 705 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { 706 if (!current.configurable()) { 707 if (throwException) 708 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); 709 return false; 710 } 711 deleteProperty(exec, propertyName); 712 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 713 } 714 715 // Changing the value and attributes of an existing property 716 if (descriptor.isDataDescriptor()) { 717 if (!current.configurable()) { 718 if (!current.writable() && descriptor.writable()) { 719 if (throwException) 720 throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); 721 return false; 722 } 723 if (!current.writable()) { 724 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { 725 if (throwException) 726 throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); 727 return false; 728 } 729 } 730 } else if (current.attributesEqual(descriptor)) { 731 if (!descriptor.value()) 732 return true; 733 PutPropertySlot slot; 734 put(exec, propertyName, descriptor.value(), slot); 735 if (exec->hadException()) 736 return false; 737 return true; 738 } 739 deleteProperty(exec, propertyName); 740 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 741 } 742 743 // Changing the accessor functions of an existing accessor property 744 ASSERT(descriptor.isAccessorDescriptor()); 745 if (!current.configurable()) { 746 if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { 747 if (throwException) 748 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property.")); 749 return false; 750 } 751 if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { 752 if (throwException) 753 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property.")); 754 return false; 755 } 756 } 757 JSValue accessor = getDirect(propertyName); 758 if (!accessor) 759 return false; 760 GetterSetter* getterSetter = asGetterSetter(accessor); 761 if (current.attributesEqual(descriptor)) { 762 if (descriptor.setter()) 763 getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter())); 764 if (descriptor.getter()) 765 getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter())); 766 return true; 767 } 768 deleteProperty(exec, propertyName); 769 unsigned attrs = current.attributesWithOverride(descriptor); 770 if (descriptor.setter()) 771 attrs |= Setter; 772 if (descriptor.getter()) 773 attrs |= Getter; 774 putDirect(exec->globalData(), propertyName, getterSetter, attrs); 775 return true; 776} 777 778JSObject* throwTypeError(ExecState* exec, const UString& message) 779{ 780 return throwError(exec, createTypeError(exec, message)); 781} 782 783} // namespace JSC 784