JSObject.h 56.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, 2013 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"
ggaren@apple.com's avatar
ggaren@apple.com committed
40
#include "VM.h"
41
#include "JSString.h"
42
#include "SparseArrayValueMap.h"
43
#include <wtf/StdLibExtras.h>
44

45
namespace JSC {
46

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

54
JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*);
55

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
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
107 108
    };

109 110 111 112 113 114 115
public:
    typedef JSCell Base;
        
    static size_t allocationSize(size_t inlineCapacity)
    {
        return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>);
    }
116
        
117 118
    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
119

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

122
    JSValue prototype() const;
ggaren@apple.com's avatar
ggaren@apple.com committed
123
    void setPrototype(VM&, JSValue prototype);
124
    bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype);
weinig@apple.com's avatar
weinig@apple.com committed
125
        
126 127 128 129
    bool mayInterceptIndexedAccesses()
    {
        return structure()->mayInterceptIndexedAccesses();
    }
130
        
131 132 133
    JSValue get(ExecState*, PropertyName) const;
    JSValue get(ExecState*, unsigned propertyName) const;

134
    bool fastGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&);
135 136 137 138
    bool getPropertySlot(ExecState*, PropertyName, PropertySlot&);
    bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
    JS_EXPORT_PRIVATE bool getPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);

139 140
    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
    JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
141 142 143 144 145 146
    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
    void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
    {
        if (canSetIndexQuickly(propertyName)) {
ggaren@apple.com's avatar
ggaren@apple.com committed
165
            setIndexQuickly(exec->vm(), propertyName, value);
166
            return;
167
        }
168 169
        methodTable()->putByIndex(this, exec, propertyName, value, shouldThrow);
    }
170
        
171 172 173 174 175 176 177 178
    // 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)) {
ggaren@apple.com's avatar
ggaren@apple.com committed
179
            setIndexQuickly(exec->vm(), propertyName, value);
180
            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
        
ggaren@apple.com's avatar
ggaren@apple.com committed
319
    void setIndexQuickly(VM& vm, unsigned i, JSValue v)
320 321
    {
        switch (structure()->indexingType()) {
322 323 324
        case ALL_INT32_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isInt32()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
325
                convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
326 327 328 329
                return;
            }
            // Fall through to contiguous case.
        }
330 331
        case ALL_CONTIGUOUS_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
ggaren@apple.com's avatar
ggaren@apple.com committed
332
            m_butterfly->contiguous()[i].set(vm, this, v);
333 334 335 336
            if (i >= m_butterfly->publicLength())
                m_butterfly->setPublicLength(i + 1);
            break;
        }
337 338 339
        case ALL_DOUBLE_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isNumber()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
340
                convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
341 342 343 344
                return;
            }
            double value = v.asNumber();
            if (value != value) {
ggaren@apple.com's avatar
ggaren@apple.com committed
345
                convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
346 347 348 349 350 351 352
                return;
            }
            m_butterfly->contiguousDouble()[i] = value;
            if (i >= m_butterfly->publicLength())
                m_butterfly->setPublicLength(i + 1);
            break;
        }
353 354 355 356
        case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
            ArrayStorage* storage = m_butterfly->arrayStorage();
            WriteBarrier<Unknown>& x = storage->m_vector[i];
            JSValue old = x.get();
ggaren@apple.com's avatar
ggaren@apple.com committed
357
            x.set(vm, this, v);
358 359 360 361
            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
        
ggaren@apple.com's avatar
ggaren@apple.com committed
370
    void initializeIndex(VM& vm, unsigned i, JSValue v)
371 372
    {
        switch (structure()->indexingType()) {
373
        case ALL_UNDECIDED_INDEXING_TYPES: {
ggaren@apple.com's avatar
ggaren@apple.com committed
374
            setIndexQuicklyToUndecided(vm, i, v);
375 376 377 378 379 380
            break;
        }
        case ALL_INT32_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isInt32()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
381
                convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
382 383 384 385
                break;
            }
            // Fall through.
        }
386 387 388
        case ALL_CONTIGUOUS_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
ggaren@apple.com's avatar
ggaren@apple.com committed
389
            m_butterfly->contiguous()[i].set(vm, this, v);
390 391
            break;
        }
392 393 394 395
        case ALL_DOUBLE_INDEXING_TYPES: {
            ASSERT(i < m_butterfly->publicLength());
            ASSERT(i < m_butterfly->vectorLength());
            if (!v.isNumber()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
396
                convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
397 398 399 400
                return;
            }
            double value = v.asNumber();
            if (value != value) {
ggaren@apple.com's avatar
ggaren@apple.com committed
401
                convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
402 403 404 405 406
                return;
            }
            m_butterfly->contiguousDouble()[i] = value;
            break;
        }
407 408 409 410
        case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
            ArrayStorage* storage = m_butterfly->arrayStorage();
            ASSERT(i < storage->length());
            ASSERT(i < storage->m_numValuesInVector);
ggaren@apple.com's avatar
ggaren@apple.com committed
411
            storage->m_vector[i].set(vm, this, v);
412 413 414
            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
        
ggaren@apple.com's avatar
ggaren@apple.com committed
453
    void enterDictionaryIndexingMode(VM&);
weinig@apple.com's avatar
weinig@apple.com committed
454

455 456 457 458 459 460
    // 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);
ggaren@apple.com's avatar
ggaren@apple.com committed
461 462 463
    void putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0);
    void putDirect(VM&, PropertyName, JSValue, PutPropertySlot&);
    void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0);
464
    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
    JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode);
weinig@apple.com's avatar
weinig@apple.com committed
490

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

493
    // This get function only looks at the property map.
ggaren@apple.com's avatar
ggaren@apple.com committed
494
    JSValue getDirect(VM& vm, PropertyName propertyName) const
495
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
496
        PropertyOffset offset = structure()->get(vm, propertyName);
497
        checkOffset(offset, structure()->inlineCapacity());
498
        return offset != invalidOffset ? getDirect(offset) : JSValue();
499
    }
500

ggaren@apple.com's avatar
ggaren@apple.com committed
501
    PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName)
502
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
503
        PropertyOffset offset = structure()->get(vm, propertyName);
504
        checkOffset(offset, structure()->inlineCapacity());
505
        return offset;
506
    }
weinig@apple.com's avatar
weinig@apple.com committed
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
    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();
    }
527
        
528 529
    const Butterfly* butterfly() const { return m_butterfly; }
    Butterfly* butterfly() { return m_butterfly; }
530
        
531 532
    ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
    PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
533

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

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

ggaren@apple.com's avatar
ggaren@apple.com committed
548
    void transitionTo(VM&, Structure*);
549

ggaren@apple.com's avatar
ggaren@apple.com committed
550
    bool removeDirect(VM&, PropertyName); // Return true if anything is removed.
551 552 553 554 555 556 557 558
    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.
ggaren@apple.com's avatar
ggaren@apple.com committed
559
    bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&);
560 561

    // Fast access to known property offsets.
562
    JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
ggaren@apple.com's avatar
ggaren@apple.com committed
563
    void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
564
    void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
565

566
    void putDirectNativeFunction(ExecState*, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
567
    void putDirectNativeFunctionWithoutTransition(ExecState*, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
568

569 570 571 572
    JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);

    bool isGlobalObject() const;
    bool isVariableObject() const;
573
    bool isStaticScopeObject() const;
574 575 576 577
    bool isNameScopeObject() const;
    bool isActivationObject() const;
    bool isErrorInstance() const;

ggaren@apple.com's avatar
ggaren@apple.com committed
578 579 580 581 582
    void seal(VM&);
    void freeze(VM&);
    JS_EXPORT_PRIVATE void preventExtensions(VM&);
    bool isSealed(VM& vm) { return structure()->isSealed(vm); }
    bool isFrozen(VM& vm) { return structure()->isFrozen(vm); }
583 584 585 586 587 588
    bool isExtensible() { return structure()->isExtensible(); }
    bool indexingShouldBeSparse()
    {
        return !isExtensible()
            || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
    }
589

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

ggaren@apple.com's avatar
ggaren@apple.com committed
593 594
    JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize);
    void setButterfly(VM&, Butterfly*, Structure*);
595
    void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
596
        
ggaren@apple.com's avatar
ggaren@apple.com committed
597 598
    void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*);
    void setStructureAndReallocateStorageIfNecessary(VM&, Structure*);
599

ggaren@apple.com's avatar
ggaren@apple.com committed
600
    void flattenDictionaryObject(VM& vm)
601
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
602
        structure()->flattenDictionaryStructure(vm, this);
603
    }
604

605 606 607 608 609 610 611
    JSGlobalObject* globalObject() const
    {
        ASSERT(structure()->globalObject());
        ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
        return structure()->globalObject();
    }
        
ggaren@apple.com's avatar
ggaren@apple.com committed
612
    void switchToSlowPutArrayStorage(VM&);
613
        
614 615 616 617 618 619 620 621 622
    // 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);
        
623 624 625 626
    // 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).
ggaren@apple.com's avatar
ggaren@apple.com committed
627
    ContiguousJSValues ensureInt32(VM& vm)
628 629 630 631
    {
        if (LIKELY(hasInt32(structure()->indexingType())))
            return m_butterfly->contiguousInt32();
            
ggaren@apple.com's avatar
ggaren@apple.com committed
632
        return ensureInt32Slow(vm);
633 634 635 636 637 638
    }
        
    // 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).
ggaren@apple.com's avatar
ggaren@apple.com committed
639
    ContiguousDoubles ensureDouble(VM& vm)
640 641 642 643
    {
        if (LIKELY(hasDouble(structure()->indexingType())))
            return m_butterfly->contiguousDouble();
            
ggaren@apple.com's avatar
ggaren@apple.com committed
644
        return ensureDoubleSlow(vm);
645 646
    }
        
647 648
    // Returns 0 if contiguous storage cannot be created - either because
    // indexing should be sparse or because we're having a bad time.
ggaren@apple.com's avatar
ggaren@apple.com committed
649
    ContiguousJSValues ensureContiguous(VM& vm)
650 651 652
    {
        if (LIKELY(hasContiguous(structure()->indexingType())))
            return m_butterfly->contiguous();
653
            
ggaren@apple.com's avatar
ggaren@apple.com committed
654
        return ensureContiguousSlow(vm);
655
    }
656
        
657 658 659
    // 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.
ggaren@apple.com's avatar
ggaren@apple.com committed
660
    ContiguousJSValues rageEnsureContiguous(VM& vm)
661 662 663 664
    {
        if (LIKELY(hasContiguous(structure()->indexingType())))
            return m_butterfly->contiguous();
            
ggaren@apple.com's avatar
ggaren@apple.com committed
665
        return rageEnsureContiguousSlow(vm);
666 667
    }
        
668 669 670 671
    // 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.
ggaren@apple.com's avatar
ggaren@apple.com committed
672
    ArrayStorage* ensureArrayStorage(VM& vm)
673 674 675
    {
        if (LIKELY(hasArrayStorage(structure()->indexingType())))
            return m_butterfly->arrayStorage();
676
            
ggaren@apple.com's avatar
ggaren@apple.com committed
677
        return ensureArrayStorageSlow(vm);
678
    }
679
        
680
    static size_t offsetOfInlineStorage();
681
        
682 683 684 685
    static ptrdiff_t butterflyOffset()
    {
        return OBJECT_OFFSETOF(JSObject, m_butterfly);
    }
686
        
687 688 689 690
    void* butterflyAddress()
    {
        return &m_butterfly;
    }
691

692
    static JS_EXPORTDATA const ClassInfo s_info;
693

694
protected:
ggaren@apple.com's avatar
ggaren@apple.com committed
695
    void finishCreation(VM& vm)
696
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
697
        Base::finishCreation(vm);
698 699 700 701 702 703 704
        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
705

ggaren@apple.com's avatar
ggaren@apple.com committed
706
    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
707
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
708
        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
709 710 711 712
    }

    // To instantiate objects you likely want JSFinalObject, below.
    // To create derived types you likely want JSNonFinalObject, below.
ggaren@apple.com's avatar
ggaren@apple.com committed
713
    JSObject(VM&, Structure*, Butterfly* = 0);
714
        
715 716
    void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize);
    void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize);
717

718 719 720 721 722 723 724
    // 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();
    }
725
        
726 727 728 729 730 731 732
    // 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();
733
                
734 735
        default:
            return 0;
736
        }
737
    }
738
        
ggaren@apple.com's avatar
ggaren@apple.com committed
739 740 741 742
    Butterfly* createInitialUndecided(VM&, unsigned length);
    ContiguousJSValues createInitialInt32(VM&, unsigned length);
    ContiguousDoubles createInitialDouble(VM&, unsigned length);
    ContiguousJSValues createInitialContiguous(VM&, unsigned length);
743
        
ggaren@apple.com's avatar
ggaren@apple.com committed
744 745
    void convertUndecidedForValue(VM&, JSValue);
    void convertInt32ForValue(VM&, JSValue);
746
        
ggaren@apple.com's avatar
ggaren@apple.com committed
747 748
    ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength);
    ArrayStorage* createInitialArrayStorage(VM&);
749
        
ggaren@apple.com's avatar
ggaren@apple.com committed
750 751 752 753 754 755
    ContiguousJSValues convertUndecidedToInt32(VM&);
    ContiguousDoubles convertUndecidedToDouble(VM&);
    ContiguousJSValues convertUndecidedToContiguous(VM&);
    ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition);
    ArrayStorage* convertUndecidedToArrayStorage(VM&);
756
        
ggaren@apple.com's avatar
ggaren@apple.com committed
757 758 759 760 761
    ContiguousDoubles convertInt32ToDouble(VM&);
    ContiguousJSValues convertInt32ToContiguous(VM&);
    ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition);
    ArrayStorage* convertInt32ToArrayStorage(VM&);
762
    
ggaren@apple.com's avatar
ggaren@apple.com committed
763 764 765 766 767
    ContiguousJSValues convertDoubleToContiguous(VM&);
    ContiguousJSValues rageConvertDoubleToContiguous(VM&);
    ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition);
    ArrayStorage* convertDoubleToArrayStorage(VM&);
768
        
ggaren@apple.com's avatar
ggaren@apple.com committed
769 770 771
    ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength);
    ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition);
    ArrayStorage* convertContiguousToArrayStorage(VM&);
772

773
        
ggaren@apple.com's avatar
ggaren@apple.com committed
774
    ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&);
775
        
776
    bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
777

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

ggaren@apple.com's avatar
ggaren@apple.com committed
782
    bool increaseVectorLength(VM&, unsigned newLength);
783 784
    void deallocateSparseIndexMap();
    bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
ggaren@apple.com's avatar
ggaren@apple.com committed
785
    SparseArrayValueMap* allocateSparseIndexMap(VM&);
786
        
ggaren@apple.com's avatar
ggaren@apple.com committed
787
    void notifyPresenceOfIndexedAccessors(VM&);
788
        
789
    bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow);
790
        
791 792
    // Call this if you want setIndexQuickly to succeed and you're sure that
    // the array is contiguous.
ggaren@apple.com's avatar
ggaren@apple.com committed
793
    void ensureLength(VM& vm, unsigned length)
794 795
    {
        ASSERT(length < MAX_ARRAY_INDEX);
796
        ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType()));
797
            
798
        if (m_butterfly->vectorLength() < length)
ggaren@apple.com's avatar
ggaren@apple.com committed
799
            ensureLengthSlow(vm, length);
800
            
801 802 803
        if (m_butterfly->publicLength() < length)
            m_butterfly->setPublicLength(length);
    }
804
        
805 806
    template<IndexingType indexingShape>
    unsigned countElements(Butterfly*);
807
        
808 809 810 811 812 813
    // 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.
814
    template<IndexingType indexingType>
815
    ContiguousJSValues indexingData()
816 817
    {
        switch (indexingType) {
818 819
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
820 821
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->contiguous();
822
                
823
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
824 825
            return m_butterfly->arrayStorage()->vector();

826 827
        default:
            CRASH();
828
            return ContiguousJSValues();
829
        }
830
    }
831

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

839
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
840
            return m_butterfly->arrayStorage()->vector();
841

842 843
        default:
            CRASH();
844
            return ContiguousJSValues();
845
        }
846
    }
847
        
848 849
    JSValue getHolyIndexQuickly(unsigned i)
    {
850
        ASSERT(i < m_butterfly->vectorLength());
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
        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();
        }
    }
        
869 870 871 872
    template<IndexingType indexingType>
    unsigned relevantLength()
    {
        switch (indexingType) {
873 874
        case ALL_INT32_INDEXING_TYPES:
        case ALL_DOUBLE_INDEXING_TYPES:
875 876
        case ALL_CONTIGUOUS_INDEXING_TYPES:
            return m_butterfly->publicLength();
877
                
878 879 880 881
        case ALL_ARRAY_STORAGE_INDEXING_TYPES:
            return std::min(
                m_butterfly->arrayStorage()->length(),
                m_butterfly->arrayStorage()->vectorLength());
882
                
883 884 885
        default:
            CRASH();
            return 0;
886
        }
887
    }
888

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

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

902 903 904
        default:
            CRASH();
            return 0;
905
        }
906
    }
907

908 909
private:
    friend class LLIntOffsetsExtractor;
910
        
911 912 913 914 915 916 917
    // 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();
918
        
ggaren@apple.com's avatar
ggaren@apple.com committed
919
    Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize);
920
        
ggaren@apple.com's avatar
ggaren@apple.com committed
921
    ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*);
922
        
923
    template<PutMode>
ggaren@apple.com's avatar
ggaren@apple.com committed
924
    bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
925

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

929
    const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
930
        
931
    void putIndexedDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
932