JSObject.h 38 KB
Newer Older
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 7 8 9 10 11 12 13 14 15 16 17
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
mjs's avatar
mjs committed
18
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 *  Boston, MA 02110-1301, USA.
20
 *
21 22
 */

weinig@apple.com's avatar
weinig@apple.com committed
23 24
#ifndef JSObject_h
#define JSObject_h
25

weinig@apple.com's avatar
weinig@apple.com committed
26
#include "ArgList.h"
weinig@apple.com's avatar
weinig@apple.com committed
27
#include "ClassInfo.h"
mjs's avatar
mjs committed
28
#include "CommonIdentifiers.h"
29
#include "Completion.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
30
#include "CallFrame.h"
oliver@apple.com's avatar
oliver@apple.com committed
31
#include "JSCell.h"
darin@apple.com's avatar
darin@apple.com committed
32
#include "PropertySlot.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
33
#include "PutPropertySlot.h"
darin@apple.com's avatar
darin@apple.com committed
34
#include "ScopeChain.h"
darin@apple.com's avatar
darin@apple.com committed
35
#include "Structure.h"
36
#include "JSGlobalData.h"
37
#include "JSString.h"
38
#include <wtf/StdLibExtras.h>
39

40
namespace JSC {
41

42 43 44 45 46 47
    inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value)
    {
        if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr))
            return value.asCell();
        return 0;
    }
48 49
    
    class HashEntry;
weinig@apple.com's avatar
weinig@apple.com committed
50
    class InternalFunction;
51
    class MarkedBlock;
52
    class PropertyDescriptor;
weinig@apple.com's avatar
weinig@apple.com committed
53
    class PropertyNameArray;
darin@apple.com's avatar
darin@apple.com committed
54
    class Structure;
weinig@apple.com's avatar
weinig@apple.com committed
55 56
    struct HashTable;

57 58 59
    JSObject* throwTypeError(ExecState*, const UString&);
    extern const char* StrictModeReadonlyPropertyWriteError;

weinig@apple.com's avatar
weinig@apple.com committed
60 61 62 63 64 65 66 67
    // ECMA 262-3 8.6.1
    // Property attributes
    enum Attribute {
        None         = 0,
        ReadOnly     = 1 << 1,  // property can be only read, not written
        DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
        DontDelete   = 1 << 3,  // property can't be deleted
        Function     = 1 << 4,  // property is a function - only used by static hashtables
68 69
        Getter       = 1 << 5,  // property is a getter
        Setter       = 1 << 6   // property is a setter
weinig@apple.com's avatar
weinig@apple.com committed
70 71
    };

72 73
    typedef WriteBarrierBase<Unknown>* PropertyStorage;
    typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage;
74

weinig@apple.com's avatar
weinig@apple.com committed
75
    class JSObject : public JSCell {
ggaren@apple.com's avatar
ggaren@apple.com committed
76
        friend class BatchedTransitionOptimizer;
77
        friend class JIT;
darin@apple.com's avatar
darin@apple.com committed
78
        friend class JSCell;
79
        friend class MarkedBlock;
80
        friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
ggaren@apple.com's avatar
ggaren@apple.com committed
81

weinig@apple.com's avatar
weinig@apple.com committed
82
    public:
83 84
        typedef JSCell Base;

85 86
        virtual void visitChildren(SlotVisitor&);
        ALWAYS_INLINE void visitChildrenDirect(SlotVisitor&);
weinig@apple.com's avatar
weinig@apple.com committed
87

mrowe@apple.com's avatar
mrowe@apple.com committed
88 89 90 91
        // The inline virtual destructor cannot be the first virtual function declared
        // in the class as it results in the vtable being generated as a weak symbol
        virtual ~JSObject();

92
        JSValue prototype() const;
93 94
        void setPrototype(JSGlobalData&, JSValue prototype);
        bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
weinig@apple.com's avatar
weinig@apple.com committed
95
        
96
        void setStructure(JSGlobalData&, Structure*);
97
        Structure* inheritorID(JSGlobalData&);
weinig@apple.com's avatar
weinig@apple.com committed
98 99 100

        virtual UString className() const;

ggaren@apple.com's avatar
ggaren@apple.com committed
101 102
        JSValue get(ExecState*, const Identifier& propertyName) const;
        JSValue get(ExecState*, unsigned propertyName) const;
weinig@apple.com's avatar
weinig@apple.com committed
103 104 105

        bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
        bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
106
        bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
weinig@apple.com's avatar
weinig@apple.com committed
107 108 109

        virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
        virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
110
        virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
weinig@apple.com's avatar
weinig@apple.com committed
111

ggaren@apple.com's avatar
ggaren@apple.com committed
112 113
        virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&);
        virtual void put(ExecState*, unsigned propertyName, JSValue value);
weinig@apple.com's avatar
weinig@apple.com committed
114

115 116 117
        virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
        virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes);
        virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes);
118
        virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
ggaren@apple.com's avatar
ggaren@apple.com committed
119 120
        virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes);
        virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes);
weinig@apple.com's avatar
weinig@apple.com committed
121 122 123 124 125 126 127 128 129 130

        bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;

        bool hasProperty(ExecState*, const Identifier& propertyName) const;
        bool hasProperty(ExecState*, unsigned propertyName) const;
        bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;

        virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
        virtual bool deleteProperty(ExecState*, unsigned propertyName);

ggaren@apple.com's avatar
ggaren@apple.com committed
131
        virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const;
weinig@apple.com's avatar
weinig@apple.com committed
132

ggaren@apple.com's avatar
ggaren@apple.com committed
133
        virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty);
weinig@apple.com's avatar
weinig@apple.com committed
134

135 136
        virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
weinig@apple.com's avatar
weinig@apple.com committed
137

ggaren@apple.com's avatar
ggaren@apple.com committed
138 139
        virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
        virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
mjs@apple.com's avatar
mjs@apple.com committed
140
        virtual bool toBoolean(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
141 142
        virtual double toNumber(ExecState*) const;
        virtual UString toString(ExecState*) const;
143
        virtual JSObject* toObject(ExecState*, JSGlobalObject*) const;
weinig@apple.com's avatar
weinig@apple.com committed
144 145

        virtual JSObject* toThisObject(ExecState*) const;
146
        virtual JSValue toStrictThisObject(ExecState*) const;
darin@apple.com's avatar
darin@apple.com committed
147
        virtual JSObject* unwrappedObject();
weinig@apple.com's avatar
weinig@apple.com committed
148

149
        bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const;
weinig@apple.com's avatar
weinig@apple.com committed
150 151

        // This get function only looks at the property map.
152
        JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const
weinig@apple.com's avatar
weinig@apple.com committed
153
        {
154
            size_t offset = m_structure->get(globalData, propertyName);
155 156 157
            return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
        }

158
        WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName)
weinig@apple.com's avatar
weinig@apple.com committed
159
        {
160
            size_t offset = m_structure->get(globalData, propertyName);
161
            return offset != WTF::notFound ? locationForOffset(offset) : 0;
weinig@apple.com's avatar
weinig@apple.com committed
162 163
        }

164
        WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes)
weinig@apple.com's avatar
weinig@apple.com committed
165
        {
166
            JSCell* specificFunction;
167
            size_t offset = m_structure->get(globalData, propertyName, attributes, specificFunction);
168
            return offset != WTF::notFound ? locationForOffset(offset) : 0;
weinig@apple.com's avatar
weinig@apple.com committed
169 170
        }

171
        size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
172
        {
173
            return location - propertyStorage();
174 175
        }

176
        void transitionTo(JSGlobalData&, Structure*);
177

178
        void removeDirect(JSGlobalData&, const Identifier& propertyName);
179
        bool hasCustomProperties() { return m_structure->didTransition(); }
darin@apple.com's avatar
darin@apple.com committed
180
        bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); }
weinig@apple.com's avatar
weinig@apple.com committed
181

182 183 184
        bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
        void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
        bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&);
185

186 187
        void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr = 0);
        void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr, bool checkReadOnly, PutPropertySlot&);
weinig@apple.com's avatar
weinig@apple.com committed
188
        void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0);
189
        void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0);
190

191 192
        void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
        void putDirectFunctionWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSCell* value, unsigned attr = 0);
193
        void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0);
194
        void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0);
weinig@apple.com's avatar
weinig@apple.com committed
195

weinig@apple.com's avatar
weinig@apple.com committed
196
        // Fast access to known property offsets.
197 198 199
        JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); }
        void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
        void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
weinig@apple.com's avatar
weinig@apple.com committed
200

201
        void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
weinig@apple.com's avatar
weinig@apple.com committed
202

203 204
        virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
        virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
ggaren@apple.com's avatar
ggaren@apple.com committed
205 206
        virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
        virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
207
        virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
weinig@apple.com's avatar
weinig@apple.com committed
208 209 210

        virtual bool isGlobalObject() const { return false; }
        virtual bool isVariableObject() const { return false; }
weinig@apple.com's avatar
weinig@apple.com committed
211
        virtual bool isActivationObject() const { return false; }
212
        virtual bool isStrictModeFunction() const { return false; }
213
        virtual bool isErrorInstance() const { return false; }
weinig@apple.com's avatar
weinig@apple.com committed
214

215 216
        void seal(JSGlobalData&);
        void freeze(JSGlobalData&);
217
        virtual void preventExtensions(JSGlobalData&);
218 219
        bool isSealed(JSGlobalData& globalData) { return m_structure->isSealed(globalData); }
        bool isFrozen(JSGlobalData& globalData) { return m_structure->isFrozen(globalData); }
220 221
        bool isExtensible() { return m_structure->isExtensible(); }

222 223
        virtual ComplType exceptionType() const { return Throw; }

224
        void allocatePropertyStorage(size_t oldSize, size_t newSize);
225
        bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage) == static_cast<const void*>(this + 1); }
226

227 228 229 230 231
        void* addressOfPropertyAtOffset(size_t offset)
        {
            return static_cast<void*>(&m_propertyStorage[offset]);
        }

232
        static const unsigned baseExternalStorageCapacity = 16;
mjs@apple.com's avatar
mjs@apple.com committed
233

234
        void flattenDictionaryObject(JSGlobalData& globalData)
235
        {
236
            m_structure->flattenDictionaryStructure(globalData, this);
237 238
        }

239
        void putAnonymousValue(JSGlobalData& globalData, unsigned index, JSValue value)
240
        {
241
            ASSERT(index < m_structure->anonymousSlotCount());
242 243 244 245 246 247
            locationForOffset(index)->set(globalData, this, value);
        }
        void clearAnonymousValue(unsigned index)
        {
            ASSERT(index < m_structure->anonymousSlotCount());
            locationForOffset(index)->clear();
248
        }
249
        JSValue getAnonymousValue(unsigned index) const
250
        {
251
            ASSERT(index < m_structure->anonymousSlotCount());
252
            return locationForOffset(index)->get();
253
        }
254 255

        static size_t offsetOfInlineStorage();
256
        static size_t offsetOfPropertyStorage();
257
        static size_t offsetOfInheritorID();
258

259
        static JS_EXPORTDATA const ClassInfo s_info;
260

261
    protected:
262 263 264 265 266 267 268
        void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
        {
            Base::finishCreation(globalData);
            ASSERT(inherits(&s_info));
            ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity);
            ASSERT(m_structure->isEmpty());
            ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
269
            ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
270 271 272
            ASSERT(m_structure->typeInfo().type() == ObjectType);
        }

273
        static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
274
        {
275
            return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
276 277
        }

278
        static const unsigned StructureFlags = 0;
279

280 281 282 283
        void putThisToAnonymousValue(unsigned index)
        {
            locationForOffset(index)->setWithoutWriteBarrier(this);
        }
284 285 286

        // To instantiate objects you likely want JSFinalObject, below.
        // To create derived types you likely want JSNonFinalObject, below.
287 288 289 290 291 292
        JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
        JSObject(VPtrStealingHackType, PropertyStorage inlineStorage)
            : JSCell(VPtrStealingHack)
            , m_propertyStorage(inlineStorage)
        {
        }
293

weinig@apple.com's avatar
weinig@apple.com committed
294
    private:
295 296 297 298 299
        // Nobody should ever ask any of these questions on something already known to be a JSObject.
        using JSCell::isAPIValueWrapper;
        using JSCell::isGetterSetter;
        using JSCell::toObject;
        void getObject();
300
        void getString(ExecState* exec);
301 302
        void isObject();
        void isString();
303
        
304 305
        ConstPropertyStorage propertyStorage() const { return m_propertyStorage; }
        PropertyStorage propertyStorage() { return m_propertyStorage; }
306

307
        const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
308
        {
309
            return &propertyStorage()[offset];
310 311
        }

312
        WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
313
        {
314
            return &propertyStorage()[offset];
315 316
        }

317 318
        bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
        bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
319 320
        void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0);

darin@apple.com's avatar
darin@apple.com committed
321 322
        bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);

weinig@apple.com's avatar
weinig@apple.com committed
323
        const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
324
        Structure* createInheritorID(JSGlobalData&);
weinig@apple.com's avatar
weinig@apple.com committed
325

326
        PropertyStorage m_propertyStorage;
327
        WriteBarrier<Structure> m_inheritorID;
328
    };
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347


#if USE(JSVALUE32_64)
#define JSNonFinalObject_inlineStorageCapacity 4
#define JSFinalObject_inlineStorageCapacity 6
#else
#define JSNonFinalObject_inlineStorageCapacity 2
#define JSFinalObject_inlineStorageCapacity 4
#endif

COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);

    // JSNonFinalObject is a type of JSObject that has some internal storage,
    // but also preserves some space in the collector cell for additional
    // data members in derived types.
    class JSNonFinalObject : public JSObject {
        friend class JSObject;

    public:
348 349
        typedef JSObject Base;

350
        static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
351
        {
352
            return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
353 354
        }

355
    protected:
356 357 358 359 360 361 362
        explicit JSNonFinalObject(VPtrStealingHackType)
            : JSObject(VPtrStealingHack, m_inlineStorage)
        {
        }
    
        explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
            : JSObject(globalData, structure, m_inlineStorage)
363
        {
364 365 366 367 368 369
            finishCreation(globalData);
        }

        void finishCreation(JSGlobalData& globalData)
        {
            Base::finishCreation(globalData, m_inlineStorage);
370
            ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
371
            ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
372 373 374
        }

    private:
375
        WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
376 377 378 379 380 381 382 383
    };

    // JSFinalObject is a type of JSObject that contains sufficent internal
    // storage to fully make use of the colloctor cell containing it.
    class JSFinalObject : public JSObject {
        friend class JSObject;

    public:
384 385
        typedef JSObject Base;

386 387 388 389 390
        explicit JSFinalObject(VPtrStealingHackType)
            : JSObject(VPtrStealingHack, m_inlineStorage)
        {
        }
        
391
        static JSFinalObject* create(ExecState* exec, Structure* structure)
392
        {
393 394 395
            JSFinalObject* finalObject = new (allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure);
            finalObject->finishCreation(exec->globalData());
            return finalObject;
396 397
        }

398
        static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
399
        {
400
            return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
401 402
        }

403 404 405 406 407 408 409 410
    protected:
        void finishCreation(JSGlobalData& globalData)
        {
            Base::finishCreation(globalData, m_inlineStorage);
            ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
            ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
        }

411
    private:
412 413
        explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
            : JSObject(globalData, structure, m_inlineStorage)
414 415 416 417 418 419 420 421 422 423 424 425 426 427
        {
        }

        static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject;

        WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
    };

inline size_t JSObject::offsetOfInlineStorage()
{
    ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
    return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
}

428 429 430 431 432
inline size_t JSObject::offsetOfPropertyStorage()
{
    return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
}

433 434 435 436 437
inline size_t JSObject::offsetOfInheritorID()
{
    return OBJECT_OFFSETOF(JSObject, m_inheritorID);
}

438
inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
439 440 441 442
{
    return JSFinalObject::create(exec, structure);
}

443
inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSValue prototype)
444
{
445
    return JSFinalObject::createStructure(globalData, prototype);
446 447
}

448 449 450 451 452 453
inline JSObject* asObject(JSCell* cell)
{
    ASSERT(cell->isObject());
    return static_cast<JSObject*>(cell);
}

ggaren@apple.com's avatar
ggaren@apple.com committed
454
inline JSObject* asObject(JSValue value)
darin@apple.com's avatar
darin@apple.com committed
455
{
456
    return asObject(value.asCell());
darin@apple.com's avatar
darin@apple.com committed
457
}
darin's avatar
darin committed
458

459 460
inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
    : JSCell(globalData, structure)
461
    , m_propertyStorage(inlineStorage)
weinig@apple.com's avatar
weinig@apple.com committed
462
{
darin's avatar
darin committed
463 464
}

weinig@apple.com's avatar
weinig@apple.com committed
465
inline JSObject::~JSObject()
darin's avatar
darin committed
466
{
467
    if (!isUsingInlineStorage())
468
        delete [] m_propertyStorage;
darin's avatar
darin committed
469 470
}

471
inline JSValue JSObject::prototype() const
darin's avatar
darin committed
472
{
darin@apple.com's avatar
darin@apple.com committed
473
    return m_structure->storedPrototype();
darin's avatar
darin committed
474 475
}

476
inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
477 478 479 480 481 482 483 484
{
    JSValue nextPrototypeValue = prototype;
    while (nextPrototypeValue && nextPrototypeValue.isObject()) {
        JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
        if (nextPrototype == this)
            return false;
        nextPrototypeValue = nextPrototype->prototype();
    }
485
    setPrototype(globalData, prototype);
486 487 488
    return true;
}

489
inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
darin's avatar
darin committed
490
{
weinig@apple.com's avatar
weinig@apple.com committed
491
    ASSERT(prototype);
492
    setStructure(globalData, Structure::changePrototypeTransition(globalData, m_structure.get(), prototype));
weinig@apple.com's avatar
weinig@apple.com committed
493 494
}

495
inline void JSObject::setStructure(JSGlobalData& globalData, Structure* structure)
weinig@apple.com's avatar
weinig@apple.com committed
496
{
497
    ASSERT(structure->typeInfo().overridesVisitChildren() == m_structure->typeInfo().overridesVisitChildren());
498
    m_structure.set(globalData, this, structure);
weinig@apple.com's avatar
weinig@apple.com committed
499 500
}

501
inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
weinig@apple.com's avatar
weinig@apple.com committed
502
{
503 504
    if (m_inheritorID) {
        ASSERT(m_inheritorID->isEmpty());
weinig@apple.com's avatar
weinig@apple.com committed
505
        return m_inheritorID.get();
506
    }
507
    return createInheritorID(globalData);
darin's avatar
darin committed
508 509
}

510 511
inline bool Structure::isUsingInlineStorage() const
{
512
    return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
513 514
}

515
inline bool JSCell::inherits(const ClassInfo* info) const
darin's avatar
darin committed
516
{
darin@apple.com's avatar
darin@apple.com committed
517
    for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
darin's avatar
darin committed
518 519
        if (ci == info)
            return true;
darin@apple.com's avatar
darin@apple.com committed
520
    }
darin's avatar
darin committed
521 522
    return false;
}
523

524 525
// this method is here to be after the inline declaration of JSCell::inherits
inline bool JSValue::inherits(const ClassInfo* classInfo) const
mjs's avatar
mjs committed
526
{
527
    return isCell() && asCell()->inherits(classInfo);
mjs's avatar
mjs committed
528 529
}

darin@apple.com's avatar
darin@apple.com committed
530
ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
531
{
532
    if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
533
        if (m_structure->hasGetterSetterProperties() && location->isGetterSetter())
darin@apple.com's avatar
darin@apple.com committed
534 535
            fillGetterPropertySlot(slot, location);
        else
536
            slot.setValue(this, location->get(), offsetForLocation(location));
darin@apple.com's avatar
darin@apple.com committed
537
        return true;
darin's avatar
darin committed
538 539
    }

darin@apple.com's avatar
darin@apple.com committed
540 541 542 543
    // non-standard Netscape extension
    if (propertyName == exec->propertyNames().underscoreProto) {
        slot.setValue(prototype());
        return true;
weinig@apple.com's avatar
weinig@apple.com committed
544 545 546
    }

    return false;
547 548
}

darin's avatar
darin committed
549 550 551
// It may seem crazy to inline a function this large, especially a virtual function,
// but it makes a big difference to property lookup that derived classes can inline their
// base class call to this.
mjs's avatar
mjs committed
552
ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
darin's avatar
darin committed
553
{
darin@apple.com's avatar
darin@apple.com committed
554 555 556 557 558
    return inlineGetOwnPropertySlot(exec, propertyName, slot);
}

ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
559
    if (!structure()->typeInfo().overridesGetOwnPropertySlot())
darin@apple.com's avatar
darin@apple.com committed
560 561 562 563
        return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
    return getOwnPropertySlot(exec, propertyName, slot);
}

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
// Fast call to get a property where we may not yet have converted the string to an
// identifier. The first time we perform a property access with a given string, try
// performing the property map lookup without forming an identifier. We detect this
// case by checking whether the hash has yet been set for this string.
ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name)
{
    if (!m_structure->typeInfo().overridesGetOwnPropertySlot() && !m_structure->hasGetterSetterProperties()) {
        size_t offset = name.impl()->hasHash()
            ? m_structure->get(exec->globalData(), Identifier(exec, name))
            : m_structure->get(exec->globalData(), name);
        if (offset != WTF::notFound)
            return asObject(this)->locationForOffset(offset)->get();
    }
    return JSValue();
}

darin@apple.com's avatar
darin@apple.com committed
580 581
// It may seem crazy to inline a function this large but it makes a big difference
// since this is function very hot in variable lookup
582
ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
darin@apple.com's avatar
darin@apple.com committed
583 584 585 586 587
{
    JSObject* object = this;
    while (true) {
        if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
            return true;
ggaren@apple.com's avatar
ggaren@apple.com committed
588
        JSValue prototype = object->prototype();
weinig@apple.com's avatar
weinig@apple.com committed
589
        if (!prototype.isObject())
darin@apple.com's avatar
darin@apple.com committed
590 591
            return false;
        object = asObject(prototype);
darin's avatar
darin committed
592
    }
darin@apple.com's avatar
darin@apple.com committed
593
}
darin's avatar
darin committed
594

595
ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
darin@apple.com's avatar
darin@apple.com committed
596 597 598 599 600
{
    JSObject* object = this;
    while (true) {
        if (object->getOwnPropertySlot(exec, propertyName, slot))
            return true;
ggaren@apple.com's avatar
ggaren@apple.com committed
601
        JSValue prototype = object->prototype();
weinig@apple.com's avatar
weinig@apple.com committed
602
        if (!prototype.isObject())
darin@apple.com's avatar
darin@apple.com committed
603 604
            return false;
        object = asObject(prototype);
darin's avatar
darin committed
605
    }
darin@apple.com's avatar
darin@apple.com committed
606
}
darin's avatar
darin committed
607

ggaren@apple.com's avatar
ggaren@apple.com committed
608
inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
darin@apple.com's avatar
darin@apple.com committed
609 610 611 612 613 614 615 616
{
    PropertySlot slot(this);
    if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
        return slot.getValue(exec, propertyName);
    
    return jsUndefined();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
617
inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
darin@apple.com's avatar
darin@apple.com committed
618 619 620 621 622 623
{
    PropertySlot slot(this);
    if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
        return slot.getValue(exec, propertyName);

    return jsUndefined();
darin's avatar
darin committed
624 625
}

626
inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
627
{
628
    ASSERT(value);
weinig@apple.com's avatar
weinig@apple.com committed
629
    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
weinig@apple.com's avatar
weinig@apple.com committed
630

darin@apple.com's avatar
darin@apple.com committed
631
    if (m_structure->isDictionary()) {
632
        unsigned currentAttributes;
633
        JSCell* currentSpecificFunction;
634
        size_t offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
635
        if (offset != WTF::notFound) {
636 637
            // If there is currently a specific function, and there now either isn't,
            // or the new value is different, then despecify.
638
            if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
639
                m_structure->despecifyDictionaryFunction(globalData, propertyName);
640
            if (checkReadOnly && currentAttributes & ReadOnly)
641 642
                return false;

643
            putDirectOffset(globalData, offset, value);
644 645 646 647 648 649 650
            // At this point, the objects structure only has a specific value set if previously there
            // had been one set, and if the new value being specified is the same (otherwise we would
            // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
            // value is different (or there is no new value), then the slot now has no value - and
            // as such it is cachable.
            // If there was previously a value, and the new value is the same, then we cannot cache.
            if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
651
                slot.setExistingProperty(this, offset);
652
            return true;
653 654
        }

655
        if (checkReadOnly && !isExtensible())
656 657
            return false;

darin@apple.com's avatar
darin@apple.com committed
658
        size_t currentCapacity = m_structure->propertyStorageCapacity();
659
        offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
darin@apple.com's avatar
darin@apple.com committed
660 661
        if (currentCapacity != m_structure->propertyStorageCapacity())
            allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
662

darin@apple.com's avatar
darin@apple.com committed
663
        ASSERT(offset < m_structure->propertyStorageCapacity());
664
        putDirectOffset(globalData, offset, value);
665 666 667
        // See comment on setNewProperty call below.
        if (!specificFunction)
            slot.setNewProperty(this, offset);
668
        return true;
669
    }
670

671
    size_t offset;
darin@apple.com's avatar
darin@apple.com committed
672
    size_t currentCapacity = m_structure->propertyStorageCapacity();
673
    if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(m_structure.get(), propertyName, attributes, specificFunction, offset)) {    
darin@apple.com's avatar
darin@apple.com committed
674 675
        if (currentCapacity != structure->propertyStorageCapacity())
            allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
676

darin@apple.com's avatar
darin@apple.com committed
677
        ASSERT(offset < structure->propertyStorageCapacity());
678
        setStructure(globalData, structure);
679
        putDirectOffset(globalData, offset, value);
680 681
        // This is a new property; transitions with specific values are not currently cachable,
        // so leave the slot in an uncachable state.
682 683
        if (!specificFunction)
            slot.setNewProperty(this, offset);
684
        return true;
685 686
    }

687
    unsigned currentAttributes;
688
    JSCell* currentSpecificFunction;
689
    offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
690 691
    if (offset != WTF::notFound) {
        if (checkReadOnly && currentAttributes & ReadOnly)
692
            return false;
693

694 695
        // There are three possibilities here:
        //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
696
        //       * Do nothing - no need to despecify, but that means we can't cache (a cached
697 698 699 700 701 702 703 704 705
        //         put could write a different value). Leave the slot in an uncachable state.
        //  (2) There is a specific value currently set, but we're writing a different value.
        //       * First, we have to despecify.  Having done so, this is now a regular slot
        //         with no specific value, so go ahead & cache like normal.
        //  (3) Normal case, there is no specific value set.
        //       * Go ahead & cache like normal.
        if (currentSpecificFunction) {
            // case (1) Do the put, then return leaving the slot uncachable.
            if (specificFunction == currentSpecificFunction) {
706
                putDirectOffset(globalData, offset, value);
707
                return true;
708 709
            }
            // case (2) Despecify, fall through to (3).
710
            setStructure(globalData, Structure::despecifyFunctionTransition(globalData, m_structure.get(), propertyName));
711
        }
712 713

        // case (3) set the slot, do the put, return.
714
        slot.setExistingProperty(this, offset);
715
        putDirectOffset(globalData, offset, value);
716
        return true;
717
    }
718

719
    if (checkReadOnly && !isExtensible())
720 721
        return false;

722
    Structure* structure = Structure::addPropertyTransition(globalData, m_structure.get(), propertyName, attributes, specificFunction, offset);
723

darin@apple.com's avatar
darin@apple.com committed
724 725
    if (currentCapacity != structure->propertyStorageCapacity())
        allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
726

darin@apple.com's avatar
darin@apple.com committed
727
    ASSERT(offset < structure->propertyStorageCapacity());
728
    setStructure(globalData, structure);
729
    putDirectOffset(globalData, offset, value);
730 731
    // This is a new property; transitions with specific values are not currently cachable,
    // so leave the slot in an uncachable state.
732 733
    if (!specificFunction)
        slot.setNewProperty(this, offset);
734
    return true;
735 736
}

737
inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
738 739 740 741
{
    ASSERT(value);
    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));

742
    return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
743 744 745 746 747
}

inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
{
    PutPropertySlot slot;