JSArray.cpp 62.4 KB
Newer Older
darin's avatar
darin committed
1 2
/*
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
 *  Copyright (C) 2003, 2007, 2008, 2009, 2012 Apple Inc. All rights reserved.
darin's avatar
darin committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
 *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

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

darin@apple.com's avatar
darin@apple.com committed
26
#include "ArrayPrototype.h"
27
#include "ButterflyInlines.h"
28
#include "CachedCall.h"
29 30
#include "CopiedSpace.h"
#include "CopiedSpaceInlines.h"
31
#include "Error.h"
32
#include "Executable.h"
33
#include "GetterSetter.h"
34
#include "IndexingHeaderInlines.h"
darin's avatar
darin committed
35
#include "PropertyNameArray.h"
36
#include "Reject.h"
ap@webkit.org's avatar
ap@webkit.org committed
37
#include <wtf/AVLTree.h>
38
#include <wtf/Assertions.h>
39
#include <wtf/OwnPtr.h>
40
#include <Operations.h>
darin's avatar
darin committed
41

42
using namespace std;
ap@webkit.org's avatar
ap@webkit.org committed
43
using namespace WTF;
bdash's avatar
bdash committed
44

45
namespace JSC {
darin's avatar
darin committed
46

47
ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSArray);
ggaren@apple.com's avatar
ggaren@apple.com committed
48

49
const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
darin's avatar
darin committed
50

51 52
Butterfly* createArrayButterflyInDictionaryIndexingMode(
    VM& vm, JSCell* intendedOwner, unsigned initialLength)
darin's avatar
darin committed
53
{
54
    Butterfly* butterfly = Butterfly::create(
55
        vm, intendedOwner, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
56 57 58 59 60 61 62
    ArrayStorage* storage = butterfly->arrayStorage();
    storage->setLength(initialLength);
    storage->setVectorLength(0);
    storage->m_indexBias = 0;
    storage->m_sparseMap.clear();
    storage->m_numValuesInVector = 0;
    return butterfly;
63 64 65 66 67 68 69 70
}

void JSArray::setLengthWritable(ExecState* exec, bool writable)
{
    ASSERT(isLengthWritable() || !writable);
    if (!isLengthWritable() || writable)
        return;

ggaren@apple.com's avatar
ggaren@apple.com committed
71
    enterDictionaryIndexingMode(exec->vm());
72

73
    SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
74 75 76 77 78
    ASSERT(map);
    map->setLengthIsReadOnly();
}

// Defined in ES5.1 15.4.5.1
79
bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
80
{
81
    JSArray* array = jsCast<JSArray*>(object);
82 83 84 85 86

    // 3. If P is "length", then
    if (propertyName == exec->propertyNames().length) {
        // All paths through length definition call the default [[DefineOwnProperty]], hence:
        // from ES5.1 8.12.9 7.a.
87
        if (descriptor.configurablePresent() && descriptor.configurable())
88 89
            return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
        // from ES5.1 8.12.9 7.b.
90
        if (descriptor.enumerablePresent() && descriptor.enumerable())
91 92 93 94 95 96 97
            return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");

        // a. If the [[Value]] field of Desc is absent, then
        // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
        if (descriptor.isAccessorDescriptor())
            return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
        // from ES5.1 8.12.9 10.a.
98
        if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
99 100 101
            return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
        // This descriptor is either just making length read-only, or changing nothing!
        if (!descriptor.value()) {
102 103
            if (descriptor.writablePresent())
                array->setLengthWritable(exec, descriptor.writable());
104 105 106 107 108 109 110 111 112 113 114 115 116 117
            return true;
        }
        
        // b. Let newLenDesc be a copy of Desc.
        // c. Let newLen be ToUint32(Desc.[[Value]]).
        unsigned newLen = descriptor.value().toUInt32(exec);
        // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
        if (newLen != descriptor.value().toNumber(exec)) {
            throwError(exec, createRangeError(exec, "Invalid array length"));
            return false;
        }

        // Based on SameValue check in 8.12.9, this is always okay.
        if (newLen == array->length()) {
118 119
            if (descriptor.writablePresent())
                array->setLengthWritable(exec, descriptor.writable());
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
            return true;
        }

        // e. Set newLenDesc.[[Value] to newLen.
        // f. If newLen >= oldLen, then
        // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
        // g. Reject if oldLenDesc.[[Writable]] is false.
        if (!array->isLengthWritable())
            return reject(exec, throwException, "Attempting to change value of a readonly property.");
        
        // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
        // i. Else,
        // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
        // i.ii. Let newWritable be false.
        // i.iii. Set newLenDesc.[[Writable] to true.
        // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
        // k. If succeeded is false, return false.
        // l. While newLen < oldLen repeat,
        // l.i. Set oldLen to oldLen – 1.
        // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
        // l.iii. If deleteSucceeded is false, then
141
        if (!array->setLength(exec, newLen, throwException)) {
142 143 144 145
            // 1. Set newLenDesc.[[Value] to oldLen+1.
            // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
            // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
            // 4. Reject.
146 147
            if (descriptor.writablePresent())
                array->setLengthWritable(exec, descriptor.writable());
148 149 150 151
            return false;
        }

        // m. If newWritable is false, then
152 153 154 155 156
        // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
        //    Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
        //    return true.
        if (descriptor.writablePresent())
            array->setLengthWritable(exec, descriptor.writable());
157 158 159 160 161 162
        // n. Return true.
        return true;
    }

    // 4. Else if P is an array index (15.4), then
    // a. Let index be ToUint32(P).
163 164
    unsigned index = propertyName.asIndex();
    if (index != PropertyName::NotAnIndex) {
165 166 167 168 169 170 171 172 173
        // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
        if (index >= array->length() && !array->isLengthWritable())
            return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
        // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments.
        // d. Reject if succeeded is false.
        // e. If index >= oldLen
        // e.i. Set oldLenDesc.[[Value]] to index + 1.
        // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true.
        // f. Return true.
174
        return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
darin's avatar
darin committed
175 176
    }

177
    return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
darin's avatar
darin committed
178 179
}

180
bool JSArray::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
181
{
182
    JSArray* thisObject = jsCast<JSArray*>(cell);
darin's avatar
darin committed
183
    if (propertyName == exec->propertyNames().length) {
184
        slot.setValue(jsNumber(thisObject->length()));
darin's avatar
darin committed
185 186 187
        return true;
    }

188
    return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
darin's avatar
darin committed
189 190
}

191
bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
192
{
193
    JSArray* thisObject = jsCast<JSArray*>(object);
194
    if (propertyName == exec->propertyNames().length) {
195
        descriptor.setDescriptor(jsNumber(thisObject->length()), thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
196 197
        return true;
    }
198

199
    return JSObject::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
200 201
}

202
// ECMA 15.4.5.1
203
void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
204
{
205
    JSArray* thisObject = jsCast<JSArray*>(cell);
darin's avatar
darin committed
206 207

    if (propertyName == exec->propertyNames().length) {
weinig@apple.com's avatar
weinig@apple.com committed
208 209
        unsigned newLength = value.toUInt32(exec);
        if (value.toNumber(exec) != static_cast<double>(newLength)) {
210
            throwError(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
darin's avatar
darin committed
211 212
            return;
        }
213
        thisObject->setLength(exec, newLength, slot.isStrictMode());
darin's avatar
darin committed
214 215 216
        return;
    }

217
    JSObject::put(thisObject, exec, propertyName, value, slot);
darin's avatar
darin committed
218 219
}

220
bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
221
{
222
    JSArray* thisObject = jsCast<JSArray*>(cell);
darin's avatar
darin committed
223 224 225 226

    if (propertyName == exec->propertyNames().length)
        return false;

227
    return JSObject::deleteProperty(thisObject, exec, propertyName);
darin's avatar
darin committed
228 229
}

230 231
static int compareKeysForQSort(const void* a, const void* b)
{
232 233
    unsigned da = *static_cast<const unsigned*>(a);
    unsigned db = *static_cast<const unsigned*>(b);
234
    return (da > db) - (da < db);
darin's avatar
darin committed
235 236
}

237
void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
darin's avatar
darin committed
238
{
239
    JSArray* thisObject = jsCast<JSArray*>(object);
240

241 242 243
    if (mode == IncludeDontEnumProperties)
        propertyNames.add(exec->propertyNames().length);

244
    JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
245
}
darin's avatar
darin committed
246

247
// This method makes room in the vector, but leaves the new space for count slots uncleared.
ggaren@apple.com's avatar
ggaren@apple.com committed
248
bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
249
{
ggaren@apple.com's avatar
ggaren@apple.com committed
250
    ArrayStorage* storage = ensureArrayStorage(vm);
251 252 253
    Butterfly* butterfly = storage->butterfly();
    unsigned propertyCapacity = structure()->outOfLineCapacity();
    unsigned propertySize = structure()->outOfLineSize();
254

255
    // If not, we should have handled this on the fast path.
256
    ASSERT(!addToFront || count > storage->m_indexBias);
257

258 259 260 261 262 263 264
    // Step 1:
    // Gather 4 key metrics:
    //  * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
    //  * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
    //  * currentCapacity - what is the current size of the vector, including any pre-capacity.
    //  * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.

265 266
    unsigned length = storage->length();
    unsigned usedVectorLength = min(storage->vectorLength(), length);
267 268 269
    ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
    // Check that required vector length is possible, in an overflow-safe fashion.
    if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
270
        return false;
271 272 273
    unsigned requiredVectorLength = usedVectorLength + count;
    ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
    // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
274 275
    ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
    unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
276 277 278 279
    // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
    unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1);

    // Step 2:
280
    // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
281

282
    void* newAllocBase = 0;
283 284 285
    unsigned newStorageCapacity;
    // If the current storage array is sufficiently large (but not too large!) then just keep using it.
    if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
286
        newAllocBase = butterfly->base(structure());
287 288
        newStorageCapacity = currentCapacity;
    } else {
289
        size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
290
        if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase))
291 292 293 294 295 296 297 298
            return false;
        newStorageCapacity = desiredCapacity;
    }

    // Step 3:
    // Work out where we're going to move things to.

    // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
299
    // If we're adding to the end, we'll add all the new space to the end.
300 301 302 303
    // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
    // If it did, we calculate the amount that will remain based on an atomic decay - leave the
    // vector with half the post-capacity it had previously.
    unsigned postCapacity = 0;
304 305 306
    if (!addToFront)
        postCapacity = max(newStorageCapacity - requiredVectorLength, count);
    else if (length < storage->vectorLength()) {
307
        // Atomic decay, + the post-capacity cannot be greater than what is available.
308 309 310
        postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
        // If we're moving contents within the same allocation, the post-capacity is being reduced.
        ASSERT(newAllocBase != butterfly->base(structure()) || postCapacity < storage->vectorLength() - length);
311
    }
312

313 314 315 316
    unsigned newVectorLength = requiredVectorLength + postCapacity;
    unsigned newIndexBias = newStorageCapacity - newVectorLength;

    Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
317

318 319
    if (addToFront) {
        ASSERT(count + usedVectorLength <= newVectorLength);
320
        memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
321 322 323
        memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
    } else if ((newAllocBase != butterfly->base(structure())) || (newIndexBias != storage->m_indexBias)) {
        memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
324 325
        memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);

326 327 328 329 330
        WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector;
        for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
            newVector[i].clear();
    }

331 332
    newButterfly->arrayStorage()->setVectorLength(newVectorLength);
    newButterfly->arrayStorage()->m_indexBias = newIndexBias;
333

334
    m_butterfly = newButterfly;
335

ap@webkit.org's avatar
ap@webkit.org committed
336
    return true;
darin's avatar
darin committed
337 338
}

339
bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
darin's avatar
darin committed
340
{
341
    unsigned length = storage->length();
darin's avatar
darin committed
342

343
    // If the length is read only then we enter sparse mode, so should enter the following 'if'.
344
    ASSERT(isLengthWritable() || storage->m_sparseMap);
345

346
    if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
347 348 349 350 351 352
        // Fail if the length is not writable.
        if (map->lengthIsReadOnly())
            return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);

        if (newLength < length) {
            // Copy any keys we might be interested in into a vector.
353
            Vector<unsigned, 0, UnsafeVectorOverflow> keys;
354
            keys.reserveInitialCapacity(min(map->size(), static_cast<size_t>(length - newLength)));
355 356
            SparseArrayValueMap::const_iterator end = map->end();
            for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
357
                unsigned index = static_cast<unsigned>(it->key);
358 359 360 361 362 363 364 365 366 367 368 369 370 371
                if (index < length && index >= newLength)
                    keys.append(index);
            }

            // Check if the array is in sparse mode. If so there may be non-configurable
            // properties, so we have to perform deletion with caution, if not we can
            // delete values in any order.
            if (map->sparseMode()) {
                qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
                unsigned i = keys.size();
                while (i) {
                    unsigned index = keys[--i];
                    SparseArrayValueMap::iterator it = map->find(index);
                    ASSERT(it != map->notFound());
372
                    if (it->value.attributes & DontDelete) {
373
                        storage->setLength(index + 1);
374 375 376 377 378 379 380
                        return reject(exec, throwException, "Unable to delete property.");
                    }
                    map->remove(it);
                }
            } else {
                for (unsigned i = 0; i < keys.size(); ++i)
                    map->remove(keys[i]);
381
                if (map->isEmpty())
382
                    deallocateSparseIndexMap();
383 384 385 386
            }
        }
    }

darin's avatar
darin committed
387
    if (newLength < length) {
388
        // Delete properties from the vector.
389
        unsigned usedVectorLength = min(length, storage->vectorLength());
darin's avatar
darin committed
390
        for (unsigned i = newLength; i < usedVectorLength; ++i) {
391
            WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
darin's avatar
darin committed
392
            bool hadValue = valueSlot;
393
            valueSlot.clear();
darin's avatar
darin committed
394 395 396
            storage->m_numValuesInVector -= hadValue;
        }
    }
aroben@apple.com's avatar
aroben@apple.com committed
397

398
    storage->setLength(newLength);
399

400
    return true;
darin's avatar
darin committed
401 402
}

403 404 405 406 407 408 409 410 411
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
    switch (structure()->indexingType()) {
    case ArrayClass:
        if (!newLength)
            return true;
        if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
            return setLengthWithArrayStorage(
                exec, newLength, throwException,
ggaren@apple.com's avatar
ggaren@apple.com committed
412
                convertContiguousToArrayStorage(exec->vm()));
413
        }
ggaren@apple.com's avatar
ggaren@apple.com committed
414
        createInitialUndecided(exec->vm(), newLength);
415 416
        return true;
        
417 418 419
    case ArrayWithUndecided:
    case ArrayWithInt32:
    case ArrayWithDouble:
420 421 422 423 424
    case ArrayWithContiguous:
        if (newLength == m_butterfly->publicLength())
            return true;
        if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
            || (newLength >= MIN_SPARSE_ARRAY_INDEX
425
                && !isDenseEnoughForVector(newLength, countElements()))) {
426 427
            return setLengthWithArrayStorage(
                exec, newLength, throwException,
ggaren@apple.com's avatar
ggaren@apple.com committed
428
                ensureArrayStorage(exec->vm()));
429 430
        }
        if (newLength > m_butterfly->publicLength()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
431
            ensureLength(exec->vm(), newLength);
432 433
            return true;
        }
434 435 436 437 438 439 440
        if (structure()->indexingType() == ArrayWithDouble) {
            for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
                m_butterfly->contiguousDouble()[i] = QNaN;
        } else {
            for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
                m_butterfly->contiguous()[i].clear();
        }
441 442 443 444 445 446 447 448 449 450 451 452 453
        m_butterfly->setPublicLength(newLength);
        return true;
        
    case ArrayWithArrayStorage:
    case ArrayWithSlowPutArrayStorage:
        return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
        
    default:
        CRASH();
        return false;
    }
}

454
JSValue JSArray::pop(ExecState* exec)
455
{
456
    switch (structure()->indexingType()) {
457
    case ArrayClass:
458
        return jsUndefined();
459
        
460 461 462 463 464 465 466
    case ArrayWithUndecided:
        if (!m_butterfly->publicLength())
            return jsUndefined();
        // We have nothing but holes. So, drop down to the slow version.
        break;
        
    case ArrayWithInt32:
467 468 469 470 471 472
    case ArrayWithContiguous: {
        unsigned length = m_butterfly->publicLength();
        
        if (!length--)
            return jsUndefined();
        
473
        RELEASE_ASSERT(length < m_butterfly->vectorLength());
474 475 476 477 478 479 480 481 482
        JSValue value = m_butterfly->contiguous()[length].get();
        if (value) {
            m_butterfly->contiguous()[length].clear();
            m_butterfly->setPublicLength(length);
            return value;
        }
        break;
    }
        
483 484 485 486 487 488
    case ArrayWithDouble: {
        unsigned length = m_butterfly->publicLength();
        
        if (!length--)
            return jsUndefined();
        
489
        RELEASE_ASSERT(length < m_butterfly->vectorLength());
490 491 492 493 494 495 496 497 498
        double value = m_butterfly->contiguousDouble()[length];
        if (value == value) {
            m_butterfly->contiguousDouble()[length] = QNaN;
            m_butterfly->setPublicLength(length);
            return JSValue(JSValue::EncodeAsDouble, value);
        }
        break;
    }
        
499
    case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
500 501 502 503 504 505 506 507
        ArrayStorage* storage = m_butterfly->arrayStorage();
    
        unsigned length = storage->length();
        if (!length) {
            if (!isLengthWritable())
                throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
            return jsUndefined();
        }
508

509 510 511 512 513 514 515
        unsigned index = length - 1;
        if (index < storage->vectorLength()) {
            WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
            if (valueSlot) {
                --storage->m_numValuesInVector;
                JSValue element = valueSlot.get();
                valueSlot.clear();
516
            
517
                RELEASE_ASSERT(isLengthWritable());
518 519 520
                storage->setLength(index);
                return element;
            }
521
        }
522
        break;
523 524 525
    }
        
    default:
526
        CRASH();
527
        return JSValue();
528
    }
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
    
    unsigned index = getArrayLength() - 1;
    // Let element be the result of calling the [[Get]] internal method of O with argument indx.
    JSValue element = get(exec, index);
    if (exec->hadException())
        return jsUndefined();
    // Call the [[Delete]] internal method of O with arguments indx and true.
    if (!deletePropertyByIndex(this, exec, index)) {
        throwTypeError(exec, "Unable to delete property.");
        return jsUndefined();
    }
    // Call the [[Put]] internal method of O with arguments "length", indx, and true.
    setLength(exec, index, true);
    // Return element.
    return element;
544 545
}

546 547 548
// Push & putIndex are almost identical, with two small differences.
//  - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
//  - pushing to an array of length 2^32-1 stores the property, but throws a range error.
ggaren@apple.com's avatar
ggaren@apple.com committed
549
void JSArray::push(ExecState* exec, JSValue value)
550
{
551
    switch (structure()->indexingType()) {
552
    case ArrayClass: {
ggaren@apple.com's avatar
ggaren@apple.com committed
553
        createInitialUndecided(exec->vm(), 0);
554 555 556 557
        // Fall through.
    }
        
    case ArrayWithUndecided: {
ggaren@apple.com's avatar
ggaren@apple.com committed
558
        convertUndecidedForValue(exec->vm(), value);
559 560
        push(exec, value);
        return;
561
    }
562
        
563 564
    case ArrayWithInt32: {
        if (!value.isInt32()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
565
            convertInt32ForValue(exec->vm(), value);
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
            push(exec, value);
            return;
        }

        unsigned length = m_butterfly->publicLength();
        ASSERT(length <= m_butterfly->vectorLength());
        if (length < m_butterfly->vectorLength()) {
            m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
            m_butterfly->setPublicLength(length + 1);
            return;
        }
        
        if (length > MAX_ARRAY_INDEX) {
            methodTable()->putByIndex(this, exec, length, value, true);
            if (!exec->hadException())
                throwError(exec, createRangeError(exec, "Invalid array length"));
            return;
        }
        
        putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
        return;
    }

589 590 591 592
    case ArrayWithContiguous: {
        unsigned length = m_butterfly->publicLength();
        ASSERT(length <= m_butterfly->vectorLength());
        if (length < m_butterfly->vectorLength()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
593
            m_butterfly->contiguous()[length].set(exec->vm(), this, value);
594 595 596 597 598 599 600 601 602 603 604
            m_butterfly->setPublicLength(length + 1);
            return;
        }
        
        if (length > MAX_ARRAY_INDEX) {
            methodTable()->putByIndex(this, exec, length, value, true);
            if (!exec->hadException())
                throwError(exec, createRangeError(exec, "Invalid array length"));
            return;
        }
        
605
        putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
606 607 608
        return;
    }
        
609 610
    case ArrayWithDouble: {
        if (!value.isNumber()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
611
            convertDoubleToContiguous(exec->vm());
612 613 614 615 616
            push(exec, value);
            return;
        }
        double valueAsDouble = value.asNumber();
        if (valueAsDouble != valueAsDouble) {
ggaren@apple.com's avatar
ggaren@apple.com committed
617
            convertDoubleToContiguous(exec->vm());
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
            push(exec, value);
            return;
        }

        unsigned length = m_butterfly->publicLength();
        ASSERT(length <= m_butterfly->vectorLength());
        if (length < m_butterfly->vectorLength()) {
            m_butterfly->contiguousDouble()[length] = valueAsDouble;
            m_butterfly->setPublicLength(length + 1);
            return;
        }
        
        if (length > MAX_ARRAY_INDEX) {
            methodTable()->putByIndex(this, exec, length, value, true);
            if (!exec->hadException())
                throwError(exec, createRangeError(exec, "Invalid array length"));
            return;
        }
        
        putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
        break;
    }
        
641 642 643 644 645 646 647 648 649 650
    case ArrayWithSlowPutArrayStorage: {
        unsigned oldLength = length();
        if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) {
            if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
                setLength(exec, oldLength + 1, true);
            return;
        }
        // Fall through.
    }
        
651 652 653 654 655 656
    case ArrayWithArrayStorage: {
        ArrayStorage* storage = m_butterfly->arrayStorage();

        // Fast case - push within vector, always update m_length & m_numValuesInVector.
        unsigned length = storage->length();
        if (length < storage->vectorLength()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
657
            storage->m_vector[length].set(exec->vm(), this, value);
658 659 660 661
            storage->setLength(length + 1);
            ++storage->m_numValuesInVector;
            return;
        }
662

663 664
        // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
        if (storage->length() > MAX_ARRAY_INDEX) {
665 666 667 668 669 670
            methodTable()->putByIndex(this, exec, storage->length(), value, true);
            // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
            if (!exec->hadException())
                throwError(exec, createRangeError(exec, "Invalid array length"));
            return;
        }
671

672 673 674 675 676 677
        // Handled the same as putIndex.
        putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
        break;
    }
        
    default:
678
        RELEASE_ASSERT_NOT_REACHED();
679
    }
680 681
}

682
bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage)
683
{
684
    unsigned oldLength = storage->length();
685
    RELEASE_ASSERT(count <= oldLength);
686
    
687 688
    // If the array contains holes or is otherwise in an abnormal state,
    // use the generic algorithm in ArrayPrototype.
689
    if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType()))
690
        return false;
691

692 693
    if (!oldLength)
        return true;
694
    
695 696
    unsigned length = oldLength - count;
    
697
    storage->m_numValuesInVector -= count;
698
    storage->setLength(length);
699
    
700
    unsigned vectorLength = storage->vectorLength();
701 702 703 704 705 706 707 708 709 710 711 712 713 714
    if (!vectorLength)
        return true;
    
    if (startIndex >= vectorLength)
        return true;
    
    if (startIndex + count > vectorLength)
        count = vectorLength - startIndex;
    
    unsigned usedVectorLength = min(vectorLength, oldLength);
    
    vectorLength -= count;
    storage->setVectorLength(vectorLength);
    
715
    if (vectorLength) {
716 717 718 719 720 721 722
        if (startIndex < usedVectorLength - (startIndex + count)) {
            if (startIndex) {
                memmove(
                    storage->m_vector + count,
                    storage->m_vector,
                    sizeof(JSValue) * startIndex);
            }
723 724 725
            m_butterfly = m_butterfly->shift(structure(), count);
            storage = m_butterfly->arrayStorage();
            storage->m_indexBias += count;
726 727 728 729 730 731 732
        } else {
            memmove(
                storage->m_vector + startIndex,
                storage->m_vector + startIndex + count,
                sizeof(JSValue) * (usedVectorLength - (startIndex + count)));
            for (unsigned i = usedVectorLength - count; i < usedVectorLength; ++i)
                storage->m_vector[i].clear();
733 734
        }
    }
735
    return true;
736
}
737

738 739
bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
{
740
    RELEASE_ASSERT(count > 0);
741 742 743 744 745
    
    switch (structure()->indexingType()) {
    case ArrayClass:
        return true;
        
746 747 748 749 750
    case ArrayWithUndecided:
        // Don't handle this because it's confusing and it shouldn't come up.
        return false;
        
    case ArrayWithInt32:
751 752
    case ArrayWithContiguous: {
        unsigned oldLength = m_butterfly->publicLength();
753
        RELEASE_ASSERT(count <= oldLength);
754 755 756 757
        
        // We may have to walk the entire array to do the shift. We're willing to do
        // so only if it's not horribly slow.
        if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
ggaren@apple.com's avatar
ggaren@apple.com committed
758
            return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
759 760 761 762 763 764 765 766 767 768 769 770 771
        
        unsigned end = oldLength - count;
        for (unsigned i = startIndex; i < end; ++i) {
            // Storing to a hole is fine since we're still having a good time. But reading
            // from a hole is totally not fine, since we might have to read from the proto
            // chain.
            JSValue v = m_butterfly->contiguous()[i + count].get();
            if (UNLIKELY(!v)) {
                // The purpose of this path is to ensure that we don't make the same
                // mistake in the future: shiftCountWithArrayStorage() can't do anything
                // about holes (at least for now), but it can detect them quickly. So
                // we convert to array storage and then allow the array storage path to
                // figure it out.
ggaren@apple.com's avatar
ggaren@apple.com committed
772
                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
773 774 775 776 777 778 779 780 781 782 783 784 785
            }
            // No need for a barrier since we're just moving data around in the same vector.
            // This is in line with our standing assumption that we won't have a deletion
            // barrier.
            m_butterfly->contiguous()[i].setWithoutWriteBarrier(v);
        }
        for (unsigned i = end; i < oldLength; ++i)
            m_butterfly->contiguous()[i].clear();
        
        m_butterfly->setPublicLength(oldLength - count);
        return true;
    }
        
786 787
    case ArrayWithDouble: {
        unsigned oldLength = m_butterfly->publicLength();
788
        RELEASE_ASSERT(count <= oldLength);
789 790 791 792
        
        // We may have to walk the entire array to do the shift. We're willing to do
        // so only if it's not horribly slow.
        if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
ggaren@apple.com's avatar
ggaren@apple.com committed
793
            return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
794 795 796 797 798 799 800 801 802 803 804 805 806
        
        unsigned end = oldLength - count;
        for (unsigned i = startIndex; i < end; ++i) {
            // Storing to a hole is fine since we're still having a good time. But reading
            // from a hole is totally not fine, since we might have to read from the proto
            // chain.
            double v = m_butterfly->contiguousDouble()[i + count];
            if (UNLIKELY(v != v)) {
                // The purpose of this path is to ensure that we don't make the same
                // mistake in the future: shiftCountWithArrayStorage() can't do anything
                // about holes (at least for now), but it can detect them quickly. So
                // we convert to array storage and then allow the array storage path to
                // figure it out.
ggaren@apple.com's avatar
ggaren@apple.com committed
807
                return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
808 809 810 811 812 813 814 815 816 817 818 819 820
            }
            // No need for a barrier since we're just moving data around in the same vector.
            // This is in line with our standing assumption that we won't have a deletion
            // barrier.
            m_butterfly->contiguousDouble()[i] = v;
        }
        for (unsigned i = end; i < oldLength; ++i)
            m_butterfly->contiguousDouble()[i] = QNaN;
        
        m_butterfly->setPublicLength(oldLength - count);
        return true;
    }
        
821 822 823 824 825 826 827 828 829 830
    case ArrayWithArrayStorage:
    case ArrayWithSlowPutArrayStorage:
        return shiftCountWithArrayStorage(startIndex, count, arrayStorage());
        
    default:
        CRASH();
        return false;
    }
}

831
// Returns true if the unshift can be handled, false to fallback.    
832
bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
833
{
834
    unsigned length = storage->length();
835

836
    RELEASE_ASSERT(startIndex <= length);
837

838 839
    // If the array contains holes or is otherwise in an abnormal state,
    // use the generic algorithm in ArrayPrototype.
840
    if (length != storage->m_numValuesInVector || storage->inSparseMode() || shouldUseSlowPut(structure()->indexingType()))
841 842
        return false;

843 844 845 846 847
    bool moveFront = !startIndex || startIndex < length / 2;

    unsigned vectorLength = storage->vectorLength();

    if (moveFront && storage->m_indexBias >= count) {
848 849 850
        m_butterfly = storage->butterfly()->unshift(structure(), count);
        storage = m_butterfly->arrayStorage();
        storage->m_indexBias -= count;
851 852 853
        storage->setVectorLength(vectorLength + count);
    } else if (!moveFront && vectorLength - length >= count)
        storage = storage->butterfly()->arrayStorage();
ggaren@apple.com's avatar
ggaren@apple.com committed
854
    else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
855 856
        storage = arrayStorage();
    else {
857
        throwOutOfMemoryError(exec);
858
        return true;
859
    }
860

861
    WriteBarrier<Unknown>* vector = storage->m_vector;
862 863 864 865

    if (startIndex) {
        if (moveFront)
            memmove(vector, vector + count, startIndex * sizeof(JSValue));
866
        else if (length - startIndex)
867 868 869
            memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
    }

870
    for (unsigned i = 0; i < count; i++)
871
        vector[i + startIndex].clear();
872
    return true;
873 874
}

875 876 877 878
bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
{
    switch (structure()->indexingType()) {
    case ArrayClass:
879
    case ArrayWithUndecided:
880 881
        // We could handle this. But it shouldn't ever come up, so we won't.
        return false;
882 883

    case ArrayWithInt32:
884 885 886 887 888 889
    case ArrayWithContiguous: {
        unsigned oldLength = m_butterfly->publicLength();
        
        // We may have to walk the entire array to do the unshift. We're willing to do so
        // only if it's not horribly slow.
        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
ggaren@apple.com's avatar
ggaren@apple.com committed
890
            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
891
        
ggaren@apple.com's avatar
ggaren@apple.com committed
892
        ensureLength(exec->vm(), oldLength + count);
893 894 895 896
        
        for (unsigned i = oldLength; i-- > startIndex;) {
            JSValue v = m_butterfly->contiguous()[i].get();
            if (UNLIKELY(!v))
ggaren@apple.com's avatar
ggaren@apple.com committed
897
                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
898 899 900 901 902 903 904 905 906 907 908
            m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
        }
        
        // NOTE: we're leaving being garbage in the part of the array that we shifted out
        // of. This is fine because the caller is required to store over that area, and
        // in contiguous mode storing into a hole is guaranteed to behave exactly the same
        // as storing over an existing element.
        
        return true;
    }
        
909 910 911 912 913 914
    case ArrayWithDouble: {
        unsigned oldLength = m_butterfly->publicLength();
        
        // We may have to walk the entire array to do the unshift. We're willing to do so
        // only if it's not horribly slow.
        if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
ggaren@apple.com's avatar
ggaren@apple.com committed
915
            return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
916
        
ggaren@apple.com's avatar
ggaren@apple.com committed
917
        ensureLength(exec->vm(), oldLength + count);
918 919 920 921
        
        for (unsigned i = oldLength; i-- > startIndex;) {
            double v = m_butterfly->contiguousDouble()[i];
            if (UNLIKELY(v != v))
ggaren@apple.com's avatar
ggaren@apple.com committed
922
                return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
923 924 925 926 927 928 929 930 931 932 933
            m_butterfly->contiguousDouble()[i + count] = v;
        }
        
        // NOTE: we're leaving being garbage in the part of the array that we shifted out
        // of. This is fine because the caller is required to store over that area, and
        // in contiguous mode storing into a hole is guaranteed to behave exactly the same
        // as storing over an existing element.
        
        return true;
    }
        
934 935 936 937 938 939 940 941 942 943
    case ArrayWithArrayStorage:
    case ArrayWithSlowPutArrayStorage:
        return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
        
    default:
        CRASH();
        return false;
    }
}

944 945 946 947 948 949 950 951 952 953 954 955 956 957
static int compareNumbersForQSortWithInt32(const void* a, const void* b)
{
    int32_t ia = static_cast<const JSValue*>(a)->asInt32();
    int32_t ib = static_cast<const JSValue*>(b)->asInt32();
    return ia - ib;
}

static int compareNumbersForQSortWithDouble(const void* a, const void* b)
{
    double da = *static_cast<const double*>(a);
    double db = *static_cast<const double*>(b);
    return (da > db) - (da < db);
}

ggaren@apple.com's avatar
ggaren@apple.com committed
958 959
static int compareNumbersForQSort(const void* a, const void* b)
{
ggaren@apple.com's avatar
ggaren@apple.com committed
960 961
    double da = static_cast<const JSValue*>(a)->asNumber();
    double db = static_cast<const JSValue*>(b)->asNumber();
ggaren@apple.com's avatar
ggaren@apple.com committed
962 963 964
    return (da > db) - (da < db);
}

965 966
static int compareByStringPairForQSort(const void* a, const void* b)
{
ggaren@apple.com's avatar
ggaren@apple.com committed
967 968
    const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
    const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
969
    return codePointCompare(va->second, vb->second);
970
}
darin's avatar
darin committed
971

972 973 974
template<IndexingType indexingType>
void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
{
975
    ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage);
976 977 978 979 980 981 982
    
    unsigned lengthNotIncludingUndefined;
    unsigned newRelevantLength;
    compactForSorting<indexingType>(
        lengthNotIncludingUndefined,
        newRelevantLength);
    
983
    ContiguousJSValues data = indexingData<indexingType>();
984 985 986 987 988 989 990 991 992 993
    
    if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
        throwOutOfMemoryError(exec);
        return;
    }
    
    if (!lengthNotIncludingUndefined)
        return;
    
    bool allValuesAreNumbers = true;
994 995 996 997 998 999 1000 1001 1002 1003 1004
    switch (indexingType) {
    case ArrayWithInt32:
    case ArrayWithDouble:
        break;
        
    default:
        for (size_t i = 0; i < newRelevantLength; ++i) {
            if (!data[i].isNumber()) {
                allValuesAreNumbers = false;
                break;
            }
1005
        }
1006
        break;
1007 1008 1009 1010 1011 1012 1013 1014
    }
    
    if (!allValuesAreNumbers)
        return sort(exec, compareFunction, callType, callData);
    
    // For numeric comparison, which is fast, qsort is faster than mergesort. We
    // also don't require mergesort's stability, since there's no user visible
    // side-effect from swapping the order of equal primitive values.
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
    int (*compare)(const void*, const void*);
    switch (indexingType) {
    case ArrayWithInt32:
        compare = compareNumbersForQSortWithInt32;
        break;
        
    case ArrayWithDouble:
        compare = compareNumbersForQSortWithDouble;
        ASSERT(sizeof(WriteBarrier<Unknown>) == sizeof(double));
        break;
        
    default:
        compare = compareNumbersForQSort;
        break;
    }
1030 1031
    ASSERT(data.length() >= newRelevantLength);
    qsort(data.data(), newRelevantLength, sizeof(WriteBarrier<Unknown>), compare);
1032 1033 1034
    return;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
1035
void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
ggaren@apple.com's avatar
ggaren@apple.com committed
1036
{
1037
    ASSERT(!inSparseIndexingMode());
ggaren@apple.com's avatar
ggaren@apple.com committed
1038

1039
    switch (structure()->indexingType()) {
1040
    case ArrayClass:
ggaren@apple.com's avatar
ggaren@apple.com committed
1041 1042
        return;
        
1043 1044 1045 1046 1047 1048 1049 1050
    case ArrayWithInt32:
        sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
        break;
        
    case ArrayWithDouble:
        sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
        break;
        
1051 1052 1053
    case ArrayWithContiguous:
        sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
        return;
1054

1055 1056
    case ArrayWithArrayStorage:
        sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
1057 1058 1059
        return;
        
    default:
1060 1061
        CRASH();
        return;
ggaren@apple.com's avatar
ggaren@apple.com committed
1062 1063 1064
    }
}

1065 1066 1067
template <IndexingType> struct ContiguousTypeAccessor {
    typedef WriteBarrier<Unknown> Type;
    static JSValue getAsValue(ContiguousData<Type> data, size_t i) { return data[i].get(); }
ggaren@apple.com's avatar
ggaren@apple.com committed
1068
    static void setWithValue(VM& vm, JSArray* thisValue, ContiguousData<Type> data, size_t i, JSValue value)
1069
    {
ggaren@apple.com's avatar
ggaren@apple.com committed
1070
        data[i].set(vm, thisValue, value);
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
    }
    static void replaceDataReference(ContiguousData<Type>* outData, ContiguousJSValues inData)
    {
        *outData = inData;
    }
};

template <> struct ContiguousTypeAccessor<ArrayWithDouble> {
    typedef double Type;
    static JSValue getAsValue(ContiguousData<Type> data, size_t i) { ASSERT(data[i] == data[i]); return JSValue(JSValue::EncodeAsDouble, data[i]); }
ggaren@apple.com's avatar
ggaren@apple.com committed
1081
    static void setWithValue(VM&, JSArray*, ContiguousData<Type> data, size_t i, JSValue value)
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
    {
        data[i