JSObject.cpp 99.1 KB
Newer Older
1
/*
2 3
 *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
 *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4
 *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
5
 *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 *  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
19
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
 *  Boston, MA 02110-1301, USA.
21
 *
22 23
 */

mjs's avatar
mjs committed
24
#include "config.h"
darin@apple.com's avatar
darin@apple.com committed
25
#include "JSObject.h"
darin's avatar
darin committed
26

27 28
#include "ButterflyInlines.h"
#include "CopiedSpaceInlines.h"
29
#include "CopyVisitor.h"
30
#include "CopyVisitorInlines.h"
31
#include "DatePrototype.h"
32
#include "ErrorConstructor.h"
33
#include "Executable.h"
weinig@apple.com's avatar
weinig@apple.com committed
34
#include "GetterSetter.h"
35
#include "IndexingHeaderInlines.h"
36
#include "JSFunction.h"
37
#include "JSGlobalObject.h"
38
#include "Lookup.h"
39
#include "NativeErrorConstructor.h"
40
#include "Nodes.h"
41
#include "ObjectPrototype.h"
42
#include "Operations.h"
43
#include "PropertyDescriptor.h"
44
#include "PropertyNameArray.h"
45
#include "Reject.h"
46
#include "SlotVisitorInlines.h"
47
#include <math.h>
ggaren's avatar
ggaren committed
48
#include <wtf/Assertions.h>
49

50
namespace JSC {
51

52 53
// We keep track of the size of the last array after it was grown. We use this
// as a simple heuristic for as the value to grow the next array from size 0.
54 55
// This value is capped by the constant FIRST_VECTOR_GROW defined in
// ArrayConventions.h.
56 57
static unsigned lastArraySize = 0;

58 59 60 61 62
JSCell* getCallableObjectSlow(JSCell* cell)
{
    Structure* structure = cell->structure();
    if (structure->typeInfo().type() == JSFunctionType)
        return cell;
63
    if (structure->classInfo()->isSubClassOf(InternalFunction::info()))
64 65 66 67
        return cell;
    return 0;
}

68 69
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject);
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject);
70

71 72
const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";

73
const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) };
74

75 76
const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) };

77
static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify)
78 79 80 81 82 83 84 85 86 87 88 89
{
    // Add properties from the static hashtables of properties
    for (; classInfo; classInfo = classInfo->parentClass) {
        const HashTable* table = classInfo->propHashTable(exec);
        if (!table)
            continue;
        table->initializeIfNeeded(exec);
        ASSERT(table->table);

        int hashSizeMask = table->compactSize - 1;
        const HashEntry* entry = table->table;
        for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
90
            if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)) && !((entry->attributes() & Function) && didReify))
91 92 93 94 95
                propertyNames.add(entry->key());
        }
    }
}

96
ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butterfly, size_t storageSize)
97
{
98 99 100 101 102 103 104
    ASSERT(butterfly);
    
    Structure* structure = this->structure();
    
    size_t propertyCapacity = structure->outOfLineCapacity();
    size_t preCapacity;
    size_t indexingPayloadSizeInBytes;
105
    bool hasIndexingHeader = this->hasIndexingHeader();
106 107 108 109 110 111 112
    if (UNLIKELY(hasIndexingHeader)) {
        preCapacity = butterfly->indexingHeader()->preCapacity(structure);
        indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
    } else {
        preCapacity = 0;
        indexingPayloadSizeInBytes = 0;
    }
113
    size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
114
    if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity))) {
115 116
        Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);
        
117
        // Copy the properties.
118 119
        PropertyStorage currentTarget = newButterfly->propertyStorage();
        PropertyStorage currentSource = butterfly->propertyStorage();
120 121
        for (size_t count = storageSize; count--;)
            (--currentTarget)->setWithoutWriteBarrier((--currentSource)->get());
122 123 124 125
        
        if (UNLIKELY(hasIndexingHeader)) {
            *newButterfly->indexingHeader() = *butterfly->indexingHeader();
            
126
            // Copy the array if appropriate.
127 128 129 130 131
            
            WriteBarrier<Unknown>* currentTarget;
            WriteBarrier<Unknown>* currentSource;
            size_t count;
            
132
            switch (structure->indexingType()) {
133
            case ALL_UNDECIDED_INDEXING_TYPES:
134 135 136
            case ALL_CONTIGUOUS_INDEXING_TYPES:
            case ALL_INT32_INDEXING_TYPES:
            case ALL_DOUBLE_INDEXING_TYPES: {
137 138
                currentTarget = newButterfly->contiguous().data();
                currentSource = butterfly->contiguous().data();
139
                RELEASE_ASSERT(newButterfly->publicLength() <= newButterfly->vectorLength());
140 141 142 143
                count = newButterfly->vectorLength();
                break;
            }
                
144
            case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
145
                newButterfly->arrayStorage()->copyHeaderFromDuringGC(*butterfly->arrayStorage());
146 147 148
                currentTarget = newButterfly->arrayStorage()->m_vector;
                currentSource = butterfly->arrayStorage()->m_vector;
                count = newButterfly->arrayStorage()->vectorLength();
149 150
                break;
            }
151
                
152
            default:
153 154 155
                currentTarget = 0;
                currentSource = 0;
                count = 0;
156 157
                break;
            }
158

159
            memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue));
160 161 162
        }
        
        m_butterfly = newButterfly;
163 164 165 166 167 168 169 170 171 172 173 174 175
        visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
    } 
}

ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* butterfly, size_t storageSize)
{
    ASSERT(butterfly);
    
    Structure* structure = this->structure();
    
    size_t propertyCapacity = structure->outOfLineCapacity();
    size_t preCapacity;
    size_t indexingPayloadSizeInBytes;
176
    bool hasIndexingHeader = this->hasIndexingHeader();
177 178 179
    if (UNLIKELY(hasIndexingHeader)) {
        preCapacity = butterfly->indexingHeader()->preCapacity(structure);
        indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
180
    } else {
181 182 183 184 185 186 187
        preCapacity = 0;
        indexingPayloadSizeInBytes = 0;
    }
    size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes);

    // Mark the properties.
    visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize);
188 189 190
    visitor.copyLater(
        this, ButterflyCopyToken,
        butterfly->base(preCapacity, propertyCapacity), capacityInBytes);
191 192 193 194
    
    // Mark the array if appropriate.
    switch (structure->indexingType()) {
    case ALL_CONTIGUOUS_INDEXING_TYPES:
195
        visitor.appendValues(butterfly->contiguous().data(), butterfly->publicLength());
196 197 198 199 200 201 202 203
        break;
    case ALL_ARRAY_STORAGE_INDEXING_TYPES:
        visitor.appendValues(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
        if (butterfly->arrayStorage()->m_sparseMap)
            visitor.append(&butterfly->arrayStorage()->m_sparseMap);
        break;
    default:
        break;
204
    }
205 206
}

207 208
void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
209
    JSObject* thisObject = jsCast<JSObject*>(cell);
210
    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
211
#if !ASSERT_DISABLED
212 213
    bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
    visitor.m_isCheckingForDefaultMarkViolation = false;
214
#endif
215 216 217
    
    JSCell::visitChildren(thisObject, visitor);

218 219
    Butterfly* butterfly = thisObject->butterfly();
    if (butterfly)
220
        thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize());
221 222 223 224 225

#if !ASSERT_DISABLED
    visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
#endif
}
226

227
void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token)
228 229
{
    JSObject* thisObject = jsCast<JSObject*>(cell);
230
    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
231
    
232 233 234
    if (token != ButterflyCopyToken)
        return;
    
235 236 237 238 239
    Butterfly* butterfly = thisObject->butterfly();
    if (butterfly)
        thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize());
}

240 241 242
void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
    JSFinalObject* thisObject = jsCast<JSFinalObject*>(cell);
243
    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
244 245 246 247 248
#if !ASSERT_DISABLED
    bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
    visitor.m_isCheckingForDefaultMarkViolation = false;
#endif
    
ggaren@apple.com's avatar
ggaren@apple.com committed
249 250
    JSCell::visitChildren(thisObject, visitor);

251 252
    Butterfly* butterfly = thisObject->butterfly();
    if (butterfly)
253
        thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize());
254

255
    size_t storageSize = thisObject->structure()->inlineSize();
256 257
    visitor.appendValues(thisObject->inlineStorage(), storageSize);

258
#if !ASSERT_DISABLED
259
    visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
260
#endif
261 262
}

263
String JSObject::className(const JSObject* object)
264
{
265
    const ClassInfo* info = object->classInfo();
266 267
    ASSERT(info);
    return info->className;
268 269
}

270
bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot)
271
{
272 273 274 275 276 277 278 279
    // NB. The fact that we're directly consulting our indexed storage implies that it is not
    // legal for anyone to override getOwnPropertySlot() without also overriding
    // getOwnPropertySlotByIndex().
    
    if (i > MAX_ARRAY_INDEX)
        return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot);
    
    switch (thisObject->structure()->indexingType()) {
280
    case ALL_BLANK_INDEXING_TYPES:
281
    case ALL_UNDECIDED_INDEXING_TYPES:
282 283
        break;
        
284
    case ALL_INT32_INDEXING_TYPES:
285 286 287 288 289 290 291
    case ALL_CONTIGUOUS_INDEXING_TYPES: {
        Butterfly* butterfly = thisObject->m_butterfly;
        if (i >= butterfly->vectorLength())
            return false;
        
        JSValue value = butterfly->contiguous()[i].get();
        if (value) {
292
            slot.setValue(thisObject, None, value);
293 294 295 296 297 298
            return true;
        }
        
        return false;
    }
        
299 300 301 302 303 304 305
    case ALL_DOUBLE_INDEXING_TYPES: {
        Butterfly* butterfly = thisObject->m_butterfly;
        if (i >= butterfly->vectorLength())
            return false;
        
        double value = butterfly->contiguousDouble()[i];
        if (value == value) {
306
            slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value));
307 308 309 310 311 312
            return true;
        }
        
        return false;
    }
        
313
    case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
314 315 316 317 318 319 320
        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
        if (i >= storage->length())
            return false;
        
        if (i < storage->vectorLength()) {
            JSValue value = storage->m_vector[i].get();
            if (value) {
321
                slot.setValue(thisObject, None, value);
322 323 324 325 326
                return true;
            }
        } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
            SparseArrayValueMap::iterator it = map->find(i);
            if (it != map->notFound()) {
327
                it->value.get(thisObject, slot);
328 329 330 331 332 333 334
                return true;
            }
        }
        break;
    }
        
    default:
335
        RELEASE_ASSERT_NOT_REACHED();
336 337 338 339
        break;
    }
    
    return false;
mjs's avatar
mjs committed
340 341
}

342
// ECMA 8.6.2.2
343
void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
344
{
345
    JSObject* thisObject = jsCast<JSObject*>(cell);
weinig@apple.com's avatar
weinig@apple.com committed
346
    ASSERT(value);
347
    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
ggaren@apple.com's avatar
ggaren@apple.com committed
348
    VM& vm = exec->vm();
349 350 351 352 353 354 355 356
    
    // Try indexed put first. This is required for correctness, since loads on property names that appear like
    // valid indices will never look in the named property storage.
    unsigned i = propertyName.asIndex();
    if (i != PropertyName::NotAnIndex) {
        putByIndex(thisObject, exec, i, value, slot.isStrictMode());
        return;
    }
357
    
weinig@apple.com's avatar
weinig@apple.com committed
358
    // Check if there are any setters or getters in the prototype chain
ggaren@apple.com's avatar
ggaren@apple.com committed
359
    JSValue prototype;
360
    if (propertyName != exec->propertyNames().underscoreProto) {
361
        for (JSObject* obj = thisObject; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
362 363
            prototype = obj->prototype();
            if (prototype.isNull()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
364 365
                ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
                if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value))
366
                    && slot.isStrictMode())
367
                    throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
368 369
                return;
            }
370
        }
371
    }
372

373 374
    JSObject* obj;
    for (obj = thisObject; ; obj = asObject(prototype)) {
375 376
        unsigned attributes;
        JSCell* specificValue;
ggaren@apple.com's avatar
ggaren@apple.com committed
377
        PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes, specificValue);
378
        if (isValidOffset(offset)) {
379
            if (attributes & ReadOnly) {
ggaren@apple.com's avatar
ggaren@apple.com committed
380
                ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
381
                if (slot.isStrictMode())
382
                    exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)));
383 384 385
                return;
            }

386
            JSValue gs = obj->getDirect(offset);
weinig@apple.com's avatar
weinig@apple.com committed
387
            if (gs.isGetterSetter()) {
388
                callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
weinig@apple.com's avatar
weinig@apple.com committed
389
                return;
390 391
            } else
                ASSERT(!(attributes & Accessor));
weinig@apple.com's avatar
weinig@apple.com committed
392 393 394 395 396

            // If there's an existing property on the object or one of its 
            // prototypes it should be replaced, so break here.
            break;
        }
darin@apple.com's avatar
darin@apple.com committed
397

weinig@apple.com's avatar
weinig@apple.com committed
398
        prototype = obj->prototype();
weinig@apple.com's avatar
weinig@apple.com committed
399
        if (prototype.isNull())
weinig@apple.com's avatar
weinig@apple.com committed
400 401
            break;
    }
402
    
ggaren@apple.com's avatar
ggaren@apple.com committed
403 404
    ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
    if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode())
405
        throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
weinig@apple.com's avatar
weinig@apple.com committed
406
    return;
darin's avatar
darin committed
407 408
}

409
void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
410
{
411
    JSObject* thisObject = jsCast<JSObject*>(cell);
412
    
413
    if (propertyName > MAX_ARRAY_INDEX) {
414 415 416 417 418 419
        PutPropertySlot slot(shouldThrow);
        thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
        return;
    }
    
    switch (thisObject->structure()->indexingType()) {
420
    case ALL_BLANK_INDEXING_TYPES:
421 422
        break;
        
423
    case ALL_UNDECIDED_INDEXING_TYPES: {
ggaren@apple.com's avatar
ggaren@apple.com committed
424
        thisObject->convertUndecidedForValue(exec->vm(), value);
425 426 427 428 429 430 431
        // Reloop.
        putByIndex(cell, exec, propertyName, value, shouldThrow);
        return;
    }
        
    case ALL_INT32_INDEXING_TYPES: {
        if (!value.isInt32()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
432
            thisObject->convertInt32ForValue(exec->vm(), value);
433 434 435 436 437 438
            putByIndex(cell, exec, propertyName, value, shouldThrow);
            return;
        }
        // Fall through.
    }
        
439 440 441 442
    case ALL_CONTIGUOUS_INDEXING_TYPES: {
        Butterfly* butterfly = thisObject->m_butterfly;
        if (propertyName >= butterfly->vectorLength())
            break;
ggaren@apple.com's avatar
ggaren@apple.com committed
443
        butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value);
444 445 446 447 448
        if (propertyName >= butterfly->publicLength())
            butterfly->setPublicLength(propertyName + 1);
        return;
    }
        
449 450
    case ALL_DOUBLE_INDEXING_TYPES: {
        if (!value.isNumber()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
451
            thisObject->convertDoubleToContiguous(exec->vm());
452 453 454 455 456 457
            // Reloop.
            putByIndex(cell, exec, propertyName, value, shouldThrow);
            return;
        }
        double valueAsDouble = value.asNumber();
        if (valueAsDouble != valueAsDouble) {
ggaren@apple.com's avatar
ggaren@apple.com committed
458
            thisObject->convertDoubleToContiguous(exec->vm());
459 460 461 462 463 464 465 466 467 468 469 470 471
            // Reloop.
            putByIndex(cell, exec, propertyName, value, shouldThrow);
            return;
        }
        Butterfly* butterfly = thisObject->m_butterfly;
        if (propertyName >= butterfly->vectorLength())
            break;
        butterfly->contiguousDouble()[propertyName] = valueAsDouble;
        if (propertyName >= butterfly->publicLength())
            butterfly->setPublicLength(propertyName + 1);
        return;
    }
        
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
    case NonArrayWithArrayStorage:
    case ArrayWithArrayStorage: {
        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
        
        if (propertyName >= storage->vectorLength())
            break;
        
        WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
        unsigned length = storage->length();
        
        // Update length & m_numValuesInVector as necessary.
        if (propertyName >= length) {
            length = propertyName + 1;
            storage->setLength(length);
            ++storage->m_numValuesInVector;
        } else if (!valueSlot)
            ++storage->m_numValuesInVector;
        
ggaren@apple.com's avatar
ggaren@apple.com committed
490
        valueSlot.set(exec->vm(), thisObject, value);
491 492 493
        return;
    }
        
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
    case NonArrayWithSlowPutArrayStorage:
    case ArrayWithSlowPutArrayStorage: {
        ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
        
        if (propertyName >= storage->vectorLength())
            break;
        
        WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
        unsigned length = storage->length();
        
        // Update length & m_numValuesInVector as necessary.
        if (propertyName >= length) {
            if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
                return;
            length = propertyName + 1;
            storage->setLength(length);
            ++storage->m_numValuesInVector;
        } else if (!valueSlot) {
            if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
                return;
            ++storage->m_numValuesInVector;
        }
        
ggaren@apple.com's avatar
ggaren@apple.com committed
517
        valueSlot.set(exec->vm(), thisObject, value);
518 519 520
        return;
    }
        
521
    default:
522
        RELEASE_ASSERT_NOT_REACHED();
523 524 525 526 527
    }
    
    thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
}

ggaren@apple.com's avatar
ggaren@apple.com committed
528
ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
529 530 531 532
{
    SparseArrayValueMap* map = storage->m_sparseMap.get();

    if (!map)
ggaren@apple.com's avatar
ggaren@apple.com committed
533
        map = allocateSparseIndexMap(vm);
534 535 536 537 538 539 540 541 542 543 544 545

    if (map->sparseMode())
        return storage;

    map->setSparseMode();

    unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
    for (unsigned i = 0; i < usedVectorLength; ++i) {
        JSValue value = storage->m_vector[i].get();
        // This will always be a new entry in the map, so no need to check we can write,
        // and attributes are default so no need to set them.
        if (value)
ggaren@apple.com's avatar
ggaren@apple.com committed
546
            map->add(this, i).iterator->value.set(vm, this, value);
547 548
    }

549
    DeferGC deferGC(vm.heap);
550
    Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(0));
551
    RELEASE_ASSERT(newButterfly);
552 553 554 555
    
    m_butterfly = newButterfly;
    newButterfly->arrayStorage()->m_indexBias = 0;
    newButterfly->arrayStorage()->setVectorLength(0);
ggaren@apple.com's avatar
ggaren@apple.com committed
556
    newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
557 558 559 560
    
    return newButterfly->arrayStorage();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
561
void JSObject::enterDictionaryIndexingMode(VM& vm)
562 563
{
    switch (structure()->indexingType()) {
564
    case ALL_BLANK_INDEXING_TYPES:
565 566 567
    case ALL_UNDECIDED_INDEXING_TYPES:
    case ALL_INT32_INDEXING_TYPES:
    case ALL_DOUBLE_INDEXING_TYPES:
568 569 570
    case ALL_CONTIGUOUS_INDEXING_TYPES:
        // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
        // this case if we ever cared.
ggaren@apple.com's avatar
ggaren@apple.com committed
571
        enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, ensureArrayStorageSlow(vm));
572
        break;
573
    case ALL_ARRAY_STORAGE_INDEXING_TYPES:
ggaren@apple.com's avatar
ggaren@apple.com committed
574
        enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
575 576 577 578 579 580 581
        break;
        
    default:
        break;
    }
}

ggaren@apple.com's avatar
ggaren@apple.com committed
582
void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
583 584 585 586
{
    if (mayInterceptIndexedAccesses())
        return;
    
587
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AddIndexedAccessors));
588
    
ggaren@apple.com's avatar
ggaren@apple.com committed
589
    if (!vm.prototypeMap.isPrototype(this))
590 591
        return;
    
ggaren@apple.com's avatar
ggaren@apple.com committed
592
    globalObject()->haveABadTime(vm);
593 594
}

ggaren@apple.com's avatar
ggaren@apple.com committed
595
Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize)
596 597 598 599 600 601 602
{
    ASSERT(length < MAX_ARRAY_INDEX);
    IndexingType oldType = structure()->indexingType();
    ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
    ASSERT(!structure()->needsSlowPutIndexing());
    ASSERT(!indexingShouldBeSparse());
    unsigned vectorLength = std::max(length, BASE_VECTOR_LEN);
603 604
    Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
        m_butterfly, vm, this, structure(), structure()->outOfLineCapacity(), false, 0,
605
        elementSize * vectorLength);
606 607
    newButterfly->setPublicLength(length);
    newButterfly->setVectorLength(vectorLength);
608 609 610
    return newButterfly;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
611
Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
612
{
613
    DeferGC deferGC(vm.heap);
ggaren@apple.com's avatar
ggaren@apple.com committed
614 615
    Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateUndecided);
616
    setStructureAndButterfly(vm, newStructure, newButterfly);
617 618 619
    return newButterfly;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
620
ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
621
{
622
    DeferGC deferGC(vm.heap);
ggaren@apple.com's avatar
ggaren@apple.com committed
623 624
    Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateInt32);
625
    setStructureAndButterfly(vm, newStructure, newButterfly);
626 627 628
    return newButterfly->contiguousInt32();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
629
ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
630
{
631
    DeferGC deferGC(vm.heap);
ggaren@apple.com's avatar
ggaren@apple.com committed
632
    Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
633 634
    for (unsigned i = newButterfly->vectorLength(); i--;)
        newButterfly->contiguousDouble()[i] = QNaN;
ggaren@apple.com's avatar
ggaren@apple.com committed
635
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateDouble);
636
    setStructureAndButterfly(vm, newStructure, newButterfly);
637 638 639
    return newButterfly->contiguousDouble();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
640
ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
641
{
642
    DeferGC deferGC(vm.heap);
ggaren@apple.com's avatar
ggaren@apple.com committed
643 644
    Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateContiguous);
645
    setStructureAndButterfly(vm, newStructure, newButterfly);
646 647 648
    return newButterfly->contiguous();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
649
ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
650
{
651
    DeferGC deferGC(vm.heap);
652
    IndexingType oldType = structure()->indexingType();
653
    ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
654 655
    Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
        m_butterfly, vm, this, structure(), structure()->outOfLineCapacity(), false, 0,
656
        ArrayStorage::sizeFor(vectorLength));
657 658
    RELEASE_ASSERT(newButterfly);

659 660 661 662 663 664
    ArrayStorage* result = newButterfly->arrayStorage();
    result->setLength(length);
    result->setVectorLength(vectorLength);
    result->m_sparseMap.clear();
    result->m_numValuesInVector = 0;
    result->m_indexBias = 0;
ggaren@apple.com's avatar
ggaren@apple.com committed
665
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), structure()->suggestedArrayStorageTransition());
666
    setStructureAndButterfly(vm, newStructure, newButterfly);
667 668 669
    return result;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
670
ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
671
{
ggaren@apple.com's avatar
ggaren@apple.com committed
672
    return createArrayStorage(vm, 0, BASE_VECTOR_LEN);
673 674
}

ggaren@apple.com's avatar
ggaren@apple.com committed
675
ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
676
{
677
    ASSERT(hasUndecided(structure()->indexingType()));
678
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32));
679 680 681
    return m_butterfly->contiguousInt32();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
682
ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
683 684 685 686 687
{
    ASSERT(hasUndecided(structure()->indexingType()));
    
    for (unsigned i = m_butterfly->vectorLength(); i--;)
        m_butterfly->contiguousDouble()[i] = QNaN;
688
    
689
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble));
690 691 692
    return m_butterfly->contiguousDouble();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
693
ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
694 695
{
    ASSERT(hasUndecided(structure()->indexingType()));
696
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
697 698 699
    return m_butterfly->contiguous();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
700
ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
701
{
702 703 704 705 706
    unsigned publicLength = m_butterfly->publicLength();
    unsigned propertyCapacity = structure()->outOfLineCapacity();
    unsigned propertySize = structure()->outOfLineSize();
    
    Butterfly* newButterfly = Butterfly::createUninitialized(
707
        vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
708 709 710 711 712 713 714 715 716 717 718 719
    
    memcpy(
        newButterfly->propertyStorage() - propertySize,
        m_butterfly->propertyStorage() - propertySize,
        propertySize * sizeof(EncodedJSValue));
    
    ArrayStorage* newStorage = newButterfly->arrayStorage();
    newStorage->setVectorLength(neededLength);
    newStorage->setLength(publicLength);
    newStorage->m_sparseMap.clear();
    newStorage->m_indexBias = 0;
    newStorage->m_numValuesInVector = 0;
720 721 722 723
    
    return newStorage;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
724
ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
725
{
726
    DeferGC deferGC(vm.heap);
727 728
    ASSERT(hasUndecided(structure()->indexingType()));
    
ggaren@apple.com's avatar
ggaren@apple.com committed
729
    ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
730 731
    // No need to copy elements.
    
ggaren@apple.com's avatar
ggaren@apple.com committed
732
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition);
733
    setStructureAndButterfly(vm, newStructure, storage->butterfly());
734 735 736
    return storage;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
737
ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
738
{
ggaren@apple.com's avatar
ggaren@apple.com committed
739
    return convertUndecidedToArrayStorage(vm, transition, m_butterfly->vectorLength());
740 741
}

ggaren@apple.com's avatar
ggaren@apple.com committed
742
ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
743
{
ggaren@apple.com's avatar
ggaren@apple.com committed
744
    return convertUndecidedToArrayStorage(vm, structure()->suggestedArrayStorageTransition());
745 746
}

ggaren@apple.com's avatar
ggaren@apple.com committed
747
ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
{
    ASSERT(hasInt32(structure()->indexingType()));
    
    for (unsigned i = m_butterfly->vectorLength(); i--;) {
        WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i];
        double* currentAsDouble = bitwise_cast<double*>(current);
        JSValue v = current->get();
        if (!v) {
            *currentAsDouble = QNaN;
            continue;
        }
        ASSERT(v.isInt32());
        *currentAsDouble = v.asInt32();
    }
    
763
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble));
764 765 766
    return m_butterfly->contiguousDouble();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
767
ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
768 769 770
{
    ASSERT(hasInt32(structure()->indexingType()));
    
771
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
772 773 774
    return m_butterfly->contiguous();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
775
ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
776 777 778
{
    ASSERT(hasInt32(structure()->indexingType()));
    
779
    DeferGC deferGC(vm.heap);
ggaren@apple.com's avatar
ggaren@apple.com committed
780
    ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
781
    for (unsigned i = m_butterfly->publicLength(); i--;) {
782 783 784 785 786 787 788
        JSValue v = m_butterfly->contiguous()[i].get();
        if (!v)
            continue;
        newStorage->m_vector[i].setWithoutWriteBarrier(v);
        newStorage->m_numValuesInVector++;
    }
    
ggaren@apple.com's avatar
ggaren@apple.com committed
789
    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition);
790
    setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
791 792 793
    return newStorage;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
794
ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
795
{
ggaren@apple.com's avatar
ggaren@apple.com committed
796
    return convertInt32ToArrayStorage(vm, transition, m_butterfly->vectorLength());
797 798
}

ggaren@apple.com's avatar
ggaren@apple.com committed
799
ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
800
{
ggaren@apple.com's avatar
ggaren@apple.com committed
801
    return convertInt32ToArrayStorage(vm, structure()->suggestedArrayStorageTransition());
802 803
}

804
template<JSObject::DoubleToContiguousMode mode>
ggaren@apple.com's avatar
ggaren@apple.com committed
805
ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
806 807 808 809 810 811 812 813 814 815 816
{
    ASSERT(hasDouble(structure()->indexingType()));
    
    for (unsigned i = m_butterfly->vectorLength(); i--;) {
        double* current = &m_butterfly->contiguousDouble()[i];
        WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
        double value = *current;
        if (value != value) {
            currentAsValue->clear();
            continue;
        }
817 818 819 820 821 822 823 824 825 826 827
        JSValue v;
        switch (mode) {
        case EncodeValueAsDouble:
            v = JSValue(JSValue::EncodeAsDouble, value);
            break;
        case RageConvertDoubleToValue:
            v = jsNumber(value);
            break;
        }
        ASSERT(v.isNumber());
        currentAsValue->setWithoutWriteBarrier(v);
828 829
    }
    
830
    setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous));
831 832 833
    return m_butterfly->contiguous();
}

ggaren@apple.com's avatar
ggaren@apple.com committed
834
ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
835
{
ggaren@apple.com's avatar
ggaren@apple.com committed
836
    return genericConvertDoubleToContiguous<EncodeValueAsDouble>(vm);
837 838
}

ggaren@apple.com's avatar
ggaren@apple.com committed
839
ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm)
840
{
ggaren@apple.com's avatar
ggaren@apple.com committed
841
    return genericConvertDoubleToContiguous<RageConvertDoubleToValue>(vm);
842 843
}

ggaren@apple.com's avatar
ggaren@apple.com committed
844
ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
845
{
846
    DeferGC deferGC(vm.heap);
847 848
    ASSERT(hasDouble(structure()->indexingType()));
    
ggaren@apple.com's avatar
ggaren@apple.com committed
849
    ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
850 851 852 853 854 855 856 857
    for (unsigned i = m_butterfly->publicLength(); i