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