JSObject.h 55.8 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, 2012 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"
27 28 29
#include "ArrayConventions.h"
#include "ArrayStorage.h"
#include "Butterfly.h"
weinig@apple.com's avatar
weinig@apple.com committed
30
#include "ClassInfo.h"
mjs's avatar
mjs committed
31
#include "CommonIdentifiers.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
32
#include "CallFrame.h"
oliver@apple.com's avatar
oliver@apple.com committed
33
#include "JSCell.h"
darin@apple.com's avatar
darin@apple.com committed
34
#include "PropertySlot.h"
35 36
#include "PropertyStorage.h"
#include "PutDirectIndexMode.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
37
#include "PutPropertySlot.h"
38

darin@apple.com's avatar
darin@apple.com committed
39
#include "Structure.h"
40
#include "JSGlobalData.h"
41
#include "JSString.h"
42
#include "SlotVisitorInlines.h"
43
#include "SparseArrayValueMap.h"
44
#include <wtf/StdLibExtras.h>
45

46
namespace JSC {
47

48 49 50 51 52 53
inline JSCell* getJSFunction(JSValue value)
{
    if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType))
        return value.asCell();
    return 0;
}
54

55
JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*);
56

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
inline JSCell* getCallableObject(JSValue value)
{
    if (!value.isCell())
        return 0;
    return getCallableObjectSlow(value.asCell());
}

class GetterSetter;
class HashEntry;
class InternalFunction;
class LLIntOffsetsExtractor;
class MarkedBlock;
class PropertyDescriptor;
class PropertyNameArray;
class Structure;
struct HashTable;

JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&);
extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError;

// 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
    Accessor     = 1 << 5,  // property is a getter/setter
};

COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute);
COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);

class JSFinalObject;

class JSObject : public JSCell {
    friend class BatchedTransitionOptimizer;
    friend class JIT;
    friend class JSCell;
    friend class JSFinalObject;
    friend class MarkedBlock;
    JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject*, PropertyName, PropertySlot&);

    enum PutMode {
        PutModePut,
        PutModeDefineOwnProperty,
weinig@apple.com's avatar
weinig@apple.com committed
108 109
    };

110 111 112 113 114 115 116
public:
    typedef JSCell Base;
        
    static size_t allocationSize(size_t inlineCapacity)
    {
        return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>);
    }
117
        
118 119
    JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
    JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&);
weinig@apple.com's avatar
weinig@apple.com committed
120

121
    JS_EXPORT_PRIVATE static String className(const JSObject*);
122

123 124 125
    JSValue prototype() const;
    void setPrototype(JSGlobalData&, JSValue prototype);
    bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
weinig@apple.com's avatar
weinig@apple.com committed
126
        
127 128 129 130
    bool mayInterceptIndexedAccesses()
    {
        return structure()->mayInterceptIndexedAccesses();
    }
131
        
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
    JSValue get(ExecState*, PropertyName) const;
    JSValue get(ExecState*, unsigned propertyName) const;

    bool getPropertySlot(ExecState*, PropertyName, PropertySlot&);
    bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
    JS_EXPORT_PRIVATE bool getPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);

    static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
    JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
    JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);

    bool allowsAccessFrom(ExecState*);

    unsigned getArrayLength() const
    {
147
        if (!hasIndexedProperties(structure()->indexingType()))
148
            return 0;
149
        return m_butterfly->publicLength();
150
    }
151
        
152 153
    unsigned getVectorLength()
    {
154
        if (!hasIndexedProperties(structure()->indexingType()))
155
            return 0;
156
        return m_butterfly->vectorLength();
157
    }
158
        
159 160
    JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
    JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
161
        
162 163 164 165 166
    void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
    {
        if (canSetIndexQuickly(propertyName)) {
            setIndexQuickly(exec->globalData(), propertyName, value);
            return;
167
        }
168 169
        methodTable()->putByIndex(this, exec, propertyName, value, shouldThrow);
    }
170
        
171 172 173 174 175 176 177 178 179 180
    // This is similar to the putDirect* methods:
    //  - the prototype chain is not consulted
    //  - accessors are not called.
    //  - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
    // This method creates a property with attributes writable, enumerable and configurable all set to true.
    bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
    {
        if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) {
            setIndexQuickly(exec->globalData(), propertyName, value);
            return true;
181
        }
182 183 184 185 186 187
        return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode);
    }
    bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value)
    {
        return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect);
    }
188

189 190
    // A non-throwing version of putDirect and putDirectIndex.
    JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
191
        
192 193 194 195
    bool canGetIndexQuickly(unsigned i)
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
196
        case ALL_UNDECIDED_INDEXING_TYPES:
197
            return false;
198
        case ALL_INT32_INDEXING_TYPES:
199 200
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i];
201 202 203 204 205 206 207 208
        case ALL_DOUBLE_INDEXING_TYPES: {
            if (i >= m_butterfly->vectorLength())
                return false;
            double value = m_butterfly->contiguousDouble()[i];
            if (value != value)
                return false;
            return true;
        }
209 210 211
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i];
        default:
212
            RELEASE_ASSERT_NOT_REACHED();
213
            return false;
214
        }
215
    }
216
        
217 218 219
    JSValue getIndexQuickly(unsigned i)
    {
        switch (structure()->indexingType()) {
220
        case ALL_INT32_INDEXING_TYPES:
221 222
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->contiguous()[i].get();
223 224
        case ALL_DOUBLE_INDEXING_TYPES:
            return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]);
225 226 227
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return m_butterfly->arrayStorage()->m_vector[i].get();
        default:
228
            RELEASE_ASSERT_NOT_REACHED();
229 230
            return JSValue();
        }
231
    }
232
        
233 234 235 236
    JSValue tryGetIndexQuickly(unsigned i)
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
237
        case ALL_UNDECIDED_INDEXING_TYPES:
238
            break;
239
        case ALL_INT32_INDEXING_TYPES:
240 241 242 243
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            if (i < m_butterfly->publicLength())
                return m_butterfly->contiguous()[i].get();
            break;
244 245 246 247 248 249 250 251
        case ALL_DOUBLE_INDEXING_TYPES: {
            if (i >= m_butterfly->publicLength())
                break;
            double result = m_butterfly->contiguousDouble()[i];
            if (result != result)
                break;
            return JSValue(JSValue::EncodeAsDouble, result);
        }
252 253 254 255 256
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            if (i < m_butterfly->arrayStorage()->vectorLength())
                return m_butterfly->arrayStorage()->m_vector[i].get();
            break;
        default:
257
            RELEASE_ASSERT_NOT_REACHED();
258
            break;
259
        }
260 261
        return JSValue();
    }
262
        
263 264 265 266 267 268 269 270 271
    JSValue getDirectIndex(ExecState* exec, unsigned i)
    {
        if (JSValue result = tryGetIndexQuickly(i))
            return result;
        PropertySlot slot(this);
        if (methodTable()->getOwnPropertySlotByIndex(this, exec, i, slot))
            return slot.getValue(exec, i);
        return JSValue();
    }
272
        
273 274 275 276 277 278 279 280 281 282 283
    JSValue getIndex(ExecState* exec, unsigned i)
    {
        if (JSValue result = tryGetIndexQuickly(i))
            return result;
        return get(exec, i);
    }
        
    bool canSetIndexQuickly(unsigned i)
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
284
        case ALL_UNDECIDED_INDEXING_TYPES:
285
            return false;
286 287
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
288 289 290 291 292 293 294 295 296
        case ALL_CONTIGUOUS_INDEXING_TYPES:
        case NonArrayWithArrayStorage:
        case ArrayWithArrayStorage:
            return i < m_butterfly->vectorLength();
        case NonArrayWithSlowPutArrayStorage:
        case ArrayWithSlowPutArrayStorage:
            return i < m_butterfly->arrayStorage()->vectorLength()
                && !!m_butterfly->arrayStorage()->m_vector[i];
        default:
297
            RELEASE_ASSERT_NOT_REACHED();
298
            return false;
299
        }
300
    }
301
        
302 303 304 305
    bool canSetIndexQuicklyForPutDirect(unsigned i)
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
306
        case ALL_UNDECIDED_INDEXING_TYPES:
307
            return false;
308 309
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
310 311 312 313
        case ALL_CONTIGUOUS_INDEXING_TYPES:
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return i < m_butterfly->vectorLength();
        default:
314
            RELEASE_ASSERT_NOT_REACHED();
315
            return false;
316
        }
317
    }
318
        
319 320 321
    void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v)
    {
        switch (structure()->indexingType()) {
322 323 324 325 326 327 328 329
        case ALL_INT32_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isInt32()) {
                convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v);
                return;
            }
            // Fall through to contiguous case.
        }
330 331 332 333 334 335 336
        case ALL_CONTIGUOUS_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
            m_butterfly->contiguous()[i].set(globalData, this, v);
            if (i >= m_butterfly->publicLength())
                m_butterfly->setPublicLength(i + 1);
            break;
        }
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
        case ALL_DOUBLE_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isNumber()) {
                convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v);
                return;
            }
            double value = v.asNumber();
            if (value != value) {
                convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v);
                return;
            }
            m_butterfly->contiguousDouble()[i] = value;
            if (i >= m_butterfly->publicLength())
                m_butterfly->setPublicLength(i + 1);
            break;
        }
353 354 355 356 357 358 359 360 361
        case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
            ArrayStorage* storage = m_butterfly->arrayStorage();
            WriteBarrier<Unknown>& x = storage->m_vector[i];
            JSValue old = x.get();
            x.set(globalData, this, v);
            if (!old) {
                ++storage->m_numValuesInVector;
                if (i >= storage->length())
                    storage->setLength(i + 1);
362
            }
363 364 365
            break;
        }
        default:
366
            RELEASE_ASSERT_NOT_REACHED();
367
        }
368
    }
369
        
370 371 372
    void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
    {
        switch (structure()->indexingType()) {
373 374 375 376 377 378 379 380 381 382 383 384 385
        case ALL_UNDECIDED_INDEXING_TYPES: {
            setIndexQuicklyToUndecided(globalData, i, v);
            break;
        }
        case ALL_INT32_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isInt32()) {
                convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(globalData, i, v);
                break;
            }
            // Fall through.
        }
386 387 388 389 390 391
        case ALL_CONTIGUOUS_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
            m_butterfly->contiguous()[i].set(globalData, this, v);
            break;
        }
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
        case ALL_DOUBLE_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isNumber()) {
                convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v);
                return;
            }
            double value = v.asNumber();
            if (value != value) {
                convertDoubleToContiguousWhilePerformingSetIndex(globalData, i, v);
                return;
            }
            m_butterfly->contiguousDouble()[i] = value;
            break;
        }
407 408 409 410 411 412 413 414
        case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
            ArrayStorage* storage = m_butterfly->arrayStorage();
            ASSERT(i < storage->length());
            ASSERT(i < storage->m_numValuesInVector);
            storage->m_vector[i].set(globalData, this, v);
            break;
        }
        default:
415
            RELEASE_ASSERT_NOT_REACHED();
416
        }
417
    }
418
        
419 420 421 422
    bool hasSparseMap()
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
423 424 425
        case ALL_UNDECIDED_INDEXING_TYPES:
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
426 427 428 429 430
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return false;
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return m_butterfly->arrayStorage()->m_sparseMap;
        default:
431
            RELEASE_ASSERT_NOT_REACHED();
432
            return false;
433
        }
434
    }
435
        
436 437 438 439
    bool inSparseIndexingMode()
    {
        switch (structure()->indexingType()) {
        case ALL_BLANK_INDEXING_TYPES:
440 441 442
        case ALL_UNDECIDED_INDEXING_TYPES:
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
443 444 445 446 447
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return false;
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return m_butterfly->arrayStorage()->inSparseMode();
        default:
448
            RELEASE_ASSERT_NOT_REACHED();
449
            return false;
450
        }
451
    }
452
        
453
    void enterDictionaryIndexingMode(JSGlobalData&);
weinig@apple.com's avatar
weinig@apple.com committed
454

455 456 457 458 459 460 461 462 463 464
    // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
    //  - the prototype chain is not consulted
    //  - accessors are not called.
    //  - attributes will be respected (after the call the property will exist with the given attributes)
    //  - the property name is assumed to not be an index.
    JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
    void putDirect(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
    void putDirect(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
    void putDirectWithoutTransition(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
    void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
weinig@apple.com's avatar
weinig@apple.com committed
465

466
    bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
weinig@apple.com's avatar
weinig@apple.com committed
467

468 469 470
    JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const;
    JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
    bool hasOwnProperty(ExecState*, PropertyName) const;
weinig@apple.com's avatar
weinig@apple.com committed
471

472 473
    JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
    JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
weinig@apple.com's avatar
weinig@apple.com committed
474

475
    JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
weinig@apple.com's avatar
weinig@apple.com committed
476

477 478
    bool hasInstance(ExecState*, JSValue);
    static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
weinig@apple.com's avatar
weinig@apple.com committed
479

480 481 482
    JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
weinig@apple.com's avatar
weinig@apple.com committed
483

484 485 486 487
    JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
    bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
    JS_EXPORT_PRIVATE double toNumber(ExecState*) const;
    JS_EXPORT_PRIVATE JSString* toString(ExecState*) const;
weinig@apple.com's avatar
weinig@apple.com committed
488

489 490 491
    // NOTE: JSObject and its subclasses must be able to gracefully handle ExecState* = 0,
    // because this call may come from inside the compiler.
    JS_EXPORT_PRIVATE static JSObject* toThisObject(JSCell*, ExecState*);
weinig@apple.com's avatar
weinig@apple.com committed
492

493
    bool getPropertySpecificValue(ExecState*, PropertyName, JSCell*& specificFunction) const;
weinig@apple.com's avatar
weinig@apple.com committed
494

495 496 497 498
    // This get function only looks at the property map.
    JSValue getDirect(JSGlobalData& globalData, PropertyName propertyName) const
    {
        PropertyOffset offset = structure()->get(globalData, propertyName);
499
        checkOffset(offset, structure()->inlineCapacity());
500
        return offset != invalidOffset ? getDirect(offset) : JSValue();
501
    }
502

503
    PropertyOffset getDirectOffset(JSGlobalData& globalData, PropertyName propertyName)
504 505
    {
        PropertyOffset offset = structure()->get(globalData, propertyName);
506
        checkOffset(offset, structure()->inlineCapacity());
507
        return offset;
508
    }
weinig@apple.com's avatar
weinig@apple.com committed
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
    bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
    ConstPropertyStorage inlineStorageUnsafe() const
    {
        return bitwise_cast<ConstPropertyStorage>(this + 1);
    }
    PropertyStorage inlineStorageUnsafe()
    {
        return bitwise_cast<PropertyStorage>(this + 1);
    }
    ConstPropertyStorage inlineStorage() const
    {
        ASSERT(hasInlineStorage());
        return inlineStorageUnsafe();
    }
    PropertyStorage inlineStorage()
    {
        ASSERT(hasInlineStorage());
        return inlineStorageUnsafe();
    }
529
        
530 531
    const Butterfly* butterfly() const { return m_butterfly; }
    Butterfly* butterfly() { return m_butterfly; }
532
        
533 534
    ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
    PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
535

536 537 538 539 540 541
    const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
    {
        if (isInlineOffset(offset))
            return &inlineStorage()[offsetInInlineStorage(offset)];
        return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
    }
542

543 544 545 546 547 548
    WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset)
    {
        if (isInlineOffset(offset))
            return &inlineStorage()[offsetInInlineStorage(offset)];
        return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
    }
549

550 551 552 553 554 555 556 557 558 559 560 561 562 563
    void transitionTo(JSGlobalData&, Structure*);

    bool removeDirect(JSGlobalData&, PropertyName); // Return true if anything is removed.
    bool hasCustomProperties() { return structure()->didTransition(); }
    bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }

    // putOwnDataProperty has 'put' like semantics, however this method:
    //  - assumes the object contains no own getter/setter properties.
    //  - provides no special handling for __proto__
    //  - does not walk the prototype chain (to check for accessors or non-writable properties).
    // This is used by JSActivation.
    bool putOwnDataProperty(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);

    // Fast access to known property offsets.
564 565 566
    JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
    void putDirect(JSGlobalData& globalData, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(globalData, this, value); }
    void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586

    JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);

    bool isGlobalObject() const;
    bool isVariableObject() const;
    bool isNameScopeObject() const;
    bool isActivationObject() const;
    bool isErrorInstance() const;

    void seal(JSGlobalData&);
    void freeze(JSGlobalData&);
    JS_EXPORT_PRIVATE void preventExtensions(JSGlobalData&);
    bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); }
    bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
    bool isExtensible() { return structure()->isExtensible(); }
    bool indexingShouldBeSparse()
    {
        return !isExtensible()
            || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
    }
587

588 589
    bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
    void reifyStaticFunctionsForDelete(ExecState* exec);
590

591 592 593
    JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
    void setButterfly(JSGlobalData&, Butterfly*, Structure*);
    void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
594
        
595 596
    void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
    void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
597

598 599 600 601
    void flattenDictionaryObject(JSGlobalData& globalData)
    {
        structure()->flattenDictionaryStructure(globalData, this);
    }
602

603 604 605 606 607 608 609 610
    JSGlobalObject* globalObject() const
    {
        ASSERT(structure()->globalObject());
        ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
        return structure()->globalObject();
    }
        
    void switchToSlowPutArrayStorage(JSGlobalData&);
611
        
612 613 614 615 616 617 618 619 620
    // The receiver is the prototype in this case. The following:
    //
    // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...)
    //
    // is equivalent to:
    //
    // foo->attemptToInterceptPutByIndexOnHole(...);
    bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow);
        
621 622 623 624
    // Returns 0 if int32 storage cannot be created - either because
    // indexing should be sparse, we're having a bad time, or because
    // we already have a more general form of storage (double,
    // contiguous, array storage).
625
    ContiguousJSValues ensureInt32(JSGlobalData& globalData)
626 627 628 629 630 631 632 633 634 635 636
    {
        if (LIKELY(hasInt32(structure()->indexingType())))
            return m_butterfly->contiguousInt32();
            
        return ensureInt32Slow(globalData);
    }
        
    // Returns 0 if double storage cannot be created - either because
    // indexing should be sparse, we're having a bad time, or because
    // we already have a more general form of storage (contiguous,
    // or array storage).
637
    ContiguousDoubles ensureDouble(JSGlobalData& globalData)
638 639 640 641 642 643 644
    {
        if (LIKELY(hasDouble(structure()->indexingType())))
            return m_butterfly->contiguousDouble();
            
        return ensureDoubleSlow(globalData);
    }
        
645 646
    // Returns 0 if contiguous storage cannot be created - either because
    // indexing should be sparse or because we're having a bad time.
647
    ContiguousJSValues ensureContiguous(JSGlobalData& globalData)
648 649 650
    {
        if (LIKELY(hasContiguous(structure()->indexingType())))
            return m_butterfly->contiguous();
651
            
652 653
        return ensureContiguousSlow(globalData);
    }
654
        
655 656 657
    // Same as ensureContiguous(), except that if the indexed storage is in
    // double mode, then it does a rage conversion to contiguous: it
    // attempts to convert each double to an int32.
658
    ContiguousJSValues rageEnsureContiguous(JSGlobalData& globalData)
659 660 661 662 663 664 665
    {
        if (LIKELY(hasContiguous(structure()->indexingType())))
            return m_butterfly->contiguous();
            
        return rageEnsureContiguousSlow(globalData);
    }
        
666 667 668 669 670 671 672 673
    // Ensure that the object is in a mode where it has array storage. Use
    // this if you're about to perform actions that would have required the
    // object to be converted to have array storage, if it didn't have it
    // already.
    ArrayStorage* ensureArrayStorage(JSGlobalData& globalData)
    {
        if (LIKELY(hasArrayStorage(structure()->indexingType())))
            return m_butterfly->arrayStorage();
674
            
675 676
        return ensureArrayStorageSlow(globalData);
    }
677
        
678
    static size_t offsetOfInlineStorage();
679
        
680 681 682 683
    static ptrdiff_t butterflyOffset()
    {
        return OBJECT_OFFSETOF(JSObject, m_butterfly);
    }
684
        
685 686 687 688
    void* butterflyAddress()
    {
        return &m_butterfly;
    }
689

690
    static JS_EXPORTDATA const ClassInfo s_info;
691

692 693 694 695 696 697 698 699 700 701 702
protected:
    void finishCreation(JSGlobalData& globalData)
    {
        Base::finishCreation(globalData);
        ASSERT(inherits(&s_info));
        ASSERT(!structure()->outOfLineCapacity());
        ASSERT(structure()->isEmpty());
        ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
        ASSERT(structure()->isObject());
        ASSERT(classInfo());
    }
mhahnenberg@apple.com's avatar
mhahnenberg@apple.com committed
703

704 705 706 707 708 709 710 711
    static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
    {
        return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
    }

    // To instantiate objects you likely want JSFinalObject, below.
    // To create derived types you likely want JSNonFinalObject, below.
    JSObject(JSGlobalData&, Structure*, Butterfly* = 0);
712
        
713 714
    void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize);
    void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize);
715

716 717 718 719 720 721 722
    // Call this if you know that the object is in a mode where it has array
    // storage. This will assert otherwise.
    ArrayStorage* arrayStorage()
    {
        ASSERT(hasArrayStorage(structure()->indexingType()));
        return m_butterfly->arrayStorage();
    }
723
        
724 725 726 727 728 729 730
    // Call this if you want to predicate some actions on whether or not the
    // object is in a mode where it has array storage.
    ArrayStorage* arrayStorageOrNull()
    {
        switch (structure()->indexingType()) {
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return m_butterfly->arrayStorage();
731
                
732 733
        default:
            return 0;
734
        }
735
    }
736 737
        
    Butterfly* createInitialUndecided(JSGlobalData&, unsigned length);
738 739 740
    ContiguousJSValues createInitialInt32(JSGlobalData&, unsigned length);
    ContiguousDoubles createInitialDouble(JSGlobalData&, unsigned length);
    ContiguousJSValues createInitialContiguous(JSGlobalData&, unsigned length);
741 742 743 744
        
    void convertUndecidedForValue(JSGlobalData&, JSValue);
    void convertInt32ForValue(JSGlobalData&, JSValue);
        
745 746
    ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength);
    ArrayStorage* createInitialArrayStorage(JSGlobalData&);
747
        
748 749 750
    ContiguousJSValues convertUndecidedToInt32(JSGlobalData&);
    ContiguousDoubles convertUndecidedToDouble(JSGlobalData&);
    ContiguousJSValues convertUndecidedToContiguous(JSGlobalData&);
751 752 753 754
    ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&, NonPropertyTransition);
    ArrayStorage* convertUndecidedToArrayStorage(JSGlobalData&);
        
755 756
    ContiguousDoubles convertInt32ToDouble(JSGlobalData&);
    ContiguousJSValues convertInt32ToContiguous(JSGlobalData&);
757 758 759
    ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&, NonPropertyTransition);
    ArrayStorage* convertInt32ToArrayStorage(JSGlobalData&);
760
    
761 762
    ContiguousJSValues convertDoubleToContiguous(JSGlobalData&);
    ContiguousJSValues rageConvertDoubleToContiguous(JSGlobalData&);
763 764 765 766
    ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&, NonPropertyTransition);
    ArrayStorage* convertDoubleToArrayStorage(JSGlobalData&);
        
767 768 769
    ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition);
    ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&);
770

771
        
772
    ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&);
773
        
774
    bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
775

776 777
    template<IndexingType indexingShape>
    void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue);
778
    void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
779

780 781 782 783
    bool increaseVectorLength(JSGlobalData&, unsigned newLength);
    void deallocateSparseIndexMap();
    bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
    SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&);
784
        
785
    void notifyPresenceOfIndexedAccessors(JSGlobalData&);
786
        
787
    bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow);
788
        
789 790
    // Call this if you want setIndexQuickly to succeed and you're sure that
    // the array is contiguous.
791
    void ensureLength(JSGlobalData& globalData, unsigned length)
792 793
    {
        ASSERT(length < MAX_ARRAY_INDEX);
794
        ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType()));
795
            
796
        if (m_butterfly->vectorLength() < length)
797
            ensureLengthSlow(globalData, length);
798
            
799 800 801
        if (m_butterfly->publicLength() < length)
            m_butterfly->setPublicLength(length);
    }
802
        
803 804
    template<IndexingType indexingShape>
    unsigned countElements(Butterfly*);
805
        
806 807 808 809 810 811
    // This is relevant to undecided, int32, double, and contiguous.
    unsigned countElements();
        
    // This strange method returns a pointer to the start of the indexed data
    // as if it contained JSValues. But it won't always contain JSValues.
    // Make sure you cast this to the appropriate type before using.
812
    template<IndexingType indexingType>
813
    ContiguousJSValues indexingData()
814 815
    {
        switch (indexingType) {
816 817
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
818 819
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->contiguous();
820
                
821
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
822 823
            return m_butterfly->arrayStorage()->vector();

824 825
        default:
            CRASH();
826
            return ContiguousJSValues();
827
        }
828
    }
829

830
    ContiguousJSValues currentIndexingData()
831 832
    {
        switch (structure()->indexingType()) {
833
        case ALL_INT32_INDEXING_TYPES:
834 835
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->contiguous();
836

837
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
838
            return m_butterfly->arrayStorage()->vector();
839

840 841
        default:
            CRASH();
842
            return ContiguousJSValues();
843
        }
844
    }
845
        
846 847
    JSValue getHolyIndexQuickly(unsigned i)
    {
848
        ASSERT(i < m_butterfly->vectorLength());
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        switch (structure()->indexingType()) {
        case ALL_INT32_INDEXING_TYPES:
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->contiguous()[i].get();
        case ALL_DOUBLE_INDEXING_TYPES: {
            double value = m_butterfly->contiguousDouble()[i];
            if (value == value)
                return JSValue(JSValue::EncodeAsDouble, value);
            return JSValue();
        }
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return m_butterfly->arrayStorage()->m_vector[i].get();
        default:
            CRASH();
            return JSValue();
        }
    }
        
867 868 869 870
    template<IndexingType indexingType>
    unsigned relevantLength()
    {
        switch (indexingType) {
871 872
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
873 874
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->publicLength();
875
                
876 877 878 879
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return std::min(
                m_butterfly->arrayStorage()->length(),
                m_butterfly->arrayStorage()->vectorLength());
880
                
881 882 883
        default:
            CRASH();
            return 0;
884
        }
885
    }
886

887 888 889
    unsigned currentRelevantLength()
    {
        switch (structure()->indexingType()) {
890 891
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
892 893
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->publicLength();
894

895 896 897 898
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return std::min(
                m_butterfly->arrayStorage()->length(),
                m_butterfly->arrayStorage()->vectorLength());
899

900 901 902
        default:
            CRASH();
            return 0;
903
        }
904
    }
905

906 907
private:
    friend class LLIntOffsetsExtractor;
908
        
909 910 911 912 913 914 915
    // Nobody should ever ask any of these questions on something already known to be a JSObject.
    using JSCell::isAPIValueWrapper;
    using JSCell::isGetterSetter;
    void getObject();
    void getString(ExecState* exec);
    void isObject();
    void isString();
916
        
917 918
    Butterfly* createInitialIndexedStorage(JSGlobalData&, unsigned length, size_t elementSize);
        
919
    ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*);
920
        
921 922
    template<PutMode>
    bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
923

924 925
    bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&);
    JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, PropertyOffset);
darin@apple.com's avatar
darin@apple.com committed
926

927
    const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
928
        
929
    void putIndexedDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
930
        
931 932 933
    void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
    bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
    JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode);
934
        
935 936
    unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength);
    unsigned getNewVectorLength(unsigned desiredLength);
weinig@apple.com's avatar
weinig@apple.com committed
937

938
    JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&);
939
        
940 941 942 943 944 945 946
    ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(JSGlobalData&, unsigned neededLength);
        
    JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(JSGlobalData&, unsigned index, JSValue);
    JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue);
    JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(JSGlobalData&, unsigned index, JSValue);
        
    void ensureLengthSlow(JSGlobalData&, unsigned length);
947
        
948 949 950 951
    ContiguousJSValues ensureInt32Slow(JSGlobalData&);
    ContiguousDoubles ensureDoubleSlow(JSGlobalData&);