]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSArray.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / JSArray.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
ed1e77d3 3 * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013, 2015 Apple Inc. All rights reserved.
9dae56ea
A
4 * Copyright (C) 2003 Peter Kelly (pmk@post.com)
5 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23#include "config.h"
24#include "JSArray.h"
25
26#include "ArrayPrototype.h"
93a37866 27#include "ButterflyInlines.h"
ba379fdc 28#include "CachedCall.h"
93a37866 29#include "CopiedSpace.h"
f9bf01c6
A
30#include "Error.h"
31#include "Executable.h"
6fe7ccc8 32#include "GetterSetter.h"
93a37866 33#include "IndexingHeaderInlines.h"
81345200 34#include "JSCInlines.h"
9dae56ea 35#include "PropertyNameArray.h"
93a37866 36#include "Reject.h"
9dae56ea 37#include <wtf/Assertions.h>
9dae56ea 38
9dae56ea
A
39using namespace std;
40using namespace WTF;
41
42namespace JSC {
43
81345200 44STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
9dae56ea 45
ed1e77d3 46const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(JSArray)};
14957cd0 47
81345200
A
48Butterfly* createArrayButterflyInDictionaryIndexingMode(
49 VM& vm, JSCell* intendedOwner, unsigned initialLength)
6fe7ccc8 50{
93a37866 51 Butterfly* butterfly = Butterfly::create(
81345200 52 vm, intendedOwner, 0, 0, true, IndexingHeader(), ArrayStorage::sizeFor(0));
93a37866
A
53 ArrayStorage* storage = butterfly->arrayStorage();
54 storage->setLength(initialLength);
55 storage->setVectorLength(0);
56 storage->m_indexBias = 0;
57 storage->m_sparseMap.clear();
58 storage->m_numValuesInVector = 0;
59 return butterfly;
6fe7ccc8
A
60}
61
62void JSArray::setLengthWritable(ExecState* exec, bool writable)
63{
64 ASSERT(isLengthWritable() || !writable);
65 if (!isLengthWritable() || writable)
66 return;
67
93a37866 68 enterDictionaryIndexingMode(exec->vm());
6fe7ccc8 69
93a37866 70 SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
6fe7ccc8
A
71 ASSERT(map);
72 map->setLengthIsReadOnly();
73}
74
75// Defined in ES5.1 15.4.5.1
81345200 76bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
6fe7ccc8
A
77{
78 JSArray* array = jsCast<JSArray*>(object);
79
80 // 3. If P is "length", then
81 if (propertyName == exec->propertyNames().length) {
82 // All paths through length definition call the default [[DefineOwnProperty]], hence:
83 // from ES5.1 8.12.9 7.a.
84 if (descriptor.configurablePresent() && descriptor.configurable())
85 return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
86 // from ES5.1 8.12.9 7.b.
87 if (descriptor.enumerablePresent() && descriptor.enumerable())
88 return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
89
90 // a. If the [[Value]] field of Desc is absent, then
91 // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments.
92 if (descriptor.isAccessorDescriptor())
93 return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property.");
94 // from ES5.1 8.12.9 10.a.
95 if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
96 return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
97 // This descriptor is either just making length read-only, or changing nothing!
98 if (!descriptor.value()) {
99 if (descriptor.writablePresent())
100 array->setLengthWritable(exec, descriptor.writable());
101 return true;
102 }
103
104 // b. Let newLenDesc be a copy of Desc.
105 // c. Let newLen be ToUint32(Desc.[[Value]]).
106 unsigned newLen = descriptor.value().toUInt32(exec);
107 // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception.
108 if (newLen != descriptor.value().toNumber(exec)) {
ed1e77d3 109 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
6fe7ccc8
A
110 return false;
111 }
112
113 // Based on SameValue check in 8.12.9, this is always okay.
ed1e77d3 114 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
6fe7ccc8
A
115 if (newLen == array->length()) {
116 if (descriptor.writablePresent())
117 array->setLengthWritable(exec, descriptor.writable());
118 return true;
119 }
120
121 // e. Set newLenDesc.[[Value] to newLen.
122 // f. If newLen >= oldLen, then
123 // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments.
124 // g. Reject if oldLenDesc.[[Writable]] is false.
125 if (!array->isLengthWritable())
126 return reject(exec, throwException, "Attempting to change value of a readonly property.");
127
128 // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
129 // i. Else,
130 // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted.
131 // i.ii. Let newWritable be false.
132 // i.iii. Set newLenDesc.[[Writable] to true.
133 // 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.
134 // k. If succeeded is false, return false.
135 // l. While newLen < oldLen repeat,
136 // l.i. Set oldLen to oldLen – 1.
137 // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments.
138 // l.iii. If deleteSucceeded is false, then
139 if (!array->setLength(exec, newLen, throwException)) {
140 // 1. Set newLenDesc.[[Value] to oldLen+1.
141 // 2. If newWritable is false, set newLenDesc.[[Writable] to false.
142 // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments.
143 // 4. Reject.
144 if (descriptor.writablePresent())
145 array->setLengthWritable(exec, descriptor.writable());
146 return false;
147 }
148
149 // m. If newWritable is false, then
150 // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length",
151 // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always
152 // return true.
153 if (descriptor.writablePresent())
154 array->setLengthWritable(exec, descriptor.writable());
155 // n. Return true.
156 return true;
157 }
158
159 // 4. Else if P is an array index (15.4), then
6fe7ccc8 160 // a. Let index be ToUint32(P).
ed1e77d3 161 if (Optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
6fe7ccc8 162 // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
ed1e77d3
A
163 uint32_t index = optionalIndex.value();
164 // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
6fe7ccc8
A
165 if (index >= array->length() && !array->isLengthWritable())
166 return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
167 // 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.
168 // d. Reject if succeeded is false.
169 // e. If index >= oldLen
170 // e.i. Set oldLenDesc.[[Value]] to index + 1.
171 // 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.
172 // f. Return true.
93a37866 173 return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
9dae56ea
A
174 }
175
93a37866 176 return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
9dae56ea
A
177}
178
81345200 179bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
f9bf01c6 180{
6fe7ccc8 181 JSArray* thisObject = jsCast<JSArray*>(object);
f9bf01c6 182 if (propertyName == exec->propertyNames().length) {
81345200
A
183 unsigned attributes = thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly;
184 slot.setValue(thisObject, attributes, jsNumber(thisObject->length()));
f9bf01c6
A
185 return true;
186 }
14957cd0 187
81345200 188 return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
f9bf01c6
A
189}
190
9dae56ea 191// ECMA 15.4.5.1
93a37866 192void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
9dae56ea 193{
6fe7ccc8 194 JSArray* thisObject = jsCast<JSArray*>(cell);
9dae56ea
A
195
196 if (propertyName == exec->propertyNames().length) {
197 unsigned newLength = value.toUInt32(exec);
198 if (value.toNumber(exec) != static_cast<double>(newLength)) {
81345200 199 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
9dae56ea
A
200 return;
201 }
6fe7ccc8 202 thisObject->setLength(exec, newLength, slot.isStrictMode());
9dae56ea
A
203 return;
204 }
205
6fe7ccc8 206 JSObject::put(thisObject, exec, propertyName, value, slot);
9dae56ea
A
207}
208
93a37866 209bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
9dae56ea 210{
6fe7ccc8 211 JSArray* thisObject = jsCast<JSArray*>(cell);
9dae56ea
A
212
213 if (propertyName == exec->propertyNames().length)
214 return false;
215
6fe7ccc8 216 return JSObject::deleteProperty(thisObject, exec, propertyName);
9dae56ea
A
217}
218
6fe7ccc8
A
219static int compareKeysForQSort(const void* a, const void* b)
220{
221 unsigned da = *static_cast<const unsigned*>(a);
222 unsigned db = *static_cast<const unsigned*>(b);
223 return (da > db) - (da < db);
9dae56ea
A
224}
225
93a37866 226void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
9dae56ea 227{
6fe7ccc8 228 JSArray* thisObject = jsCast<JSArray*>(object);
9dae56ea 229
ed1e77d3 230 if (mode.includeDontEnumProperties())
f9bf01c6
A
231 propertyNames.add(exec->propertyNames().length);
232
93a37866 233 JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
9dae56ea
A
234}
235
93a37866
A
236// This method makes room in the vector, but leaves the new space for count slots uncleared.
237bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
14957cd0 238{
93a37866
A
239 ArrayStorage* storage = ensureArrayStorage(vm);
240 Butterfly* butterfly = storage->butterfly();
81345200
A
241 unsigned propertyCapacity = structure(vm)->outOfLineCapacity();
242 unsigned propertySize = structure(vm)->outOfLineSize();
14957cd0 243
6fe7ccc8 244 // If not, we should have handled this on the fast path.
93a37866 245 ASSERT(!addToFront || count > storage->m_indexBias);
9dae56ea 246
6fe7ccc8
A
247 // Step 1:
248 // Gather 4 key metrics:
249 // * usedVectorLength - how many entries are currently in the vector (conservative estimate - fewer may be in use in sparse vectors).
250 // * requiredVectorLength - how many entries are will there be in the vector, after allocating space for 'count' more.
251 // * currentCapacity - what is the current size of the vector, including any pre-capacity.
252 // * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
253
93a37866
A
254 unsigned length = storage->length();
255 unsigned usedVectorLength = min(storage->vectorLength(), length);
6fe7ccc8
A
256 ASSERT(usedVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
257 // Check that required vector length is possible, in an overflow-safe fashion.
258 if (count > MAX_STORAGE_VECTOR_LENGTH - usedVectorLength)
14957cd0 259 return false;
6fe7ccc8
A
260 unsigned requiredVectorLength = usedVectorLength + count;
261 ASSERT(requiredVectorLength <= MAX_STORAGE_VECTOR_LENGTH);
262 // The sum of m_vectorLength and m_indexBias will never exceed MAX_STORAGE_VECTOR_LENGTH.
93a37866
A
263 ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
264 unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
6fe7ccc8
A
265 // The calculation of desiredCapacity won't overflow, due to the range of MAX_STORAGE_VECTOR_LENGTH.
266 unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_VECTOR_LEN, requiredVectorLength) << 1);
267
268 // Step 2:
93a37866 269 // We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
6fe7ccc8 270
81345200 271 DeferGC deferGC(vm.heap);
6fe7ccc8
A
272 void* newAllocBase = 0;
273 unsigned newStorageCapacity;
274 // If the current storage array is sufficiently large (but not too large!) then just keep using it.
275 if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
81345200 276 newAllocBase = butterfly->base(structure(vm));
6fe7ccc8
A
277 newStorageCapacity = currentCapacity;
278 } else {
93a37866 279 size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
81345200 280 if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase))
6fe7ccc8
A
281 return false;
282 newStorageCapacity = desiredCapacity;
283 }
284
285 // Step 3:
286 // Work out where we're going to move things to.
287
288 // Determine how much of the vector to use as pre-capacity, and how much as post-capacity.
93a37866 289 // If we're adding to the end, we'll add all the new space to the end.
6fe7ccc8
A
290 // If the vector had no free post-capacity (length >= m_vectorLength), don't give it any.
291 // If it did, we calculate the amount that will remain based on an atomic decay - leave the
292 // vector with half the post-capacity it had previously.
293 unsigned postCapacity = 0;
93a37866
A
294 if (!addToFront)
295 postCapacity = max(newStorageCapacity - requiredVectorLength, count);
296 else if (length < storage->vectorLength()) {
6fe7ccc8 297 // Atomic decay, + the post-capacity cannot be greater than what is available.
93a37866 298 postCapacity = min((storage->vectorLength() - length) >> 1, newStorageCapacity - requiredVectorLength);
6fe7ccc8 299 // If we're moving contents within the same allocation, the post-capacity is being reduced.
81345200 300 ASSERT(newAllocBase != butterfly->base(structure(vm)) || postCapacity < storage->vectorLength() - length);
6fe7ccc8
A
301 }
302
93a37866
A
303 unsigned newVectorLength = requiredVectorLength + postCapacity;
304 unsigned newIndexBias = newStorageCapacity - newVectorLength;
6fe7ccc8 305
93a37866 306 Butterfly* newButterfly = Butterfly::fromBase(newAllocBase, newIndexBias, propertyCapacity);
6fe7ccc8 307
93a37866
A
308 if (addToFront) {
309 ASSERT(count + usedVectorLength <= newVectorLength);
310 memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
311 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
81345200 312 } else if ((newAllocBase != butterfly->base(structure(vm))) || (newIndexBias != storage->m_indexBias)) {
93a37866
A
313 memmove(newButterfly->propertyStorage() - propertySize, butterfly->propertyStorage() - propertySize, sizeof(JSValue) * propertySize + sizeof(IndexingHeader) + ArrayStorage::sizeFor(0));
314 memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
6fe7ccc8 315
93a37866
A
316 WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector;
317 for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
318 newVector[i].clear();
6fe7ccc8 319 }
14957cd0 320
93a37866
A
321 newButterfly->arrayStorage()->setVectorLength(newVectorLength);
322 newButterfly->arrayStorage()->m_indexBias = newIndexBias;
81345200 323 setButterflyWithoutChangingStructure(vm, newButterfly);
93a37866 324
14957cd0
A
325 return true;
326}
14957cd0 327
93a37866 328bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
14957cd0 329{
93a37866 330 unsigned length = storage->length();
9dae56ea 331
6fe7ccc8 332 // If the length is read only then we enter sparse mode, so should enter the following 'if'.
93a37866 333 ASSERT(isLengthWritable() || storage->m_sparseMap);
6fe7ccc8 334
93a37866 335 if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
6fe7ccc8
A
336 // Fail if the length is not writable.
337 if (map->lengthIsReadOnly())
338 return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
339
340 if (newLength < length) {
341 // Copy any keys we might be interested in into a vector.
93a37866
A
342 Vector<unsigned, 0, UnsafeVectorOverflow> keys;
343 keys.reserveInitialCapacity(min(map->size(), static_cast<size_t>(length - newLength)));
6fe7ccc8
A
344 SparseArrayValueMap::const_iterator end = map->end();
345 for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
93a37866 346 unsigned index = static_cast<unsigned>(it->key);
6fe7ccc8
A
347 if (index < length && index >= newLength)
348 keys.append(index);
349 }
350
351 // Check if the array is in sparse mode. If so there may be non-configurable
352 // properties, so we have to perform deletion with caution, if not we can
353 // delete values in any order.
354 if (map->sparseMode()) {
355 qsort(keys.begin(), keys.size(), sizeof(unsigned), compareKeysForQSort);
356 unsigned i = keys.size();
357 while (i) {
358 unsigned index = keys[--i];
359 SparseArrayValueMap::iterator it = map->find(index);
360 ASSERT(it != map->notFound());
93a37866
A
361 if (it->value.attributes & DontDelete) {
362 storage->setLength(index + 1);
6fe7ccc8
A
363 return reject(exec, throwException, "Unable to delete property.");
364 }
365 map->remove(it);
366 }
367 } else {
368 for (unsigned i = 0; i < keys.size(); ++i)
369 map->remove(keys[i]);
370 if (map->isEmpty())
93a37866 371 deallocateSparseIndexMap();
6fe7ccc8
A
372 }
373 }
374 }
375
9dae56ea 376 if (newLength < length) {
6fe7ccc8 377 // Delete properties from the vector.
93a37866 378 unsigned usedVectorLength = min(length, storage->vectorLength());
9dae56ea 379 for (unsigned i = newLength; i < usedVectorLength; ++i) {
14957cd0 380 WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
ed1e77d3 381 bool hadValue = !!valueSlot;
14957cd0 382 valueSlot.clear();
9dae56ea
A
383 storage->m_numValuesInVector -= hadValue;
384 }
9dae56ea
A
385 }
386
93a37866 387 storage->setLength(newLength);
9dae56ea 388
6fe7ccc8 389 return true;
9dae56ea
A
390}
391
93a37866
A
392bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
393{
81345200 394 switch (indexingType()) {
93a37866
A
395 case ArrayClass:
396 if (!newLength)
397 return true;
398 if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
399 return setLengthWithArrayStorage(
400 exec, newLength, throwException,
401 convertContiguousToArrayStorage(exec->vm()));
402 }
403 createInitialUndecided(exec->vm(), newLength);
404 return true;
405
406 case ArrayWithUndecided:
407 case ArrayWithInt32:
408 case ArrayWithDouble:
ed1e77d3 409 case ArrayWithContiguous: {
93a37866
A
410 if (newLength == m_butterfly->publicLength())
411 return true;
412 if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
413 || (newLength >= MIN_SPARSE_ARRAY_INDEX
414 && !isDenseEnoughForVector(newLength, countElements()))) {
415 return setLengthWithArrayStorage(
416 exec, newLength, throwException,
417 ensureArrayStorage(exec->vm()));
418 }
419 if (newLength > m_butterfly->publicLength()) {
420 ensureLength(exec->vm(), newLength);
421 return true;
422 }
ed1e77d3
A
423
424 unsigned lengthToClear = m_butterfly->publicLength() - newLength;
425 unsigned costToAllocateNewButterfly = 64; // a heuristic.
426 if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
427 reallocateAndShrinkButterfly(exec->vm(), newLength);
428 return true;
429 }
430
81345200 431 if (indexingType() == ArrayWithDouble) {
93a37866 432 for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
81345200 433 m_butterfly->contiguousDouble()[i] = PNaN;
93a37866
A
434 } else {
435 for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
436 m_butterfly->contiguous()[i].clear();
437 }
438 m_butterfly->setPublicLength(newLength);
439 return true;
ed1e77d3 440 }
93a37866
A
441
442 case ArrayWithArrayStorage:
443 case ArrayWithSlowPutArrayStorage:
444 return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
445
446 default:
447 CRASH();
448 return false;
449 }
450}
451
6fe7ccc8 452JSValue JSArray::pop(ExecState* exec)
9dae56ea 453{
81345200 454 switch (indexingType()) {
93a37866 455 case ArrayClass:
9dae56ea 456 return jsUndefined();
93a37866
A
457
458 case ArrayWithUndecided:
459 if (!m_butterfly->publicLength())
460 return jsUndefined();
461 // We have nothing but holes. So, drop down to the slow version.
462 break;
463
464 case ArrayWithInt32:
465 case ArrayWithContiguous: {
466 unsigned length = m_butterfly->publicLength();
467
468 if (!length--)
469 return jsUndefined();
470
471 RELEASE_ASSERT(length < m_butterfly->vectorLength());
472 JSValue value = m_butterfly->contiguous()[length].get();
473 if (value) {
474 m_butterfly->contiguous()[length].clear();
475 m_butterfly->setPublicLength(length);
476 return value;
477 }
478 break;
479 }
480
481 case ArrayWithDouble: {
482 unsigned length = m_butterfly->publicLength();
483
484 if (!length--)
485 return jsUndefined();
486
487 RELEASE_ASSERT(length < m_butterfly->vectorLength());
488 double value = m_butterfly->contiguousDouble()[length];
489 if (value == value) {
81345200 490 m_butterfly->contiguousDouble()[length] = PNaN;
93a37866
A
491 m_butterfly->setPublicLength(length);
492 return JSValue(JSValue::EncodeAsDouble, value);
493 }
494 break;
6fe7ccc8 495 }
93a37866
A
496
497 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
498 ArrayStorage* storage = m_butterfly->arrayStorage();
499
500 unsigned length = storage->length();
501 if (!length) {
502 if (!isLengthWritable())
503 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
504 return jsUndefined();
505 }
9dae56ea 506
93a37866
A
507 unsigned index = length - 1;
508 if (index < storage->vectorLength()) {
509 WriteBarrier<Unknown>& valueSlot = storage->m_vector[index];
510 if (valueSlot) {
511 --storage->m_numValuesInVector;
512 JSValue element = valueSlot.get();
513 valueSlot.clear();
6fe7ccc8 514
93a37866
A
515 RELEASE_ASSERT(isLengthWritable());
516 storage->setLength(index);
517 return element;
518 }
9dae56ea 519 }
93a37866 520 break;
9dae56ea 521 }
93a37866
A
522
523 default:
524 CRASH();
525 return JSValue();
526 }
527
528 unsigned index = getArrayLength() - 1;
6fe7ccc8
A
529 // Let element be the result of calling the [[Get]] internal method of O with argument indx.
530 JSValue element = get(exec, index);
531 if (exec->hadException())
532 return jsUndefined();
533 // Call the [[Delete]] internal method of O with arguments indx and true.
93a37866 534 if (!deletePropertyByIndex(this, exec, index)) {
ed1e77d3 535 throwTypeError(exec, ASCIILiteral("Unable to delete property."));
93a37866
A
536 return jsUndefined();
537 }
6fe7ccc8
A
538 // Call the [[Put]] internal method of O with arguments "length", indx, and true.
539 setLength(exec, index, true);
540 // Return element.
6fe7ccc8 541 return element;
9dae56ea
A
542}
543
6fe7ccc8
A
544// Push & putIndex are almost identical, with two small differences.
545// - we always are writing beyond the current array bounds, so it is always necessary to update m_length & m_numValuesInVector.
546// - pushing to an array of length 2^32-1 stores the property, but throws a range error.
ba379fdc 547void JSArray::push(ExecState* exec, JSValue value)
9dae56ea 548{
81345200 549 switch (indexingType()) {
93a37866
A
550 case ArrayClass: {
551 createInitialUndecided(exec->vm(), 0);
81345200 552 FALLTHROUGH;
93a37866
A
553 }
554
555 case ArrayWithUndecided: {
556 convertUndecidedForValue(exec->vm(), value);
557 push(exec, value);
558 return;
559 }
560
561 case ArrayWithInt32: {
562 if (!value.isInt32()) {
563 convertInt32ForValue(exec->vm(), value);
564 push(exec, value);
565 return;
566 }
567
568 unsigned length = m_butterfly->publicLength();
569 ASSERT(length <= m_butterfly->vectorLength());
570 if (length < m_butterfly->vectorLength()) {
571 m_butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
572 m_butterfly->setPublicLength(length + 1);
573 return;
574 }
575
576 if (length > MAX_ARRAY_INDEX) {
81345200 577 methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
93a37866 578 if (!exec->hadException())
ed1e77d3 579 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
93a37866
A
580 return;
581 }
582
583 putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
9dae56ea
A
584 return;
585 }
586
93a37866
A
587 case ArrayWithContiguous: {
588 unsigned length = m_butterfly->publicLength();
589 ASSERT(length <= m_butterfly->vectorLength());
590 if (length < m_butterfly->vectorLength()) {
591 m_butterfly->contiguous()[length].set(exec->vm(), this, value);
592 m_butterfly->setPublicLength(length + 1);
593 return;
594 }
595
596 if (length > MAX_ARRAY_INDEX) {
81345200 597 methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
93a37866 598 if (!exec->hadException())
ed1e77d3 599 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
93a37866
A
600 return;
601 }
602
603 putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
6fe7ccc8 604 return;
9dae56ea 605 }
93a37866
A
606
607 case ArrayWithDouble: {
608 if (!value.isNumber()) {
609 convertDoubleToContiguous(exec->vm());
610 push(exec, value);
611 return;
612 }
613 double valueAsDouble = value.asNumber();
614 if (valueAsDouble != valueAsDouble) {
615 convertDoubleToContiguous(exec->vm());
616 push(exec, value);
617 return;
618 }
9dae56ea 619
93a37866
A
620 unsigned length = m_butterfly->publicLength();
621 ASSERT(length <= m_butterfly->vectorLength());
622 if (length < m_butterfly->vectorLength()) {
623 m_butterfly->contiguousDouble()[length] = valueAsDouble;
624 m_butterfly->setPublicLength(length + 1);
625 return;
626 }
627
628 if (length > MAX_ARRAY_INDEX) {
81345200 629 methodTable(exec->vm())->putByIndex(this, exec, length, value, true);
93a37866 630 if (!exec->hadException())
ed1e77d3 631 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
93a37866
A
632 return;
633 }
634
635 putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
636 break;
637 }
638
639 case ArrayWithSlowPutArrayStorage: {
640 unsigned oldLength = length();
641 if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) {
642 if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
643 setLength(exec, oldLength + 1, true);
644 return;
645 }
81345200 646 FALLTHROUGH;
93a37866
A
647 }
648
649 case ArrayWithArrayStorage: {
650 ArrayStorage* storage = m_butterfly->arrayStorage();
651
652 // Fast case - push within vector, always update m_length & m_numValuesInVector.
653 unsigned length = storage->length();
654 if (length < storage->vectorLength()) {
655 storage->m_vector[length].set(exec->vm(), this, value);
656 storage->setLength(length + 1);
657 ++storage->m_numValuesInVector;
658 return;
659 }
660
661 // Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
662 if (storage->length() > MAX_ARRAY_INDEX) {
81345200 663 methodTable(exec->vm())->putByIndex(this, exec, storage->length(), value, true);
93a37866
A
664 // Per ES5.1 15.4.4.7 step 6 & 15.4.5.1 step 3.d.
665 if (!exec->hadException())
ed1e77d3 666 exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
93a37866
A
667 return;
668 }
669
670 // Handled the same as putIndex.
671 putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
672 break;
673 }
674
675 default:
676 RELEASE_ASSERT_NOT_REACHED();
677 }
14957cd0
A
678}
679
ed1e77d3
A
680JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
681{
682 auto arrayType = indexingType();
683 switch (arrayType) {
684 case ArrayWithDouble:
685 case ArrayWithInt32:
686 case ArrayWithContiguous: {
687 VM& vm = exec.vm();
688 if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm))
689 return nullptr;
690
691 Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(arrayType);
692 JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, count);
693 if (!resultArray)
694 return nullptr;
695
696 auto& resultButterfly = *resultArray->butterfly();
697 if (arrayType == ArrayWithDouble)
698 memcpy(resultButterfly.contiguousDouble().data(), m_butterfly->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
699 else
700 memcpy(resultButterfly.contiguous().data(), m_butterfly->contiguous().data() + startIndex, sizeof(JSValue) * count);
701 resultButterfly.setPublicLength(count);
702
703 return resultArray;
704 }
705 default:
706 return nullptr;
707 }
708}
709
710EncodedJSValue JSArray::fastConcatWith(ExecState& exec, JSArray& otherArray)
711{
712 auto newArrayType = indexingType();
713
714 VM& vm = exec.vm();
715 ASSERT(newArrayType == fastConcatType(vm, *this, otherArray));
716
717 unsigned thisArraySize = m_butterfly->publicLength();
718 unsigned otherArraySize = otherArray.m_butterfly->publicLength();
719 ASSERT(thisArraySize + otherArraySize < MIN_SPARSE_ARRAY_INDEX);
720
721 Structure* resultStructure = exec.lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(newArrayType);
722 JSArray* resultArray = JSArray::tryCreateUninitialized(vm, resultStructure, thisArraySize + otherArraySize);
723 if (!resultArray)
724 return JSValue::encode(throwOutOfMemoryError(&exec));
725
726 auto& resultButterfly = *resultArray->butterfly();
727 auto& otherButterfly = *otherArray.butterfly();
728 if (newArrayType == ArrayWithDouble) {
729 auto buffer = resultButterfly.contiguousDouble().data();
730 memcpy(buffer, m_butterfly->contiguousDouble().data(), sizeof(JSValue) * thisArraySize);
731 memcpy(buffer + thisArraySize, otherButterfly.contiguousDouble().data(), sizeof(JSValue) * otherArraySize);
732 } else {
733 auto buffer = resultButterfly.contiguous().data();
734 memcpy(buffer, m_butterfly->contiguous().data(), sizeof(JSValue) * thisArraySize);
735 memcpy(buffer + thisArraySize, otherButterfly.contiguous().data(), sizeof(JSValue) * otherArraySize);
736 }
737
738 resultButterfly.setPublicLength(thisArraySize + otherArraySize);
739 return JSValue::encode(resultArray);
740}
741
81345200 742bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
14957cd0 743{
93a37866
A
744 unsigned oldLength = storage->length();
745 RELEASE_ASSERT(count <= oldLength);
14957cd0 746
6fe7ccc8
A
747 // If the array contains holes or is otherwise in an abnormal state,
748 // use the generic algorithm in ArrayPrototype.
81345200 749 if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm))
ed1e77d3 750 || hasSparseMap()
81345200 751 || shouldUseSlowPut(indexingType())) {
6fe7ccc8 752 return false;
81345200 753 }
14957cd0 754
6fe7ccc8
A
755 if (!oldLength)
756 return true;
14957cd0 757
93a37866
A
758 unsigned length = oldLength - count;
759
6fe7ccc8 760 storage->m_numValuesInVector -= count;
93a37866
A
761 storage->setLength(length);
762
763 unsigned vectorLength = storage->vectorLength();
764 if (!vectorLength)
765 return true;
766
767 if (startIndex >= vectorLength)
768 return true;
769
770 if (startIndex + count > vectorLength)
771 count = vectorLength - startIndex;
772
773 unsigned usedVectorLength = min(vectorLength, oldLength);
774
81345200
A
775 unsigned numElementsBeforeShiftRegion = startIndex;
776 unsigned firstIndexAfterShiftRegion = startIndex + count;
777 unsigned numElementsAfterShiftRegion = usedVectorLength - firstIndexAfterShiftRegion;
778 ASSERT(numElementsBeforeShiftRegion + count + numElementsAfterShiftRegion == usedVectorLength);
779
780 // The point of this comparison seems to be to minimize the amount of elements that have to
781 // be moved during a shift operation.
782 if (numElementsBeforeShiftRegion < numElementsAfterShiftRegion) {
783 // The number of elements before the shift region is less than the number of elements
784 // after the shift region, so we move the elements before to the right.
785 if (numElementsBeforeShiftRegion) {
786 RELEASE_ASSERT(count + startIndex <= vectorLength);
787 if (storage->hasHoles()) {
788 for (unsigned i = startIndex; i-- > 0;) {
789 unsigned destinationIndex = count + i;
790 JSValue source = storage->m_vector[i].get();
791 JSValue dest = storage->m_vector[destinationIndex].get();
792 // Any time we overwrite a hole we know we overcounted the number of values we removed
793 // when we subtracted count from m_numValuesInVector above.
794 if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
795 storage->m_numValuesInVector++;
796 storage->m_vector[count + i].setWithoutWriteBarrier(source);
797 }
798 } else {
799 memmove(storage->m_vector + count,
93a37866
A
800 storage->m_vector,
801 sizeof(JSValue) * startIndex);
802 }
81345200
A
803 }
804 // Adjust the Butterfly and the index bias. We only need to do this here because we're changing
805 // the start of the Butterfly, which needs to point at the first indexed property in the used
806 // portion of the vector.
807 m_butterfly.setWithoutWriteBarrier(m_butterfly->shift(structure(), count));
808 storage = m_butterfly->arrayStorage();
809 storage->m_indexBias += count;
810
811 // Since we're consuming part of the vector by moving its beginning to the left,
812 // we need to modify the vector length appropriately.
813 storage->setVectorLength(vectorLength - count);
814 } else {
815 // The number of elements before the shift region is greater than or equal to the number
816 // of elements after the shift region, so we move the elements after the shift region to the left.
817 if (storage->hasHoles()) {
818 for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
819 unsigned destinationIndex = startIndex + i;
820 JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
821 JSValue dest = storage->m_vector[destinationIndex].get();
822 // Any time we overwrite a hole we know we overcounted the number of values we removed
823 // when we subtracted count from m_numValuesInVector above.
824 if (!dest && destinationIndex < firstIndexAfterShiftRegion)
825 storage->m_numValuesInVector++;
826 storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
827 }
93a37866 828 } else {
81345200
A
829 memmove(storage->m_vector + startIndex,
830 storage->m_vector + firstIndexAfterShiftRegion,
831 sizeof(JSValue) * numElementsAfterShiftRegion);
93a37866 832 }
81345200
A
833 // Clear the slots of the elements we just moved.
834 unsigned startOfEmptyVectorTail = usedVectorLength - count;
835 for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
836 storage->m_vector[i].clear();
837 // We don't modify the index bias or the Butterfly pointer in this case because we're not changing
838 // the start of the Butterfly, which needs to point at the first indexed property in the used
839 // portion of the vector. We also don't modify the vector length because we're not actually changing
840 // its length; we're just using less of it.
93a37866 841 }
81345200 842
93a37866
A
843 return true;
844}
845
81345200 846bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
93a37866 847{
81345200 848 VM& vm = exec->vm();
93a37866
A
849 RELEASE_ASSERT(count > 0);
850
81345200 851 switch (indexingType()) {
93a37866
A
852 case ArrayClass:
853 return true;
14957cd0 854
93a37866
A
855 case ArrayWithUndecided:
856 // Don't handle this because it's confusing and it shouldn't come up.
857 return false;
14957cd0 858
93a37866
A
859 case ArrayWithInt32:
860 case ArrayWithContiguous: {
861 unsigned oldLength = m_butterfly->publicLength();
862 RELEASE_ASSERT(count <= oldLength);
863
864 // We may have to walk the entire array to do the shift. We're willing to do
865 // so only if it's not horribly slow.
866 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
81345200 867 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
12899fa2
A
868
869 // Storing to a hole is fine since we're still having a good time. But reading from a hole
870 // is totally not fine, since we might have to read from the proto chain.
871 // We have to check for holes before we start moving things around so that we don't get halfway
872 // through shifting and then realize we should have been in ArrayStorage mode.
93a37866 873 unsigned end = oldLength - count;
81345200
A
874 if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
875 for (unsigned i = startIndex; i < end; ++i) {
876 JSValue v = m_butterfly->contiguous()[i + count].get();
877 if (UNLIKELY(!v)) {
878 startIndex = i;
879 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
880 }
881 m_butterfly->contiguous()[i].setWithoutWriteBarrier(v);
882 }
883 } else {
884 memmove(m_butterfly->contiguous().data() + startIndex,
885 m_butterfly->contiguous().data() + startIndex + count,
886 sizeof(JSValue) * (end - startIndex));
12899fa2
A
887 }
888
93a37866
A
889 for (unsigned i = end; i < oldLength; ++i)
890 m_butterfly->contiguous()[i].clear();
891
892 m_butterfly->setPublicLength(oldLength - count);
893 return true;
894 }
895
896 case ArrayWithDouble: {
897 unsigned oldLength = m_butterfly->publicLength();
898 RELEASE_ASSERT(count <= oldLength);
899
900 // We may have to walk the entire array to do the shift. We're willing to do
901 // so only if it's not horribly slow.
902 if (oldLength - (startIndex + count) >= MIN_SPARSE_ARRAY_INDEX)
81345200 903 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
12899fa2
A
904
905 // Storing to a hole is fine since we're still having a good time. But reading from a hole
906 // is totally not fine, since we might have to read from the proto chain.
907 // We have to check for holes before we start moving things around so that we don't get halfway
908 // through shifting and then realize we should have been in ArrayStorage mode.
93a37866 909 unsigned end = oldLength - count;
81345200
A
910 if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
911 for (unsigned i = startIndex; i < end; ++i) {
912 double v = m_butterfly->contiguousDouble()[i + count];
913 if (UNLIKELY(v != v)) {
914 startIndex = i;
915 return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
916 }
917 m_butterfly->contiguousDouble()[i] = v;
918 }
919 } else {
920 memmove(m_butterfly->contiguousDouble().data() + startIndex,
921 m_butterfly->contiguousDouble().data() + startIndex + count,
922 sizeof(JSValue) * (end - startIndex));
14957cd0 923 }
93a37866 924 for (unsigned i = end; i < oldLength; ++i)
81345200 925 m_butterfly->contiguousDouble()[i] = PNaN;
93a37866
A
926
927 m_butterfly->setPublicLength(oldLength - count);
928 return true;
929 }
930
931 case ArrayWithArrayStorage:
932 case ArrayWithSlowPutArrayStorage:
81345200 933 return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
93a37866
A
934
935 default:
936 CRASH();
937 return false;
14957cd0
A
938 }
939}
6fe7ccc8
A
940
941// Returns true if the unshift can be handled, false to fallback.
93a37866 942bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
14957cd0 943{
93a37866
A
944 unsigned length = storage->length();
945
946 RELEASE_ASSERT(startIndex <= length);
6fe7ccc8
A
947
948 // If the array contains holes or is otherwise in an abnormal state,
949 // use the generic algorithm in ArrayPrototype.
81345200 950 if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
6fe7ccc8
A
951 return false;
952
93a37866
A
953 bool moveFront = !startIndex || startIndex < length / 2;
954
955 unsigned vectorLength = storage->vectorLength();
a253471d 956
93a37866 957 if (moveFront && storage->m_indexBias >= count) {
81345200
A
958 Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
959 storage = newButterfly->arrayStorage();
93a37866
A
960 storage->m_indexBias -= count;
961 storage->setVectorLength(vectorLength + count);
81345200 962 setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
93a37866
A
963 } else if (!moveFront && vectorLength - length >= count)
964 storage = storage->butterfly()->arrayStorage();
965 else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
966 storage = arrayStorage();
967 else {
14957cd0 968 throwOutOfMemoryError(exec);
6fe7ccc8 969 return true;
14957cd0 970 }
93a37866
A
971
972 WriteBarrier<Unknown>* vector = storage->m_vector;
973
974 if (startIndex) {
975 if (moveFront)
976 memmove(vector, vector + count, startIndex * sizeof(JSValue));
977 else if (length - startIndex)
978 memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
979 }
980
981 for (unsigned i = 0; i < count; i++)
982 vector[i + startIndex].clear();
983 return true;
984}
985
986bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
987{
81345200 988 switch (indexingType()) {
93a37866
A
989 case ArrayClass:
990 case ArrayWithUndecided:
991 // We could handle this. But it shouldn't ever come up, so we won't.
992 return false;
993
994 case ArrayWithInt32:
995 case ArrayWithContiguous: {
996 unsigned oldLength = m_butterfly->publicLength();
997
998 // We may have to walk the entire array to do the unshift. We're willing to do so
999 // only if it's not horribly slow.
1000 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
1001 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1002
1003 ensureLength(exec->vm(), oldLength + count);
12899fa2
A
1004
1005 // We have to check for holes before we start moving things around so that we don't get halfway
1006 // through shifting and then realize we should have been in ArrayStorage mode.
93a37866
A
1007 for (unsigned i = oldLength; i-- > startIndex;) {
1008 JSValue v = m_butterfly->contiguous()[i].get();
1009 if (UNLIKELY(!v))
1010 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
12899fa2
A
1011 }
1012
1013 for (unsigned i = oldLength; i-- > startIndex;) {
1014 JSValue v = m_butterfly->contiguous()[i].get();
1015 ASSERT(v);
93a37866
A
1016 m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
1017 }
1018
1019 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1020 // of. This is fine because the caller is required to store over that area, and
1021 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1022 // as storing over an existing element.
1023
1024 return true;
1025 }
1026
1027 case ArrayWithDouble: {
1028 unsigned oldLength = m_butterfly->publicLength();
1029
1030 // We may have to walk the entire array to do the unshift. We're willing to do so
1031 // only if it's not horribly slow.
1032 if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX)
1033 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
1034
1035 ensureLength(exec->vm(), oldLength + count);
1036
12899fa2
A
1037 // We have to check for holes before we start moving things around so that we don't get halfway
1038 // through shifting and then realize we should have been in ArrayStorage mode.
93a37866
A
1039 for (unsigned i = oldLength; i-- > startIndex;) {
1040 double v = m_butterfly->contiguousDouble()[i];
1041 if (UNLIKELY(v != v))
1042 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
12899fa2
A
1043 }
1044
1045 for (unsigned i = oldLength; i-- > startIndex;) {
1046 double v = m_butterfly->contiguousDouble()[i];
1047 ASSERT(v == v);
93a37866
A
1048 m_butterfly->contiguousDouble()[i + count] = v;
1049 }
1050
1051 // NOTE: we're leaving being garbage in the part of the array that we shifted out
1052 // of. This is fine because the caller is required to store over that area, and
1053 // in contiguous mode storing into a hole is guaranteed to behave exactly the same
1054 // as storing over an existing element.
1055
1056 return true;
1057 }
1058
1059 case ArrayWithArrayStorage:
1060 case ArrayWithSlowPutArrayStorage:
1061 return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
1062
1063 default:
1064 CRASH();
1065 return false;
1066 }
9dae56ea
A
1067}
1068
ba379fdc 1069void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
9dae56ea 1070{
9dae56ea 1071 unsigned i = 0;
93a37866
A
1072 unsigned vectorEnd;
1073 WriteBarrier<Unknown>* vector;
1074
81345200 1075 switch (indexingType()) {
93a37866
A
1076 case ArrayClass:
1077 return;
1078
1079 case ArrayWithUndecided: {
1080 vector = 0;
1081 vectorEnd = 0;
1082 break;
1083 }
1084
1085 case ArrayWithInt32:
1086 case ArrayWithContiguous: {
1087 vectorEnd = m_butterfly->publicLength();
1088 vector = m_butterfly->contiguous().data();
1089 break;
1090 }
1091
1092 case ArrayWithDouble: {
1093 vector = 0;
1094 vectorEnd = 0;
1095 for (; i < m_butterfly->publicLength(); ++i) {
1096 double v = butterfly()->contiguousDouble()[i];
1097 if (v != v)
1098 break;
1099 args.append(JSValue(JSValue::EncodeAsDouble, v));
1100 }
1101 break;
1102 }
1103
1104 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1105 ArrayStorage* storage = m_butterfly->arrayStorage();
1106
1107 vector = storage->m_vector;
1108 vectorEnd = min(storage->length(), storage->vectorLength());
1109 break;
1110 }
1111
1112 default:
1113 CRASH();
ed1e77d3 1114#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
93a37866
A
1115 vector = 0;
1116 vectorEnd = 0;
1117 break;
ed1e77d3 1118#endif
93a37866
A
1119 }
1120
f9bf01c6 1121 for (; i < vectorEnd; ++i) {
14957cd0 1122 WriteBarrier<Unknown>& v = vector[i];
f9bf01c6
A
1123 if (!v)
1124 break;
14957cd0 1125 args.append(v.get());
f9bf01c6 1126 }
ed1e77d3
A
1127
1128 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
93a37866 1129 for (; i < length(); ++i)
9dae56ea
A
1130 args.append(get(exec, i));
1131}
1132
ed1e77d3 1133void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
ba379fdc 1134{
ed1e77d3 1135 unsigned i = offset;
93a37866
A
1136 WriteBarrier<Unknown>* vector;
1137 unsigned vectorEnd;
ed1e77d3
A
1138 length += offset; // We like to think of the length as being our length, rather than the output length.
1139
1140 // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
93a37866 1141 ASSERT(length == this->length());
ed1e77d3 1142
81345200 1143 switch (indexingType()) {
93a37866
A
1144 case ArrayClass:
1145 return;
1146
1147 case ArrayWithUndecided: {
1148 vector = 0;
1149 vectorEnd = 0;
1150 break;
1151 }
1152
1153 case ArrayWithInt32:
1154 case ArrayWithContiguous: {
1155 vector = m_butterfly->contiguous().data();
1156 vectorEnd = m_butterfly->publicLength();
1157 break;
1158 }
1159
1160 case ArrayWithDouble: {
1161 vector = 0;
1162 vectorEnd = 0;
1163 for (; i < m_butterfly->publicLength(); ++i) {
1164 ASSERT(i < butterfly()->vectorLength());
1165 double v = m_butterfly->contiguousDouble()[i];
1166 if (v != v)
1167 break;
ed1e77d3 1168 exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
93a37866
A
1169 }
1170 break;
1171 }
1172
1173 case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
1174 ArrayStorage* storage = m_butterfly->arrayStorage();
1175 vector = storage->m_vector;
1176 vectorEnd = min(length, storage->vectorLength());
1177 break;
1178 }
1179
1180 default:
1181 CRASH();
ed1e77d3 1182#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
93a37866
A
1183 vector = 0;
1184 vectorEnd = 0;
1185 break;
ed1e77d3 1186#endif
93a37866
A
1187 }
1188
f9bf01c6 1189 for (; i < vectorEnd; ++i) {
14957cd0 1190 WriteBarrier<Unknown>& v = vector[i];
f9bf01c6
A
1191 if (!v)
1192 break;
ed1e77d3 1193 exec->r(firstElementDest + i - offset) = v.get();
f9bf01c6 1194 }
93a37866 1195
ed1e77d3
A
1196 for (; i < length; ++i) {
1197 exec->r(firstElementDest + i - offset) = get(exec, i);
1198 if (UNLIKELY(exec->vm().exception()))
1199 return;
9dae56ea 1200 }
93a37866 1201}
9dae56ea 1202
9dae56ea 1203} // namespace JSC