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