JSObject.h revision 2bde8e466a4451c7319e3a072d118917957d6554
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, 2007, 2008, 2009 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 * 21 */ 22 23#ifndef JSObject_h 24#define JSObject_h 25 26#include "ArgList.h" 27#include "ClassInfo.h" 28#include "CommonIdentifiers.h" 29#include "Completion.h" 30#include "CallFrame.h" 31#include "JSCell.h" 32#include "JSNumberCell.h" 33#include "MarkStack.h" 34#include "PropertySlot.h" 35#include "PutPropertySlot.h" 36#include "ScopeChain.h" 37#include "Structure.h" 38#include "JSGlobalData.h" 39#include "JSString.h" 40#include <wtf/StdLibExtras.h> 41 42namespace JSC { 43 44 inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value) 45 { 46 if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr)) 47 return value.asCell(); 48 return 0; 49 } 50 51 class HashEntry; 52 class InternalFunction; 53 class PropertyDescriptor; 54 class PropertyNameArray; 55 class Structure; 56 struct HashTable; 57 58 JSObject* throwTypeError(ExecState*, const UString&); 59 extern const char* StrictModeReadonlyPropertyWriteError; 60 61 // ECMA 262-3 8.6.1 62 // Property attributes 63 enum Attribute { 64 None = 0, 65 ReadOnly = 1 << 1, // property can be only read, not written 66 DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) 67 DontDelete = 1 << 3, // property can't be deleted 68 Function = 1 << 4, // property is a function - only used by static hashtables 69 Getter = 1 << 5, // property is a getter 70 Setter = 1 << 6 // property is a setter 71 }; 72 73 typedef WriteBarrierBase<Unknown>* PropertyStorage; 74 typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage; 75 76 class JSObject : public JSCell { 77 friend class BatchedTransitionOptimizer; 78 friend class JIT; 79 friend class JSCell; 80 friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); 81 82 public: 83 virtual void markChildren(MarkStack&); 84 ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack); 85 86 // The inline virtual destructor cannot be the first virtual function declared 87 // in the class as it results in the vtable being generated as a weak symbol 88 virtual ~JSObject(); 89 90 JSValue prototype() const; 91 void setPrototype(JSValue prototype); 92 bool setPrototypeWithCycleCheck(JSValue prototype); 93 94 void setStructure(NonNullPassRefPtr<Structure>); 95 Structure* inheritorID(JSGlobalData&); 96 97 virtual UString className() const; 98 99 JSValue get(ExecState*, const Identifier& propertyName) const; 100 JSValue get(ExecState*, unsigned propertyName) const; 101 102 bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 103 bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 104 bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); 105 106 virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 107 virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); 108 virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); 109 110 virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); 111 virtual void put(ExecState*, unsigned propertyName, JSValue value); 112 113 virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); 114 virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes); 115 virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes); 116 virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); 117 virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); 118 virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes); 119 120 bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const; 121 122 bool hasProperty(ExecState*, const Identifier& propertyName) const; 123 bool hasProperty(ExecState*, unsigned propertyName) const; 124 bool hasOwnProperty(ExecState*, const Identifier& propertyName) const; 125 126 virtual bool deleteProperty(ExecState*, const Identifier& propertyName); 127 virtual bool deleteProperty(ExecState*, unsigned propertyName); 128 129 virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; 130 131 virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty); 132 133 virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); 134 virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); 135 136 virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; 137 virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); 138 virtual bool toBoolean(ExecState*) const; 139 virtual double toNumber(ExecState*) const; 140 virtual UString toString(ExecState*) const; 141 virtual JSObject* toObject(ExecState*, JSGlobalObject*) const; 142 143 virtual JSObject* toThisObject(ExecState*) const; 144 virtual JSValue toStrictThisObject(ExecState*) const; 145 virtual JSObject* unwrappedObject(); 146 147 bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; 148 149 // This get function only looks at the property map. 150 JSValue getDirect(const Identifier& propertyName) const 151 { 152 size_t offset = m_structure->get(propertyName); 153 return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); 154 } 155 156 WriteBarrierBase<Unknown>* getDirectLocation(const Identifier& propertyName) 157 { 158 size_t offset = m_structure->get(propertyName); 159 return offset != WTF::notFound ? locationForOffset(offset) : 0; 160 } 161 162 WriteBarrierBase<Unknown>* getDirectLocation(const Identifier& propertyName, unsigned& attributes) 163 { 164 JSCell* specificFunction; 165 size_t offset = m_structure->get(propertyName, attributes, specificFunction); 166 return offset != WTF::notFound ? locationForOffset(offset) : 0; 167 } 168 169 size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const 170 { 171 return location - propertyStorage(); 172 } 173 174 void transitionTo(Structure*); 175 176 void removeDirect(const Identifier& propertyName); 177 bool hasCustomProperties() { return !m_structure->isEmpty(); } 178 bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } 179 180 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&); 181 void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); 182 bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&); 183 184 void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr = 0); 185 void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr, bool checkReadOnly, PutPropertySlot&); 186 void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); 187 void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0); 188 189 void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); 190 void putDirectFunctionWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSCell* value, unsigned attr = 0); 191 void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); 192 void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0); 193 194 // Fast access to known property offsets. 195 JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); } 196 void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); } 197 void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); } 198 199 void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location); 200 201 virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); 202 virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); 203 virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); 204 virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName); 205 virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); 206 207 virtual bool isGlobalObject() const { return false; } 208 virtual bool isVariableObject() const { return false; } 209 virtual bool isActivationObject() const { return false; } 210 virtual bool isStrictModeFunction() const { return false; } 211 virtual bool isErrorInstance() const { return false; } 212 213 void seal(); 214 void freeze(); 215 void preventExtensions(); 216 bool isSealed() { return m_structure->isSealed(); } 217 bool isFrozen() { return m_structure->isFrozen(); } 218 bool isExtensible() { return m_structure->isExtensible(); } 219 220 virtual ComplType exceptionType() const { return Throw; } 221 222 void allocatePropertyStorage(size_t oldSize, size_t newSize); 223 bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } 224 225 static const unsigned baseExternalStorageCapacity = 16; 226 227 void flattenDictionaryObject(JSGlobalData& globalData) 228 { 229 m_structure->flattenDictionaryStructure(globalData, this); 230 } 231 232 void putAnonymousValue(JSGlobalData& globalData, unsigned index, JSValue value) 233 { 234 ASSERT(index < m_structure->anonymousSlotCount()); 235 locationForOffset(index)->set(globalData, this, value); 236 } 237 void clearAnonymousValue(unsigned index) 238 { 239 ASSERT(index < m_structure->anonymousSlotCount()); 240 locationForOffset(index)->clear(); 241 } 242 JSValue getAnonymousValue(unsigned index) const 243 { 244 ASSERT(index < m_structure->anonymousSlotCount()); 245 return locationForOffset(index)->get(); 246 } 247 248 static size_t offsetOfInlineStorage(); 249 250 static JS_EXPORTDATA const ClassInfo s_info; 251 252 protected: 253 static PassRefPtr<Structure> createStructure(JSGlobalData& globalData, JSValue prototype) 254 { 255 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); 256 } 257 258 static const unsigned StructureFlags = 0; 259 260 void putThisToAnonymousValue(unsigned index) 261 { 262 locationForOffset(index)->setWithoutWriteBarrier(this); 263 } 264 265 // To instantiate objects you likely want JSFinalObject, below. 266 // To create derived types you likely want JSNonFinalObject, below. 267 JSObject(NonNullPassRefPtr<Structure>, PropertyStorage inlineStorage); 268 269 private: 270 // Nobody should ever ask any of these questions on something already known to be a JSObject. 271 using JSCell::isAPIValueWrapper; 272 using JSCell::isGetterSetter; 273 using JSCell::toObject; 274 void getObject(); 275 void getString(ExecState* exec); 276 void isObject(); 277 void isString(); 278 279 ConstPropertyStorage propertyStorage() const { return m_propertyStorage; } 280 PropertyStorage propertyStorage() { return m_propertyStorage; } 281 282 const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const 283 { 284 return &propertyStorage()[offset]; 285 } 286 287 WriteBarrierBase<Unknown>* locationForOffset(size_t offset) 288 { 289 return &propertyStorage()[offset]; 290 } 291 292 bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*); 293 bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&); 294 void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); 295 296 bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); 297 298 const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; 299 Structure* createInheritorID(JSGlobalData&); 300 301 PropertyStorage m_propertyStorage; 302 RefPtr<Structure> m_inheritorID; 303 }; 304 305 306#if USE(JSVALUE32_64) 307#define JSNonFinalObject_inlineStorageCapacity 4 308#define JSFinalObject_inlineStorageCapacity 6 309#else 310#define JSNonFinalObject_inlineStorageCapacity 2 311#define JSFinalObject_inlineStorageCapacity 4 312#endif 313 314COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final); 315 316 // JSNonFinalObject is a type of JSObject that has some internal storage, 317 // but also preserves some space in the collector cell for additional 318 // data members in derived types. 319 class JSNonFinalObject : public JSObject { 320 friend class JSObject; 321 322 public: 323 static PassRefPtr<Structure> createStructure(JSGlobalData& globalData, JSValue prototype) 324 { 325 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); 326 } 327 328 protected: 329 explicit JSNonFinalObject(NonNullPassRefPtr<Structure> structure) 330 : JSObject(structure, m_inlineStorage) 331 { 332 ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double))); 333 ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity); 334 } 335 336 private: 337 WriteBarrierBase<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity]; 338 }; 339 340 // JSFinalObject is a type of JSObject that contains sufficent internal 341 // storage to fully make use of the colloctor cell containing it. 342 class JSFinalObject : public JSObject { 343 friend class JSObject; 344 345 public: 346 static JSFinalObject* create(ExecState* exec, NonNullPassRefPtr<Structure> structure) 347 { 348 return new (exec) JSFinalObject(structure); 349 } 350 351 static PassRefPtr<Structure> createStructure(JSGlobalData& globalData, JSValue prototype) 352 { 353 return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); 354 } 355 356 private: 357 explicit JSFinalObject(NonNullPassRefPtr<Structure> structure) 358 : JSObject(structure, m_inlineStorage) 359 { 360 ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double) == 0); 361 ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity); 362 } 363 364 static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject; 365 366 WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity]; 367 }; 368 369inline size_t JSObject::offsetOfInlineStorage() 370{ 371 ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage)); 372 return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage); 373} 374 375inline JSObject* constructEmptyObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) 376{ 377 return JSFinalObject::create(exec, structure); 378} 379 380inline PassRefPtr<Structure> createEmptyObjectStructure(JSGlobalData& globalData, JSValue prototype) 381{ 382 return JSFinalObject::createStructure(globalData, prototype); 383} 384 385inline JSObject* asObject(JSCell* cell) 386{ 387 ASSERT(cell->isObject()); 388 return static_cast<JSObject*>(cell); 389} 390 391inline JSObject* asObject(JSValue value) 392{ 393 return asObject(value.asCell()); 394} 395 396inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure, PropertyStorage inlineStorage) 397 : JSCell(structure.releaseRef()) // ~JSObject balances this ref() 398 , m_propertyStorage(inlineStorage) 399{ 400 ASSERT(inherits(&s_info)); 401 ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity); 402 ASSERT(m_structure->isEmpty()); 403 ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); 404} 405 406inline JSObject::~JSObject() 407{ 408 ASSERT(m_structure); 409 if (!isUsingInlineStorage()) 410 delete [] m_propertyStorage; 411 m_structure->deref(); 412} 413 414inline JSValue JSObject::prototype() const 415{ 416 return m_structure->storedPrototype(); 417} 418 419inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype) 420{ 421 JSValue nextPrototypeValue = prototype; 422 while (nextPrototypeValue && nextPrototypeValue.isObject()) { 423 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); 424 if (nextPrototype == this) 425 return false; 426 nextPrototypeValue = nextPrototype->prototype(); 427 } 428 setPrototype(prototype); 429 return true; 430} 431 432inline void JSObject::setPrototype(JSValue prototype) 433{ 434 ASSERT(prototype); 435 RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype); 436 setStructure(newStructure.release()); 437} 438 439inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure) 440{ 441 m_structure->deref(); 442 m_structure = structure.leakRef(); // ~JSObject balances this ref() 443} 444 445inline Structure* JSObject::inheritorID(JSGlobalData& globalData) 446{ 447 if (m_inheritorID) 448 return m_inheritorID.get(); 449 return createInheritorID(globalData); 450} 451 452inline bool Structure::isUsingInlineStorage() const 453{ 454 return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity; 455} 456 457inline bool JSCell::inherits(const ClassInfo* info) const 458{ 459 for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { 460 if (ci == info) 461 return true; 462 } 463 return false; 464} 465 466// this method is here to be after the inline declaration of JSCell::inherits 467inline bool JSValue::inherits(const ClassInfo* classInfo) const 468{ 469 return isCell() && asCell()->inherits(classInfo); 470} 471 472ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 473{ 474 if (WriteBarrierBase<Unknown>* location = getDirectLocation(propertyName)) { 475 if (m_structure->hasGetterSetterProperties() && location->isGetterSetter()) 476 fillGetterPropertySlot(slot, location); 477 else 478 slot.setValue(this, location->get(), offsetForLocation(location)); 479 return true; 480 } 481 482 // non-standard Netscape extension 483 if (propertyName == exec->propertyNames().underscoreProto) { 484 slot.setValue(prototype()); 485 return true; 486 } 487 488 return false; 489} 490 491// It may seem crazy to inline a function this large, especially a virtual function, 492// but it makes a big difference to property lookup that derived classes can inline their 493// base class call to this. 494ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 495{ 496 return inlineGetOwnPropertySlot(exec, propertyName, slot); 497} 498 499ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 500{ 501 if (!structure()->typeInfo().overridesGetOwnPropertySlot()) 502 return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); 503 return getOwnPropertySlot(exec, propertyName, slot); 504} 505 506// It may seem crazy to inline a function this large but it makes a big difference 507// since this is function very hot in variable lookup 508ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 509{ 510 JSObject* object = this; 511 while (true) { 512 if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) 513 return true; 514 JSValue prototype = object->prototype(); 515 if (!prototype.isObject()) 516 return false; 517 object = asObject(prototype); 518 } 519} 520 521ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) 522{ 523 JSObject* object = this; 524 while (true) { 525 if (object->getOwnPropertySlot(exec, propertyName, slot)) 526 return true; 527 JSValue prototype = object->prototype(); 528 if (!prototype.isObject()) 529 return false; 530 object = asObject(prototype); 531 } 532} 533 534inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const 535{ 536 PropertySlot slot(this); 537 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) 538 return slot.getValue(exec, propertyName); 539 540 return jsUndefined(); 541} 542 543inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const 544{ 545 PropertySlot slot(this); 546 if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) 547 return slot.getValue(exec, propertyName); 548 549 return jsUndefined(); 550} 551 552inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) 553{ 554 ASSERT(value); 555 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 556 557 if (m_structure->isDictionary()) { 558 unsigned currentAttributes; 559 JSCell* currentSpecificFunction; 560 size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); 561 if (offset != WTF::notFound) { 562 // If there is currently a specific function, and there now either isn't, 563 // or the new value is different, then despecify. 564 if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) 565 m_structure->despecifyDictionaryFunction(propertyName); 566 if (checkReadOnly && currentAttributes & ReadOnly) 567 return false; 568 569 putDirectOffset(globalData, offset, value); 570 // At this point, the objects structure only has a specific value set if previously there 571 // had been one set, and if the new value being specified is the same (otherwise we would 572 // have despecified, above). So, if currentSpecificFunction is not set, or if the new 573 // value is different (or there is no new value), then the slot now has no value - and 574 // as such it is cachable. 575 // If there was previously a value, and the new value is the same, then we cannot cache. 576 if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) 577 slot.setExistingProperty(this, offset); 578 return true; 579 } 580 581 if (!isExtensible()) 582 return false; 583 584 size_t currentCapacity = m_structure->propertyStorageCapacity(); 585 offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); 586 if (currentCapacity != m_structure->propertyStorageCapacity()) 587 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); 588 589 ASSERT(offset < m_structure->propertyStorageCapacity()); 590 putDirectOffset(globalData, offset, value); 591 // See comment on setNewProperty call below. 592 if (!specificFunction) 593 slot.setNewProperty(this, offset); 594 return true; 595 } 596 597 size_t offset; 598 size_t currentCapacity = m_structure->propertyStorageCapacity(); 599 if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) { 600 if (currentCapacity != structure->propertyStorageCapacity()) 601 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); 602 603 ASSERT(offset < structure->propertyStorageCapacity()); 604 setStructure(structure.release()); 605 putDirectOffset(globalData, offset, value); 606 // This is a new property; transitions with specific values are not currently cachable, 607 // so leave the slot in an uncachable state. 608 if (!specificFunction) 609 slot.setNewProperty(this, offset); 610 return true; 611 } 612 613 unsigned currentAttributes; 614 JSCell* currentSpecificFunction; 615 offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); 616 if (offset != WTF::notFound) { 617 if (checkReadOnly && currentAttributes & ReadOnly) 618 return false; 619 620 // There are three possibilities here: 621 // (1) There is an existing specific value set, and we're overwriting with *the same value*. 622 // * Do nothing - no need to despecify, but that means we can't cache (a cached 623 // put could write a different value). Leave the slot in an uncachable state. 624 // (2) There is a specific value currently set, but we're writing a different value. 625 // * First, we have to despecify. Having done so, this is now a regular slot 626 // with no specific value, so go ahead & cache like normal. 627 // (3) Normal case, there is no specific value set. 628 // * Go ahead & cache like normal. 629 if (currentSpecificFunction) { 630 // case (1) Do the put, then return leaving the slot uncachable. 631 if (specificFunction == currentSpecificFunction) { 632 putDirectOffset(globalData, offset, value); 633 return true; 634 } 635 // case (2) Despecify, fall through to (3). 636 setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); 637 } 638 639 // case (3) set the slot, do the put, return. 640 slot.setExistingProperty(this, offset); 641 putDirectOffset(globalData, offset, value); 642 return true; 643 } 644 645 if (!isExtensible()) 646 return false; 647 648 RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); 649 650 if (currentCapacity != structure->propertyStorageCapacity()) 651 allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); 652 653 ASSERT(offset < structure->propertyStorageCapacity()); 654 setStructure(structure.release()); 655 putDirectOffset(globalData, offset, value); 656 // This is a new property; transitions with specific values are not currently cachable, 657 // so leave the slot in an uncachable state. 658 if (!specificFunction) 659 slot.setNewProperty(this, offset); 660 return true; 661} 662 663inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) 664{ 665 ASSERT(value); 666 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 667 668 return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); 669} 670 671inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) 672{ 673 PutPropertySlot slot; 674 putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); 675} 676 677inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) 678{ 679 ASSERT(value); 680 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); 681 682 return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0); 683} 684 685inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) 686{ 687 PutPropertySlot slot; 688 putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0); 689} 690 691inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 692{ 693 return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0); 694} 695 696inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) 697{ 698 putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value); 699} 700 701inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr) 702{ 703 PutPropertySlot slot; 704 putDirectInternal(globalData, propertyName, value, attr, false, slot, value); 705} 706 707inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) 708{ 709 size_t currentCapacity = m_structure->propertyStorageCapacity(); 710 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); 711 if (currentCapacity != m_structure->propertyStorageCapacity()) 712 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); 713 putDirectOffset(globalData, offset, value); 714} 715 716inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes) 717{ 718 size_t currentCapacity = m_structure->propertyStorageCapacity(); 719 size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); 720 if (currentCapacity != m_structure->propertyStorageCapacity()) 721 allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); 722 putDirectOffset(globalData, offset, value); 723} 724 725inline void JSObject::transitionTo(Structure* newStructure) 726{ 727 if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) 728 allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); 729 setStructure(newStructure); 730} 731 732inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const 733{ 734 return defaultValue(exec, preferredType); 735} 736 737inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const 738{ 739 PropertySlot slot(asValue()); 740 return get(exec, propertyName, slot); 741} 742 743inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const 744{ 745 if (UNLIKELY(!isCell())) { 746 JSObject* prototype = synthesizePrototype(exec); 747 if (propertyName == exec->propertyNames().underscoreProto) 748 return prototype; 749 if (!prototype->getPropertySlot(exec, propertyName, slot)) 750 return jsUndefined(); 751 return slot.getValue(exec, propertyName); 752 } 753 JSCell* cell = asCell(); 754 while (true) { 755 if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) 756 return slot.getValue(exec, propertyName); 757 JSValue prototype = asObject(cell)->prototype(); 758 if (!prototype.isObject()) 759 return jsUndefined(); 760 cell = asObject(prototype); 761 } 762} 763 764inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const 765{ 766 PropertySlot slot(asValue()); 767 return get(exec, propertyName, slot); 768} 769 770inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const 771{ 772 if (UNLIKELY(!isCell())) { 773 JSObject* prototype = synthesizePrototype(exec); 774 if (!prototype->getPropertySlot(exec, propertyName, slot)) 775 return jsUndefined(); 776 return slot.getValue(exec, propertyName); 777 } 778 JSCell* cell = const_cast<JSCell*>(asCell()); 779 while (true) { 780 if (cell->getOwnPropertySlot(exec, propertyName, slot)) 781 return slot.getValue(exec, propertyName); 782 JSValue prototype = asObject(cell)->prototype(); 783 if (!prototype.isObject()) 784 return jsUndefined(); 785 cell = prototype.asCell(); 786 } 787} 788 789inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 790{ 791 if (UNLIKELY(!isCell())) { 792 synthesizeObject(exec)->put(exec, propertyName, value, slot); 793 return; 794 } 795 asCell()->put(exec, propertyName, value, slot); 796} 797 798inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 799{ 800 ASSERT(isCell() && isObject()); 801 if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode()) 802 throwTypeError(exec, StrictModeReadonlyPropertyWriteError); 803} 804 805inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) 806{ 807 if (UNLIKELY(!isCell())) { 808 synthesizeObject(exec)->put(exec, propertyName, value); 809 return; 810 } 811 asCell()->put(exec, propertyName, value); 812} 813 814ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) 815{ 816 JSCell::markChildren(markStack); 817 818 markStack.append(m_structure->storedPrototypeSlot()); 819 if (*m_structure->cachedPrototypeChainSlot()) 820 markStack.append(m_structure->cachedPrototypeChainSlot()); 821 PropertyStorage storage = propertyStorage(); 822 size_t storageSize = m_structure->propertyStorageSize(); 823 markStack.appendValues(storage, storageSize); 824} 825 826// --- JSValue inlines ---------------------------- 827 828ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const 829{ 830 return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec); 831} 832 833inline JSString* JSValue::toThisJSString(ExecState* exec) const 834{ 835 return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec)); 836} 837 838inline JSValue JSValue::toStrictThisObject(ExecState* exec) const 839{ 840 if (!isObject()) 841 return *this; 842 return asObject(asCell())->toStrictThisObject(exec); 843} 844 845ALWAYS_INLINE JSObject* Register::function() const 846{ 847 if (!jsValue()) 848 return 0; 849 return asObject(jsValue()); 850} 851 852ALWAYS_INLINE Register Register::withCallee(JSObject* callee) 853{ 854 Register r; 855 r = JSValue(callee); 856 return r; 857} 858 859} // namespace JSC 860 861#endif // JSObject_h 862