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_FITS_IN_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(exec->globalData(), value)) 113 throwError(exec, createError(exec, "cyclic __proto__ value")); 114 return; 115 } 116 117 // Check if there are any setters or getters in the prototype chain 118 JSValue prototype; 119 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { 120 prototype = obj->prototype(); 121 if (prototype.isNull()) { 122 if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) 123 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 124 return; 125 } 126 } 127 128 unsigned attributes; 129 JSCell* specificValue; 130 if ((m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) { 131 if (slot.isStrictMode()) 132 throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); 133 return; 134 } 135 136 for (JSObject* obj = this; ; obj = asObject(prototype)) { 137 if (JSValue gs = obj->getDirect(exec->globalData(), propertyName)) { 138 if (gs.isGetterSetter()) { 139 JSObject* setterFunc = asGetterSetter(gs)->setter(); 140 if (!setterFunc) { 141 throwSetterError(exec); 142 return; 143 } 144 145 CallData callData; 146 CallType callType = setterFunc->getCallData(callData); 147 MarkedArgumentBuffer args; 148 args.append(value); 149 call(exec, setterFunc, callType, callData, this, args); 150 return; 151 } 152 153 // If there's an existing property on the object or one of its 154 // prototypes it should be replaced, so break here. 155 break; 156 } 157 158 prototype = obj->prototype(); 159 if (prototype.isNull()) 160 break; 161 } 162 163 if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) 164 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 165 return; 166} 167 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(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) { 222 if ((attributes & DontDelete)) 223 return false; 224 removeDirect(exec->globalData(), propertyName); 225 return true; 226 } 227 228 // Look in the static hashtable of properties 229 const HashEntry* entry = findPropertyHashEntry(exec, propertyName); 230 if (entry && entry->attributes() & DontDelete) 231 return false; // this builtin property can't be deleted 232 233 // FIXME: Should the code here actually do some deletion? 234 return true; 235} 236 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(exec->globalData(), propertyName); 316 if (object && object.isGetterSetter()) { 317 ASSERT(m_structure->hasGetterSetterProperties()); 318 asGetterSetter(object)->setGetter(exec->globalData(), getterFunction); 319 return; 320 } 321 322 JSGlobalData& globalData = exec->globalData(); 323 PutPropertySlot slot; 324 GetterSetter* getterSetter = new (exec) GetterSetter(exec); 325 putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot); 326 327 // putDirect will change our Structure if we add a new property. For 328 // getters and setters, though, we also need to change our Structure 329 // if we override an existing non-getter or non-setter. 330 if (slot.type() != PutPropertySlot::NewProperty) { 331 if (!m_structure->isDictionary()) 332 setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get())); 333 } 334 335 m_structure->setHasGetterSetterProperties(true); 336 getterSetter->setGetter(globalData, getterFunction); 337} 338 339void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) 340{ 341 JSValue object = getDirect(exec->globalData(), propertyName); 342 if (object && object.isGetterSetter()) { 343 ASSERT(m_structure->hasGetterSetterProperties()); 344 asGetterSetter(object)->setSetter(exec->globalData(), setterFunction); 345 return; 346 } 347 348 PutPropertySlot slot; 349 GetterSetter* getterSetter = new (exec) GetterSetter(exec); 350 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot); 351 352 // putDirect will change our Structure if we add a new property. For 353 // getters and setters, though, we also need to change our Structure 354 // if we override an existing non-getter or non-setter. 355 if (slot.type() != PutPropertySlot::NewProperty) { 356 if (!m_structure->isDictionary()) 357 setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get())); 358 } 359 360 m_structure->setHasGetterSetterProperties(true); 361 getterSetter->setSetter(exec->globalData(), setterFunction); 362} 363 364JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName) 365{ 366 JSObject* object = this; 367 while (true) { 368 if (JSValue value = object->getDirect(exec->globalData(), propertyName)) { 369 if (!value.isGetterSetter()) 370 return jsUndefined(); 371 JSObject* functionObject = asGetterSetter(value)->getter(); 372 if (!functionObject) 373 return jsUndefined(); 374 return functionObject; 375 } 376 377 if (!object->prototype() || !object->prototype().isObject()) 378 return jsUndefined(); 379 object = asObject(object->prototype()); 380 } 381} 382 383JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName) 384{ 385 JSObject* object = this; 386 while (true) { 387 if (JSValue value = object->getDirect(exec->globalData(), propertyName)) { 388 if (!value.isGetterSetter()) 389 return jsUndefined(); 390 JSObject* functionObject = asGetterSetter(value)->setter(); 391 if (!functionObject) 392 return jsUndefined(); 393 return functionObject; 394 } 395 396 if (!object->prototype() || !object->prototype().isObject()) 397 return jsUndefined(); 398 object = asObject(object->prototype()); 399 } 400} 401 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* exec, const Identifier& propertyName, JSCell*& specificValue) const 429{ 430 unsigned attributes; 431 if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) 432 return true; 433 434 // This could be a function within the static table? - should probably 435 // also look in the hash? This currently should not be a problem, since 436 // we've currently always call 'get' first, which should have populated 437 // the normal storage. 438 return false; 439} 440 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(exec->globalData(), 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*, JSGlobalObject*) 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::seal(JSGlobalData& globalData) 510{ 511 setStructure(globalData, Structure::sealTransition(globalData, m_structure.get())); 512} 513 514void JSObject::freeze(JSGlobalData& globalData) 515{ 516 setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get())); 517} 518 519void JSObject::preventExtensions(JSGlobalData& globalData) 520{ 521 if (isExtensible()) 522 setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get())); 523} 524 525void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName) 526{ 527 size_t offset; 528 if (m_structure->isUncacheableDictionary()) { 529 offset = m_structure->removePropertyWithoutTransition(globalData, propertyName); 530 if (offset != WTF::notFound) 531 putUndefinedAtDirectOffset(offset); 532 return; 533 } 534 535 setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset)); 536 if (offset != WTF::notFound) 537 putUndefinedAtDirectOffset(offset); 538} 539 540void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) 541{ 542 putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 543} 544 545void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) 546{ 547 putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 548} 549 550void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) 551{ 552 putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 553} 554 555void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) 556{ 557 putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); 558} 559 560NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location) 561{ 562 if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) { 563 if (!structure()->isDictionary()) 564 slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location)); 565 else 566 slot.setGetterSlot(getterFunction); 567 } else 568 slot.setUndefined(); 569} 570 571Structure* JSObject::createInheritorID(JSGlobalData& globalData) 572{ 573 m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this)); 574 ASSERT(m_inheritorID->isEmpty()); 575 return m_inheritorID.get(); 576} 577 578void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) 579{ 580 ASSERT(newSize > oldSize); 581 582 // It's important that this function not rely on m_structure, since 583 // we might be in the middle of a transition. 584 bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity); 585 586 PropertyStorage oldPropertyStorage = m_propertyStorage; 587 PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize]; 588 589 for (unsigned i = 0; i < oldSize; ++i) 590 newPropertyStorage[i] = oldPropertyStorage[i]; 591 592 if (!wasInline) 593 delete [] oldPropertyStorage; 594 595 m_propertyStorage = newPropertyStorage; 596} 597 598bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 599{ 600 unsigned attributes = 0; 601 JSCell* cell = 0; 602 size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell); 603 if (offset == WTF::notFound) 604 return false; 605 descriptor.setDescriptor(getDirectOffset(offset), attributes); 606 return true; 607} 608 609bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 610{ 611 JSObject* object = this; 612 while (true) { 613 if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) 614 return true; 615 JSValue prototype = object->prototype(); 616 if (!prototype.isObject()) 617 return false; 618 object = asObject(prototype); 619 } 620} 621 622static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) 623{ 624 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { 625 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { 626 GetterSetter* accessor = new (exec) GetterSetter(exec); 627 if (oldDescriptor.getter()) { 628 attributes |= Getter; 629 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter())); 630 } 631 if (oldDescriptor.setter()) { 632 attributes |= Setter; 633 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter())); 634 } 635 target->putWithAttributes(exec, propertyName, accessor, attributes); 636 return true; 637 } 638 JSValue newValue = jsUndefined(); 639 if (descriptor.value()) 640 newValue = descriptor.value(); 641 else if (oldDescriptor.value()) 642 newValue = oldDescriptor.value(); 643 target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter)); 644 return true; 645 } 646 attributes &= ~ReadOnly; 647 if (descriptor.getter() && descriptor.getter().isObject()) 648 target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes); 649 if (exec->hadException()) 650 return false; 651 if (descriptor.setter() && descriptor.setter().isObject()) 652 target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes); 653 return !exec->hadException(); 654} 655 656bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) 657{ 658 // If we have a new property we can just put it on normally 659 PropertyDescriptor current; 660 if (!getOwnPropertyDescriptor(exec, propertyName, current)) { 661 // unless extensions are prevented! 662 if (!isExtensible()) { 663 if (throwException) 664 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible.")); 665 return false; 666 } 667 PropertyDescriptor oldDescriptor; 668 oldDescriptor.setValue(jsUndefined()); 669 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); 670 } 671 672 if (descriptor.isEmpty()) 673 return true; 674 675 if (current.equalTo(exec, descriptor)) 676 return true; 677 678 // Filter out invalid changes 679 if (!current.configurable()) { 680 if (descriptor.configurable()) { 681 if (throwException) 682 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); 683 return false; 684 } 685 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { 686 if (throwException) 687 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); 688 return false; 689 } 690 } 691 692 // A generic descriptor is simply changing the attributes of an existing property 693 if (descriptor.isGenericDescriptor()) { 694 if (!current.attributesEqual(descriptor)) { 695 deleteProperty(exec, propertyName); 696 putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 697 } 698 return true; 699 } 700 701 // Changing between a normal property or an accessor property 702 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { 703 if (!current.configurable()) { 704 if (throwException) 705 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property.")); 706 return false; 707 } 708 deleteProperty(exec, propertyName); 709 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 710 } 711 712 // Changing the value and attributes of an existing property 713 if (descriptor.isDataDescriptor()) { 714 if (!current.configurable()) { 715 if (!current.writable() && descriptor.writable()) { 716 if (throwException) 717 throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property.")); 718 return false; 719 } 720 if (!current.writable()) { 721 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { 722 if (throwException) 723 throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property.")); 724 return false; 725 } 726 } 727 } else if (current.attributesEqual(descriptor)) { 728 if (!descriptor.value()) 729 return true; 730 PutPropertySlot slot; 731 put(exec, propertyName, descriptor.value(), slot); 732 if (exec->hadException()) 733 return false; 734 return true; 735 } 736 deleteProperty(exec, propertyName); 737 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current); 738 } 739 740 // Changing the accessor functions of an existing accessor property 741 ASSERT(descriptor.isAccessorDescriptor()); 742 if (!current.configurable()) { 743 if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { 744 if (throwException) 745 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property.")); 746 return false; 747 } 748 if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { 749 if (throwException) 750 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property.")); 751 return false; 752 } 753 } 754 JSValue accessor = getDirect(exec->globalData(), propertyName); 755 if (!accessor) 756 return false; 757 GetterSetter* getterSetter = asGetterSetter(accessor); 758 if (current.attributesEqual(descriptor)) { 759 if (descriptor.setter()) 760 getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter())); 761 if (descriptor.getter()) 762 getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter())); 763 return true; 764 } 765 deleteProperty(exec, propertyName); 766 unsigned attrs = current.attributesWithOverride(descriptor); 767 if (descriptor.setter()) 768 attrs |= Setter; 769 if (descriptor.getter()) 770 attrs |= Getter; 771 putDirect(exec->globalData(), propertyName, getterSetter, attrs); 772 return true; 773} 774 775JSObject* throwTypeError(ExecState* exec, const UString& message) 776{ 777 return throwError(exec, createTypeError(exec, message)); 778} 779 780} // namespace JSC 781