Home | History | Annotate | Download | only in runtime
      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