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