summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSArray.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/JSArray.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSArray.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/JSArray.cpp1337
1 files changed, 517 insertions, 820 deletions
diff --git a/Source/JavaScriptCore/runtime/JSArray.cpp b/Source/JavaScriptCore/runtime/JSArray.cpp
index 9007b5f7d..efd79dd8c 100644
--- a/Source/JavaScriptCore/runtime/JSArray.cpp
+++ b/Source/JavaScriptCore/runtime/JSArray.cpp
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- * Copyright (C) 2003, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2017 Apple Inc. All rights reserved.
* Copyright (C) 2003 Peter Kelly (pmk@post.com)
* Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
*
@@ -25,28 +25,26 @@
#include "ArrayPrototype.h"
#include "ButterflyInlines.h"
-#include "CachedCall.h"
-#include "CopiedSpace.h"
-#include "CopiedSpaceInlines.h"
+#include "CodeBlock.h"
#include "Error.h"
-#include "Executable.h"
#include "GetterSetter.h"
#include "IndexingHeaderInlines.h"
+#include "JSArrayInlines.h"
+#include "JSCInlines.h"
#include "PropertyNameArray.h"
-#include "Reject.h"
-#include <wtf/AVLTree.h>
+#include "TypeError.h"
#include <wtf/Assertions.h>
-#include <wtf/OwnPtr.h>
-#include <Operations.h>
using namespace std;
using namespace WTF;
namespace JSC {
+static const char* const LengthExceededTheMaximumArrayLengthError = "Length exceeded the maximum array length";
+
STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSArray);
-const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, 0, CREATE_METHOD_TABLE(JSArray)};
+const ClassInfo JSArray::s_info = {"Array", &JSNonFinalObject::s_info, 0, CREATE_METHOD_TABLE(JSArray)};
Butterfly* createArrayButterflyInDictionaryIndexingMode(
VM& vm, JSCell* intendedOwner, unsigned initialLength)
@@ -62,6 +60,54 @@ Butterfly* createArrayButterflyInDictionaryIndexingMode(
return butterfly;
}
+JSArray* JSArray::tryCreateForInitializationPrivate(VM& vm, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
+{
+ if (UNLIKELY(initialLength > MAX_STORAGE_VECTOR_LENGTH))
+ return 0;
+
+ unsigned outOfLineStorage = structure->outOfLineCapacity();
+
+ Butterfly* butterfly;
+ IndexingType indexingType = structure->indexingType();
+ if (LIKELY(!hasAnyArrayStorage(indexingType))) {
+ ASSERT(
+ hasUndecided(indexingType)
+ || hasInt32(indexingType)
+ || hasDouble(indexingType)
+ || hasContiguous(indexingType));
+
+ unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, initialLength);
+ void* temp = vm.auxiliarySpace.tryAllocate(deferralContext, Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)));
+ if (UNLIKELY(!temp))
+ return nullptr;
+ butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
+ butterfly->setVectorLength(vectorLength);
+ butterfly->setPublicLength(initialLength);
+ if (hasDouble(indexingType)) {
+ for (unsigned i = initialLength; i < vectorLength; ++i)
+ butterfly->contiguousDouble()[i] = PNaN;
+ } else {
+ for (unsigned i = initialLength; i < vectorLength; ++i)
+ butterfly->contiguous()[i].clear();
+ }
+ } else {
+ unsigned vectorLength = ArrayStorage::optimalVectorLength(0, structure, initialLength);
+ void* temp = vm.auxiliarySpace.tryAllocate(deferralContext, Butterfly::totalSize(0, outOfLineStorage, true, ArrayStorage::sizeFor(vectorLength)));
+ if (UNLIKELY(!temp))
+ return nullptr;
+ butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
+ *butterfly->indexingHeader() = indexingHeaderForArrayStorage(initialLength, vectorLength);
+ ArrayStorage* storage = butterfly->arrayStorage();
+ storage->m_indexBias = 0;
+ storage->m_sparseMap.clear();
+ storage->m_numValuesInVector = initialLength;
+ for (unsigned i = initialLength; i < vectorLength; ++i)
+ storage->m_vector[i].clear();
+ }
+
+ return createWithButterfly(vm, deferralContext, structure, butterfly);
+}
+
void JSArray::setLengthWritable(ExecState* exec, bool writable)
{
ASSERT(isLengthWritable() || !writable);
@@ -78,25 +124,28 @@ void JSArray::setLengthWritable(ExecState* exec, bool writable)
// Defined in ES5.1 15.4.5.1
bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
JSArray* array = jsCast<JSArray*>(object);
// 3. If P is "length", then
- if (propertyName == exec->propertyNames().length) {
+ if (propertyName == vm.propertyNames->length) {
// All paths through length definition call the default [[DefineOwnProperty]], hence:
// from ES5.1 8.12.9 7.a.
if (descriptor.configurablePresent() && descriptor.configurable())
- return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property.");
+ return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError));
// from ES5.1 8.12.9 7.b.
if (descriptor.enumerablePresent() && descriptor.enumerable())
- return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property.");
+ return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError));
// 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.");
+ return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError));
// from ES5.1 8.12.9 10.a.
if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable())
- return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property.");
+ return typeError(exec, scope, throwException, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError));
// This descriptor is either just making length read-only, or changing nothing!
if (!descriptor.value()) {
if (descriptor.writablePresent())
@@ -109,11 +158,12 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
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)) {
- exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+ JSC::throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
return false;
}
// Based on SameValue check in 8.12.9, this is always okay.
+ // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
if (newLen == array->length()) {
if (descriptor.writablePresent())
array->setLengthWritable(exec, descriptor.writable());
@@ -125,7 +175,7 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
// 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.");
+ return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyChangeError));
// h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
// i. Else,
@@ -138,7 +188,9 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
// 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
- if (!array->setLength(exec, newLen, throwException)) {
+ bool success = array->setLength(exec, newLen, throwException);
+ ASSERT(!scope.exception() || !success);
+ if (!success) {
// 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.
@@ -160,20 +212,23 @@ bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName
// 4. Else if P is an array index (15.4), then
// a. Let index be ToUint32(P).
- unsigned index = propertyName.asIndex();
- if (index != PropertyName::NotAnIndex) {
+ if (std::optional<uint32_t> optionalIndex = parseIndex(propertyName)) {
// b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false.
+ uint32_t index = optionalIndex.value();
+ // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case.
if (index >= array->length() && !array->isLengthWritable())
- return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property.");
+ return typeError(exec, scope, throwException, ASCIILiteral("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.
+ scope.release();
return array->defineOwnIndexedProperty(exec, index, descriptor, throwException);
}
+ scope.release();
return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
}
@@ -190,21 +245,31 @@ bool JSArray::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName
}
// ECMA 15.4.5.1
-void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
JSArray* thisObject = jsCast<JSArray*>(cell);
+ if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
+ scope.release();
+ return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
+ }
+
if (propertyName == exec->propertyNames().length) {
unsigned newLength = value.toUInt32(exec);
+ RETURN_IF_EXCEPTION(scope, false);
if (value.toNumber(exec) != static_cast<double>(newLength)) {
- exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length")));
- return;
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral("Invalid array length")));
+ return false;
}
- thisObject->setLength(exec, newLength, slot.isStrictMode());
- return;
+ scope.release();
+ return thisObject->setLength(exec, newLength, slot.isStrictMode());
}
- JSObject::put(thisObject, exec, propertyName, value, slot);
+ scope.release();
+ return JSObject::put(thisObject, exec, propertyName, value, slot);
}
bool JSArray::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
@@ -228,20 +293,21 @@ void JSArray::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, Pro
{
JSArray* thisObject = jsCast<JSArray*>(object);
- if (mode == IncludeDontEnumProperties)
+ if (mode.includeDontEnumProperties())
propertyNames.add(exec->propertyNames().length);
JSObject::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
}
// This method makes room in the vector, but leaves the new space for count slots uncleared.
-bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
+bool JSArray::unshiftCountSlowCase(const AbstractLocker&, VM& vm, DeferGC&, bool addToFront, unsigned count)
{
ArrayStorage* storage = ensureArrayStorage(vm);
Butterfly* butterfly = storage->butterfly();
- unsigned propertyCapacity = structure()->outOfLineCapacity();
- unsigned propertySize = structure()->outOfLineSize();
-
+ Structure* structure = this->structure(vm);
+ unsigned propertyCapacity = structure->outOfLineCapacity();
+ unsigned propertySize = structure->outOfLineSize();
+
// If not, we should have handled this on the fast path.
ASSERT(!addToFront || count > storage->m_indexBias);
@@ -253,7 +319,8 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
// * desiredCapacity - how large should we like to grow the vector to - based on 2x requiredVectorLength.
unsigned length = storage->length();
- unsigned usedVectorLength = min(storage->vectorLength(), length);
+ unsigned oldVectorLength = storage->vectorLength();
+ unsigned usedVectorLength = min(oldVectorLength, length);
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)
@@ -264,23 +331,29 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
ASSERT(storage->vectorLength() <= MAX_STORAGE_VECTOR_LENGTH && (MAX_STORAGE_VECTOR_LENGTH - storage->vectorLength()) >= storage->m_indexBias);
unsigned currentCapacity = storage->vectorLength() + storage->m_indexBias;
// 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);
+ // FIXME: This code should be fixed to avoid internal fragmentation. It's not super high
+ // priority since increaseVectorLength() will "fix" any mistakes we make, but it would be cool
+ // to get this right eventually.
+ unsigned desiredCapacity = min(MAX_STORAGE_VECTOR_LENGTH, max(BASE_ARRAY_STORAGE_VECTOR_LEN, requiredVectorLength) << 1);
// Step 2:
// We're either going to choose to allocate a new ArrayStorage, or we're going to reuse the existing one.
- DeferGC deferGC(vm.heap);
void* newAllocBase = 0;
unsigned newStorageCapacity;
+ bool allocatedNewStorage;
// If the current storage array is sufficiently large (but not too large!) then just keep using it.
if (currentCapacity > desiredCapacity && isDenseEnoughForVector(currentCapacity, requiredVectorLength)) {
- newAllocBase = butterfly->base(structure());
+ newAllocBase = butterfly->base(structure);
newStorageCapacity = currentCapacity;
+ allocatedNewStorage = false;
} else {
size_t newSize = Butterfly::totalSize(0, propertyCapacity, true, ArrayStorage::sizeFor(desiredCapacity));
- if (!vm.heap.tryAllocateStorage(this, newSize, &newAllocBase))
+ newAllocBase = vm.auxiliarySpace.tryAllocate(newSize);
+ if (!newAllocBase)
return false;
newStorageCapacity = desiredCapacity;
+ allocatedNewStorage = true;
}
// Step 3:
@@ -298,7 +371,7 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
// Atomic decay, + the post-capacity cannot be greater than what is available.
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);
+ ASSERT(newAllocBase != butterfly->base(structure) || postCapacity < storage->vectorLength() - length);
}
unsigned newVectorLength = requiredVectorLength + postCapacity;
@@ -310,33 +383,43 @@ bool JSArray::unshiftCountSlowCase(VM& vm, bool addToFront, unsigned count)
ASSERT(count + usedVectorLength <= newVectorLength);
memmove(newButterfly->arrayStorage()->m_vector + count, storage->m_vector, sizeof(JSValue) * usedVectorLength);
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)) {
+
+ if (allocatedNewStorage) {
+ // We will set the vectorLength to newVectorLength. We populated requiredVectorLength
+ // (usedVectorLength + count), which is less. Clear the difference.
+ for (unsigned i = requiredVectorLength; i < newVectorLength; ++i)
+ newButterfly->arrayStorage()->m_vector[i].clear();
+ }
+ } 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));
memmove(newButterfly->arrayStorage()->m_vector, storage->m_vector, sizeof(JSValue) * usedVectorLength);
-
- WriteBarrier<Unknown>* newVector = newButterfly->arrayStorage()->m_vector;
+
for (unsigned i = requiredVectorLength; i < newVectorLength; i++)
- newVector[i].clear();
+ newButterfly->arrayStorage()->m_vector[i].clear();
}
newButterfly->arrayStorage()->setVectorLength(newVectorLength);
newButterfly->arrayStorage()->m_indexBias = newIndexBias;
- setButterflyWithoutChangingStructure(vm, newButterfly);
+
+ setButterfly(vm, newButterfly);
return true;
}
bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, bool throwException, ArrayStorage* storage)
{
- unsigned length = storage->length();
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ unsigned length = storage->length();
+
// If the length is read only then we enter sparse mode, so should enter the following 'if'.
ASSERT(isLengthWritable() || storage->m_sparseMap);
if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
// Fail if the length is not writable.
if (map->lengthIsReadOnly())
- return reject(exec, throwException, StrictModeReadonlyPropertyWriteError);
+ return typeError(exec, scope, throwException, ASCIILiteral(ReadonlyPropertyWriteError));
if (newLength < length) {
// Copy any keys we might be interested in into a vector.
@@ -361,7 +444,7 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
ASSERT(it != map->notFound());
if (it->value.attributes & DontDelete) {
storage->setLength(index + 1);
- return reject(exec, throwException, "Unable to delete property.");
+ return typeError(exec, scope, throwException, ASCIILiteral(UnableToDeletePropertyError));
}
map->remove(it);
}
@@ -379,7 +462,7 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
unsigned usedVectorLength = min(length, storage->vectorLength());
for (unsigned i = newLength; i < usedVectorLength; ++i) {
WriteBarrier<Unknown>& valueSlot = storage->m_vector[i];
- bool hadValue = valueSlot;
+ bool hadValue = !!valueSlot;
valueSlot.clear();
storage->m_numValuesInVector -= hadValue;
}
@@ -390,49 +473,118 @@ bool JSArray::setLengthWithArrayStorage(ExecState* exec, unsigned newLength, boo
return true;
}
+bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JSArray* otherArray)
+{
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (!canFastCopy(vm, otherArray))
+ return false;
+
+ IndexingType type = indexingType();
+ IndexingType copyType = mergeIndexingTypeForCopying(otherArray->indexingType());
+ if (type == ArrayWithUndecided && copyType != NonArray) {
+ if (copyType == ArrayWithInt32)
+ convertUndecidedToInt32(vm);
+ else if (copyType == ArrayWithDouble)
+ convertUndecidedToDouble(vm);
+ else if (copyType == ArrayWithContiguous)
+ convertUndecidedToContiguous(vm);
+ else {
+ ASSERT(copyType == ArrayWithUndecided);
+ return true;
+ }
+ } else if (type != copyType)
+ return false;
+
+ unsigned otherLength = otherArray->length();
+ Checked<unsigned, RecordOverflow> checkedNewLength = startIndex;
+ checkedNewLength += otherLength;
+
+ unsigned newLength;
+ if (checkedNewLength.safeGet(newLength) == CheckedState::DidOverflow) {
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
+ return false;
+ }
+
+ if (newLength >= MIN_SPARSE_ARRAY_INDEX)
+ return false;
+
+ if (!ensureLength(vm, newLength)) {
+ throwOutOfMemoryError(exec, scope);
+ return false;
+ }
+ ASSERT(copyType == indexingType());
+
+ if (type == ArrayWithDouble)
+ memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
+ else
+ memcpy(butterfly()->contiguous().data() + startIndex, otherArray->butterfly()->contiguous().data(), sizeof(JSValue) * otherLength);
+
+ return true;
+}
+
bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException)
{
- switch (structure()->indexingType()) {
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Butterfly* butterfly = m_butterfly.get();
+ switch (indexingType()) {
case ArrayClass:
if (!newLength)
return true;
if (newLength >= MIN_SPARSE_ARRAY_INDEX) {
+ scope.release();
return setLengthWithArrayStorage(
exec, newLength, throwException,
- convertContiguousToArrayStorage(exec->vm()));
+ ensureArrayStorage(vm));
}
- createInitialUndecided(exec->vm(), newLength);
+ createInitialUndecided(vm, newLength);
return true;
case ArrayWithUndecided:
case ArrayWithInt32:
case ArrayWithDouble:
- case ArrayWithContiguous:
- if (newLength == m_butterfly->publicLength())
+ case ArrayWithContiguous: {
+ if (newLength == butterfly->publicLength())
return true;
if (newLength >= MAX_ARRAY_INDEX // This case ensures that we can do fast push.
|| (newLength >= MIN_SPARSE_ARRAY_INDEX
&& !isDenseEnoughForVector(newLength, countElements()))) {
+ scope.release();
return setLengthWithArrayStorage(
exec, newLength, throwException,
- ensureArrayStorage(exec->vm()));
+ ensureArrayStorage(vm));
}
- if (newLength > m_butterfly->publicLength()) {
- ensureLength(exec->vm(), newLength);
+ if (newLength > butterfly->publicLength()) {
+ if (!ensureLength(vm, newLength)) {
+ throwOutOfMemoryError(exec, scope);
+ return false;
+ }
+ return true;
+ }
+
+ unsigned lengthToClear = butterfly->publicLength() - newLength;
+ unsigned costToAllocateNewButterfly = 64; // a heuristic.
+ if (lengthToClear > newLength && lengthToClear > costToAllocateNewButterfly) {
+ reallocateAndShrinkButterfly(vm, newLength);
return true;
}
- if (structure()->indexingType() == ArrayWithDouble) {
- for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
- m_butterfly->contiguousDouble()[i] = QNaN;
+
+ if (indexingType() == ArrayWithDouble) {
+ for (unsigned i = butterfly->publicLength(); i-- > newLength;)
+ butterfly->contiguousDouble()[i] = PNaN;
} else {
- for (unsigned i = m_butterfly->publicLength(); i-- > newLength;)
- m_butterfly->contiguous()[i].clear();
+ for (unsigned i = butterfly->publicLength(); i-- > newLength;)
+ butterfly->contiguous()[i].clear();
}
- m_butterfly->setPublicLength(newLength);
+ butterfly->setPublicLength(newLength);
return true;
+ }
case ArrayWithArrayStorage:
case ArrayWithSlowPutArrayStorage:
+ scope.release();
return setLengthWithArrayStorage(exec, newLength, throwException, arrayStorage());
default:
@@ -443,56 +595,61 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
JSValue JSArray::pop(ExecState* exec)
{
- switch (structure()->indexingType()) {
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Butterfly* butterfly = m_butterfly.get();
+
+ switch (indexingType()) {
case ArrayClass:
return jsUndefined();
case ArrayWithUndecided:
- if (!m_butterfly->publicLength())
+ if (!butterfly->publicLength())
return jsUndefined();
// We have nothing but holes. So, drop down to the slow version.
break;
case ArrayWithInt32:
case ArrayWithContiguous: {
- unsigned length = m_butterfly->publicLength();
+ unsigned length = butterfly->publicLength();
if (!length--)
return jsUndefined();
- RELEASE_ASSERT(length < m_butterfly->vectorLength());
- JSValue value = m_butterfly->contiguous()[length].get();
+ RELEASE_ASSERT(length < butterfly->vectorLength());
+ JSValue value = butterfly->contiguous()[length].get();
if (value) {
- m_butterfly->contiguous()[length].clear();
- m_butterfly->setPublicLength(length);
+ butterfly->contiguous()[length].clear();
+ butterfly->setPublicLength(length);
return value;
}
break;
}
case ArrayWithDouble: {
- unsigned length = m_butterfly->publicLength();
+ unsigned length = butterfly->publicLength();
if (!length--)
return jsUndefined();
- RELEASE_ASSERT(length < m_butterfly->vectorLength());
- double value = m_butterfly->contiguousDouble()[length];
+ RELEASE_ASSERT(length < butterfly->vectorLength());
+ double value = butterfly->contiguousDouble()[length];
if (value == value) {
- m_butterfly->contiguousDouble()[length] = QNaN;
- m_butterfly->setPublicLength(length);
+ butterfly->contiguousDouble()[length] = PNaN;
+ butterfly->setPublicLength(length);
return JSValue(JSValue::EncodeAsDouble, value);
}
break;
}
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
- ArrayStorage* storage = m_butterfly->arrayStorage();
+ ArrayStorage* storage = butterfly->arrayStorage();
unsigned length = storage->length();
if (!length) {
if (!isLengthWritable())
- throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ throwTypeError(exec, scope, ASCIILiteral(ReadonlyPropertyWriteError));
return jsUndefined();
}
@@ -520,14 +677,16 @@ JSValue JSArray::pop(ExecState* exec)
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();
+ RETURN_IF_EXCEPTION(scope, JSValue());
// Call the [[Delete]] internal method of O with arguments indx and true.
- if (!deletePropertyByIndex(this, exec, index)) {
- throwTypeError(exec, "Unable to delete property.");
+ bool success = deletePropertyByIndex(this, exec, index);
+ RETURN_IF_EXCEPTION(scope, JSValue());
+ if (!success) {
+ throwTypeError(exec, scope, ASCIILiteral(UnableToDeletePropertyError));
return jsUndefined();
}
// Call the [[Put]] internal method of O with arguments "length", indx, and true.
+ scope.release();
setLength(exec, index, true);
// Return element.
return element;
@@ -538,130 +697,146 @@ JSValue JSArray::pop(ExecState* exec)
// - pushing to an array of length 2^32-1 stores the property, but throws a range error.
void JSArray::push(ExecState* exec, JSValue value)
{
- switch (structure()->indexingType()) {
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Butterfly* butterfly = m_butterfly.get();
+
+ switch (indexingType()) {
case ArrayClass: {
- createInitialUndecided(exec->vm(), 0);
+ createInitialUndecided(vm, 0);
FALLTHROUGH;
}
case ArrayWithUndecided: {
- convertUndecidedForValue(exec->vm(), value);
+ convertUndecidedForValue(vm, value);
+ scope.release();
push(exec, value);
return;
}
case ArrayWithInt32: {
if (!value.isInt32()) {
- convertInt32ForValue(exec->vm(), value);
+ convertInt32ForValue(vm, value);
+ scope.release();
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);
+ unsigned length = butterfly->publicLength();
+ ASSERT(length <= butterfly->vectorLength());
+ if (length < butterfly->vectorLength()) {
+ butterfly->contiguousInt32()[length].setWithoutWriteBarrier(value);
+ butterfly->setPublicLength(length + 1);
return;
}
- if (length > MAX_ARRAY_INDEX) {
- methodTable()->putByIndex(this, exec, length, value, true);
- if (!exec->hadException())
- exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+ if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+ methodTable(vm)->putByIndex(this, exec, length, value, true);
+ if (!scope.exception())
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
return;
}
-
+
+ scope.release();
putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, length, value);
return;
}
case ArrayWithContiguous: {
- unsigned length = m_butterfly->publicLength();
- ASSERT(length <= m_butterfly->vectorLength());
- if (length < m_butterfly->vectorLength()) {
- m_butterfly->contiguous()[length].set(exec->vm(), this, value);
- m_butterfly->setPublicLength(length + 1);
+ unsigned length = butterfly->publicLength();
+ ASSERT(length <= butterfly->vectorLength());
+ if (length < butterfly->vectorLength()) {
+ butterfly->contiguous()[length].set(vm, this, value);
+ butterfly->setPublicLength(length + 1);
return;
}
- if (length > MAX_ARRAY_INDEX) {
- methodTable()->putByIndex(this, exec, length, value, true);
- if (!exec->hadException())
- exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+ if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+ methodTable(vm)->putByIndex(this, exec, length, value, true);
+ if (!scope.exception())
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
return;
}
-
+
+ scope.release();
putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, length, value);
return;
}
case ArrayWithDouble: {
if (!value.isNumber()) {
- convertDoubleToContiguous(exec->vm());
+ convertDoubleToContiguous(vm);
+ scope.release();
push(exec, value);
return;
}
double valueAsDouble = value.asNumber();
if (valueAsDouble != valueAsDouble) {
- convertDoubleToContiguous(exec->vm());
+ convertDoubleToContiguous(vm);
+ scope.release();
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);
+ unsigned length = butterfly->publicLength();
+ ASSERT(length <= butterfly->vectorLength());
+ if (length < butterfly->vectorLength()) {
+ butterfly->contiguousDouble()[length] = valueAsDouble;
+ butterfly->setPublicLength(length + 1);
return;
}
- if (length > MAX_ARRAY_INDEX) {
- methodTable()->putByIndex(this, exec, length, value, true);
- if (!exec->hadException())
- exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+ if (UNLIKELY(length > MAX_ARRAY_INDEX)) {
+ methodTable(vm)->putByIndex(this, exec, length, value, true);
+ if (!scope.exception())
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
return;
}
-
+
+ scope.release();
putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, length, value);
- break;
+ return;
}
case ArrayWithSlowPutArrayStorage: {
unsigned oldLength = length();
- if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true)) {
- if (!exec->hadException() && oldLength < 0xFFFFFFFFu)
+ bool putResult = false;
+ if (attemptToInterceptPutByIndexOnHole(exec, oldLength, value, true, putResult)) {
+ if (!scope.exception() && oldLength < 0xFFFFFFFFu) {
+ scope.release();
setLength(exec, oldLength + 1, true);
+ }
return;
}
FALLTHROUGH;
}
case ArrayWithArrayStorage: {
- ArrayStorage* storage = m_butterfly->arrayStorage();
+ ArrayStorage* storage = butterfly->arrayStorage();
// Fast case - push within vector, always update m_length & m_numValuesInVector.
unsigned length = storage->length();
if (length < storage->vectorLength()) {
- storage->m_vector[length].set(exec->vm(), this, value);
+ storage->m_vector[length].set(vm, this, value);
storage->setLength(length + 1);
++storage->m_numValuesInVector;
return;
}
// Pushing to an array of invalid length (2^31-1) stores the property, but throws a range error.
- if (storage->length() > MAX_ARRAY_INDEX) {
- methodTable()->putByIndex(this, exec, storage->length(), value, true);
+ if (UNLIKELY(storage->length() > MAX_ARRAY_INDEX)) {
+ methodTable(vm)->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())
- exec->vm().throwException(exec, createRangeError(exec, "Invalid array length"));
+ if (!scope.exception())
+ throwException(exec, scope, createRangeError(exec, ASCIILiteral(LengthExceededTheMaximumArrayLengthError)));
return;
}
// Handled the same as putIndex.
+ scope.release();
putByIndexBeyondVectorLengthWithArrayStorage(exec, storage->length(), value, true, storage);
- break;
+ return;
}
default:
@@ -669,15 +844,53 @@ void JSArray::push(ExecState* exec, JSValue value)
}
}
-bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, ArrayStorage* storage)
+JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
+{
+ auto arrayType = indexingType();
+ switch (arrayType) {
+ case ArrayWithDouble:
+ case ArrayWithInt32:
+ case ArrayWithContiguous: {
+ VM& vm = exec.vm();
+ if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm))
+ return nullptr;
+
+ JSGlobalObject* lexicalGlobalObject = exec.lexicalGlobalObject();
+ Structure* resultStructure = lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(arrayType);
+ if (UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType())))
+ return nullptr;
+
+ ASSERT(!lexicalGlobalObject->isHavingABadTime());
+ JSArray* resultArray = JSArray::tryCreateForInitializationPrivate(vm, resultStructure, count);
+ if (UNLIKELY(!resultArray))
+ return nullptr;
+
+ auto& resultButterfly = *resultArray->butterfly();
+ if (arrayType == ArrayWithDouble)
+ memcpy(resultButterfly.contiguousDouble().data(), m_butterfly.get()->contiguousDouble().data() + startIndex, sizeof(JSValue) * count);
+ else
+ memcpy(resultButterfly.contiguous().data(), m_butterfly.get()->contiguous().data() + startIndex, sizeof(JSValue) * count);
+ resultButterfly.setPublicLength(count);
+
+ return resultArray;
+ }
+ default:
+ return nullptr;
+ }
+}
+
+bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
{
unsigned oldLength = storage->length();
RELEASE_ASSERT(count <= oldLength);
// If the array contains holes or is otherwise in an abnormal state,
// use the generic algorithm in ArrayPrototype.
- if (oldLength != storage->m_numValuesInVector || inSparseIndexingMode() || shouldUseSlowPut(structure()->indexingType()))
+ if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm))
+ || hasSparseMap()
+ || shouldUseSlowPut(indexingType())) {
return false;
+ }
if (!oldLength)
return true;
@@ -694,6 +907,9 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
if (startIndex >= vectorLength)
return true;
+ DisallowGC disallowGC;
+ auto locker = holdLock(*this);
+
if (startIndex + count > vectorLength)
count = vectorLength - startIndex;
@@ -711,16 +927,29 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
// after the shift region, so we move the elements before to the right.
if (numElementsBeforeShiftRegion) {
RELEASE_ASSERT(count + startIndex <= vectorLength);
- memmove(
- storage->m_vector + count,
- storage->m_vector,
- sizeof(JSValue) * startIndex);
+ if (storage->hasHoles()) {
+ for (unsigned i = startIndex; i-- > 0;) {
+ unsigned destinationIndex = count + i;
+ JSValue source = storage->m_vector[i].get();
+ JSValue dest = storage->m_vector[destinationIndex].get();
+ // Any time we overwrite a hole we know we overcounted the number of values we removed
+ // when we subtracted count from m_numValuesInVector above.
+ if (!dest && destinationIndex >= firstIndexAfterShiftRegion)
+ storage->m_numValuesInVector++;
+ storage->m_vector[count + i].setWithoutWriteBarrier(source);
+ }
+ } else {
+ memmove(storage->m_vector + count,
+ storage->m_vector,
+ sizeof(JSValue) * startIndex);
+ }
}
// Adjust the Butterfly and the index bias. We only need to do this here because we're changing
// the start of the Butterfly, which needs to point at the first indexed property in the used
// portion of the vector.
- m_butterfly.setWithoutWriteBarrier(m_butterfly->shift(structure(), count));
- storage = m_butterfly->arrayStorage();
+ Butterfly* butterfly = m_butterfly.get()->shift(structure(), count);
+ setButterfly(vm, butterfly);
+ storage = butterfly->arrayStorage();
storage->m_indexBias += count;
// Since we're consuming part of the vector by moving its beginning to the left,
@@ -729,10 +958,22 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
} else {
// The number of elements before the shift region is greater than or equal to the number
// of elements after the shift region, so we move the elements after the shift region to the left.
- memmove(
- storage->m_vector + startIndex,
- storage->m_vector + firstIndexAfterShiftRegion,
- sizeof(JSValue) * numElementsAfterShiftRegion);
+ if (storage->hasHoles()) {
+ for (unsigned i = 0; i < numElementsAfterShiftRegion; ++i) {
+ unsigned destinationIndex = startIndex + i;
+ JSValue source = storage->m_vector[firstIndexAfterShiftRegion + i].get();
+ JSValue dest = storage->m_vector[destinationIndex].get();
+ // Any time we overwrite a hole we know we overcounted the number of values we removed
+ // when we subtracted count from m_numValuesInVector above.
+ if (!dest && destinationIndex < firstIndexAfterShiftRegion)
+ storage->m_numValuesInVector++;
+ storage->m_vector[startIndex + i].setWithoutWriteBarrier(source);
+ }
+ } else {
+ memmove(storage->m_vector + startIndex,
+ storage->m_vector + firstIndexAfterShiftRegion,
+ sizeof(JSValue) * numElementsAfterShiftRegion);
+ }
// Clear the slots of the elements we just moved.
unsigned startOfEmptyVectorTail = usedVectorLength - count;
for (unsigned i = startOfEmptyVectorTail; i < usedVectorLength; ++i)
@@ -746,11 +987,14 @@ bool JSArray::shiftCountWithArrayStorage(unsigned startIndex, unsigned count, Ar
return true;
}
-bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
+bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startIndex, unsigned count)
{
+ VM& vm = exec->vm();
RELEASE_ASSERT(count > 0);
+
+ Butterfly* butterfly = m_butterfly.get();
- switch (structure()->indexingType()) {
+ switch (indexingType()) {
case ArrayClass:
return true;
@@ -760,78 +1004,84 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
case ArrayWithInt32:
case ArrayWithContiguous: {
- unsigned oldLength = m_butterfly->publicLength();
+ unsigned oldLength = butterfly->publicLength();
RELEASE_ASSERT(count <= oldLength);
// 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)
- return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+ return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
// 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.
// We have to check for holes before we start moving things around so that we don't get halfway
// through shifting and then realize we should have been in ArrayStorage mode.
unsigned end = oldLength - count;
- for (unsigned i = startIndex; i < end; ++i) {
- JSValue v = m_butterfly->contiguous()[i + count].get();
- if (UNLIKELY(!v))
- return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+ if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+ for (unsigned i = startIndex; i < end; ++i) {
+ JSValue v = butterfly->contiguous()[i + count].get();
+ if (UNLIKELY(!v)) {
+ startIndex = i;
+ return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+ }
+ butterfly->contiguous()[i].setWithoutWriteBarrier(v);
+ }
+ } else {
+ memmove(butterfly->contiguous().data() + startIndex,
+ butterfly->contiguous().data() + startIndex + count,
+ sizeof(JSValue) * (end - startIndex));
}
- for (unsigned i = startIndex; i < end; ++i) {
- JSValue v = m_butterfly->contiguous()[i + count].get();
- ASSERT(v);
- // 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();
+ butterfly->contiguous()[i].clear();
+
+ butterfly->setPublicLength(oldLength - count);
+
+ // Our memmoving of values around in the array could have concealed some of them from
+ // the collector. Let's make sure that the collector scans this object again.
+ vm.heap.writeBarrier(this);
- m_butterfly->setPublicLength(oldLength - count);
return true;
}
case ArrayWithDouble: {
- unsigned oldLength = m_butterfly->publicLength();
+ unsigned oldLength = butterfly->publicLength();
RELEASE_ASSERT(count <= oldLength);
// 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)
- return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
+ return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
// 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.
// We have to check for holes before we start moving things around so that we don't get halfway
// through shifting and then realize we should have been in ArrayStorage mode.
unsigned end = oldLength - count;
- for (unsigned i = startIndex; i < end; ++i) {
- double v = m_butterfly->contiguousDouble()[i + count];
- if (UNLIKELY(v != v))
- return shiftCountWithArrayStorage(startIndex, count, ensureArrayStorage(exec->vm()));
- }
-
- for (unsigned i = startIndex; i < end; ++i) {
- double v = m_butterfly->contiguousDouble()[i + count];
- ASSERT(v == v);
- // 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;
+ if (this->structure(vm)->holesMustForwardToPrototype(vm)) {
+ for (unsigned i = startIndex; i < end; ++i) {
+ double v = butterfly->contiguousDouble()[i + count];
+ if (UNLIKELY(v != v)) {
+ startIndex = i;
+ return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
+ }
+ butterfly->contiguousDouble()[i] = v;
+ }
+ } else {
+ memmove(butterfly->contiguousDouble().data() + startIndex,
+ butterfly->contiguousDouble().data() + startIndex + count,
+ sizeof(JSValue) * (end - startIndex));
}
for (unsigned i = end; i < oldLength; ++i)
- m_butterfly->contiguousDouble()[i] = QNaN;
+ butterfly->contiguousDouble()[i] = PNaN;
- m_butterfly->setPublicLength(oldLength - count);
+ butterfly->setPublicLength(oldLength - count);
return true;
}
case ArrayWithArrayStorage:
case ArrayWithSlowPutArrayStorage:
- return shiftCountWithArrayStorage(startIndex, count, arrayStorage());
+ return shiftCountWithArrayStorage(vm, startIndex, count, arrayStorage());
default:
CRASH();
@@ -842,31 +1092,39 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex
// Returns true if the unshift can be handled, false to fallback.
bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
{
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
unsigned length = storage->length();
RELEASE_ASSERT(startIndex <= length);
// If the array contains holes or is otherwise in an abnormal state,
// use the generic algorithm in ArrayPrototype.
- if (length != storage->m_numValuesInVector || storage->inSparseMode() || shouldUseSlowPut(structure()->indexingType()))
+ if (storage->hasHoles() || storage->inSparseMode() || shouldUseSlowPut(indexingType()))
return false;
bool moveFront = !startIndex || startIndex < length / 2;
unsigned vectorLength = storage->vectorLength();
+ // Need to have GC deferred around the unshiftCountSlowCase(), since that leaves the butterfly in
+ // a weird state: some parts of it will be left uninitialized, which we will fill in here.
+ DeferGC deferGC(vm.heap);
+ auto locker = holdLock(*this);
+
if (moveFront && storage->m_indexBias >= count) {
Butterfly* newButterfly = storage->butterfly()->unshift(structure(), count);
storage = newButterfly->arrayStorage();
storage->m_indexBias -= count;
storage->setVectorLength(vectorLength + count);
- setButterflyWithoutChangingStructure(exec->vm(), newButterfly);
+ setButterfly(vm, newButterfly);
} else if (!moveFront && vectorLength - length >= count)
storage = storage->butterfly()->arrayStorage();
- else if (unshiftCountSlowCase(exec->vm(), moveFront, count))
+ else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
storage = arrayStorage();
else {
- throwOutOfMemoryError(exec);
+ throwOutOfMemoryError(exec, scope);
return true;
}
@@ -881,12 +1139,18 @@ bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex,
for (unsigned i = 0; i < count; i++)
vector[i + startIndex].clear();
+
return true;
}
bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startIndex, unsigned count)
{
- switch (structure()->indexingType()) {
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ Butterfly* butterfly = m_butterfly.get();
+
+ switch (indexingType()) {
case ArrayClass:
case ArrayWithUndecided:
// We could handle this. But it shouldn't ever come up, so we won't.
@@ -894,29 +1158,41 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
case ArrayWithInt32:
case ArrayWithContiguous: {
- unsigned oldLength = m_butterfly->publicLength();
+ unsigned oldLength = 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)
- return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+ if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
+ scope.release();
+ return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+ }
- ensureLength(exec->vm(), oldLength + count);
+ if (!ensureLength(vm, oldLength + count)) {
+ throwOutOfMemoryError(exec, scope);
+ return false;
+ }
+ butterfly = m_butterfly.get();
// We have to check for holes before we start moving things around so that we don't get halfway
// through shifting and then realize we should have been in ArrayStorage mode.
for (unsigned i = oldLength; i-- > startIndex;) {
- JSValue v = m_butterfly->contiguous()[i].get();
- if (UNLIKELY(!v))
- return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+ JSValue v = butterfly->contiguous()[i].get();
+ if (UNLIKELY(!v)) {
+ scope.release();
+ return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+ }
}
for (unsigned i = oldLength; i-- > startIndex;) {
- JSValue v = m_butterfly->contiguous()[i].get();
+ JSValue v = butterfly->contiguous()[i].get();
ASSERT(v);
- m_butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
+ butterfly->contiguous()[i + count].setWithoutWriteBarrier(v);
}
+ // Our memmoving of values around in the array could have concealed some of them from
+ // the collector. Let's make sure that the collector scans this object again.
+ vm.heap.writeBarrier(this);
+
// 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
@@ -926,27 +1202,35 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
}
case ArrayWithDouble: {
- unsigned oldLength = m_butterfly->publicLength();
+ unsigned oldLength = 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)
- return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+ if (oldLength - startIndex >= MIN_SPARSE_ARRAY_INDEX) {
+ scope.release();
+ return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+ }
- ensureLength(exec->vm(), oldLength + count);
+ if (!ensureLength(vm, oldLength + count)) {
+ throwOutOfMemoryError(exec, scope);
+ return false;
+ }
+ butterfly = m_butterfly.get();
// We have to check for holes before we start moving things around so that we don't get halfway
// through shifting and then realize we should have been in ArrayStorage mode.
for (unsigned i = oldLength; i-- > startIndex;) {
- double v = m_butterfly->contiguousDouble()[i];
- if (UNLIKELY(v != v))
- return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
+ double v = butterfly->contiguousDouble()[i];
+ if (UNLIKELY(v != v)) {
+ scope.release();
+ return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
+ }
}
for (unsigned i = oldLength; i-- > startIndex;) {
- double v = m_butterfly->contiguousDouble()[i];
+ double v = butterfly->contiguousDouble()[i];
ASSERT(v == v);
- m_butterfly->contiguousDouble()[i + count] = v;
+ butterfly->contiguousDouble()[i + count] = v;
}
// NOTE: we're leaving being garbage in the part of the array that we shifted out
@@ -959,6 +1243,7 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
case ArrayWithArrayStorage:
case ArrayWithSlowPutArrayStorage:
+ scope.release();
return unshiftCountWithArrayStorage(exec, startIndex, count, arrayStorage());
default:
@@ -967,526 +1252,15 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
}
}
-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);
-}
-
-static int compareNumbersForQSort(const void* a, const void* b)
-{
- double da = static_cast<const JSValue*>(a)->asNumber();
- double db = static_cast<const JSValue*>(b)->asNumber();
- return (da > db) - (da < db);
-}
-
-static int compareByStringPairForQSort(const void* a, const void* b)
-{
- const ValueStringPair* va = static_cast<const ValueStringPair*>(a);
- const ValueStringPair* vb = static_cast<const ValueStringPair*>(b);
- return codePointCompare(va->second, vb->second);
-}
-
-template<IndexingType indexingType>
-void JSArray::sortNumericVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
- ASSERT(indexingType == ArrayWithInt32 || indexingType == ArrayWithDouble || indexingType == ArrayWithContiguous || indexingType == ArrayWithArrayStorage);
-
- unsigned lengthNotIncludingUndefined;
- unsigned newRelevantLength;
- compactForSorting<indexingType>(
- lengthNotIncludingUndefined,
- newRelevantLength);
-
- ContiguousJSValues data = indexingData<indexingType>();
-
- if (indexingType == ArrayWithArrayStorage && arrayStorage()->m_sparseMap.get()) {
- throwOutOfMemoryError(exec);
- return;
- }
-
- if (!lengthNotIncludingUndefined)
- return;
-
- bool allValuesAreNumbers = true;
- switch (indexingType) {
- case ArrayWithInt32:
- case ArrayWithDouble:
- break;
-
- default:
- for (size_t i = 0; i < newRelevantLength; ++i) {
- if (!data[i].isNumber()) {
- allValuesAreNumbers = false;
- break;
- }
- }
- break;
- }
-
- 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.
- 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;
- }
- ASSERT(data.length() >= newRelevantLength);
- qsort(data.data(), newRelevantLength, sizeof(WriteBarrier<Unknown>), compare);
- return;
-}
-
-void JSArray::sortNumeric(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
- ASSERT(!inSparseIndexingMode());
-
- switch (structure()->indexingType()) {
- case ArrayClass:
- return;
-
- case ArrayWithInt32:
- sortNumericVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
- break;
-
- case ArrayWithDouble:
- sortNumericVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
- break;
-
- case ArrayWithContiguous:
- sortNumericVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
- return;
-
- case ArrayWithArrayStorage:
- sortNumericVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
- return;
-
- default:
- CRASH();
- return;
- }
-}
-
-template <IndexingType> struct ContiguousTypeAccessor {
- typedef WriteBarrier<Unknown> Type;
- static JSValue getAsValue(ContiguousData<Type> data, size_t i) { return data[i].get(); }
- static void setWithValue(VM& vm, JSArray* thisValue, ContiguousData<Type> data, size_t i, JSValue value)
- {
- data[i].set(vm, thisValue, value);
- }
- 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]); }
- static void setWithValue(VM&, JSArray*, ContiguousData<Type> data, size_t i, JSValue value)
- {
- data[i] = value.asNumber();
- }
- static NO_RETURN_DUE_TO_CRASH void replaceDataReference(ContiguousData<Type>*, ContiguousJSValues)
- {
- RELEASE_ASSERT_WITH_MESSAGE(0, "Inconsistent indexing types during compact array sort.");
- }
-};
-
-
-template<IndexingType indexingType, typename StorageType>
-void JSArray::sortCompactedVector(ExecState* exec, ContiguousData<StorageType> data, unsigned relevantLength)
-{
- if (!relevantLength)
- return;
-
- VM& vm = exec->vm();
-
- // Converting JavaScript values to strings can be expensive, so we do it once up front and sort based on that.
- // This is a considerable improvement over doing it twice per comparison, though it requires a large temporary
- // buffer. Besides, this protects us from crashing if some objects have custom toString methods that return
- // random or otherwise changing results, effectively making compare function inconsistent.
-
- Vector<ValueStringPair, 0, UnsafeVectorOverflow> values(relevantLength);
- if (!values.begin()) {
- throwOutOfMemoryError(exec);
- return;
- }
-
- Heap::heap(this)->pushTempSortVector(&values);
-
- bool isSortingPrimitiveValues = true;
-
- for (size_t i = 0; i < relevantLength; i++) {
- JSValue value = ContiguousTypeAccessor<indexingType>::getAsValue(data, i);
- ASSERT(indexingType != ArrayWithInt32 || value.isInt32());
- ASSERT(!value.isUndefined());
- values[i].first = value;
- if (indexingType != ArrayWithDouble && indexingType != ArrayWithInt32)
- isSortingPrimitiveValues = isSortingPrimitiveValues && value.isPrimitive();
- }
-
- // FIXME: The following loop continues to call toString on subsequent values even after
- // a toString call raises an exception.
-
- for (size_t i = 0; i < relevantLength; i++)
- values[i].second = values[i].first.toWTFStringInline(exec);
-
- if (exec->hadException()) {
- Heap::heap(this)->popTempSortVector(&values);
- return;
- }
-
- // FIXME: Since we sort by string value, a fast algorithm might be to use a radix sort. That would be O(N) rather
- // than O(N log N).
-
-#if HAVE(MERGESORT)
- if (isSortingPrimitiveValues)
- qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
- else
- mergesort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
-#else
- // FIXME: The qsort library function is likely to not be a stable sort.
- // ECMAScript-262 does not specify a stable sort, but in practice, browsers perform a stable sort.
- qsort(values.begin(), values.size(), sizeof(ValueStringPair), compareByStringPairForQSort);
-#endif
-
- // If the toString function changed the length of the array or vector storage,
- // increase the length to handle the orignal number of actual values.
- switch (indexingType) {
- case ArrayWithInt32:
- case ArrayWithDouble:
- case ArrayWithContiguous:
- ensureLength(vm, relevantLength);
- break;
-
- case ArrayWithArrayStorage:
- if (arrayStorage()->vectorLength() < relevantLength) {
- increaseVectorLength(exec->vm(), relevantLength);
- ContiguousTypeAccessor<indexingType>::replaceDataReference(&data, arrayStorage()->vector());
- }
- if (arrayStorage()->length() < relevantLength)
- arrayStorage()->setLength(relevantLength);
- break;
-
- default:
- CRASH();
- }
-
- for (size_t i = 0; i < relevantLength; i++)
- ContiguousTypeAccessor<indexingType>::setWithValue(vm, this, data, i, values[i].first);
-
- Heap::heap(this)->popTempSortVector(&values);
-}
-
-void JSArray::sort(ExecState* exec)
-{
- ASSERT(!inSparseIndexingMode());
-
- switch (structure()->indexingType()) {
- case ArrayClass:
- case ArrayWithUndecided:
- return;
-
- case ArrayWithInt32: {
- unsigned lengthNotIncludingUndefined;
- unsigned newRelevantLength;
- compactForSorting<ArrayWithInt32>(
- lengthNotIncludingUndefined, newRelevantLength);
-
- sortCompactedVector<ArrayWithInt32>(
- exec, m_butterfly->contiguousInt32(), lengthNotIncludingUndefined);
- return;
- }
-
- case ArrayWithDouble: {
- unsigned lengthNotIncludingUndefined;
- unsigned newRelevantLength;
- compactForSorting<ArrayWithDouble>(
- lengthNotIncludingUndefined, newRelevantLength);
-
- sortCompactedVector<ArrayWithDouble>(
- exec, m_butterfly->contiguousDouble(), lengthNotIncludingUndefined);
- return;
- }
-
- case ArrayWithContiguous: {
- unsigned lengthNotIncludingUndefined;
- unsigned newRelevantLength;
- compactForSorting<ArrayWithContiguous>(
- lengthNotIncludingUndefined, newRelevantLength);
-
- sortCompactedVector<ArrayWithContiguous>(
- exec, m_butterfly->contiguous(), lengthNotIncludingUndefined);
- return;
- }
-
- case ArrayWithArrayStorage: {
- unsigned lengthNotIncludingUndefined;
- unsigned newRelevantLength;
- compactForSorting<ArrayWithArrayStorage>(
- lengthNotIncludingUndefined, newRelevantLength);
- ArrayStorage* storage = m_butterfly->arrayStorage();
- ASSERT(!storage->m_sparseMap);
-
- sortCompactedVector<ArrayWithArrayStorage>(exec, storage->vector(), lengthNotIncludingUndefined);
- return;
- }
-
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
-}
-
-struct AVLTreeNodeForArrayCompare {
- JSValue value;
-
- // Child pointers. The high bit of gt is robbed and used as the
- // balance factor sign. The high bit of lt is robbed and used as
- // the magnitude of the balance factor.
- int32_t gt;
- int32_t lt;
-};
-
-struct AVLTreeAbstractorForArrayCompare {
- typedef int32_t handle; // Handle is an index into m_nodes vector.
- typedef JSValue key;
- typedef int32_t size;
-
- Vector<AVLTreeNodeForArrayCompare, 0, UnsafeVectorOverflow> m_nodes;
- ExecState* m_exec;
- JSValue m_compareFunction;
- CallType m_compareCallType;
- const CallData* m_compareCallData;
- OwnPtr<CachedCall> m_cachedCall;
-
- handle get_less(handle h) { return m_nodes[h].lt & 0x7FFFFFFF; }
- void set_less(handle h, handle lh) { m_nodes[h].lt &= 0x80000000; m_nodes[h].lt |= lh; }
- handle get_greater(handle h) { return m_nodes[h].gt & 0x7FFFFFFF; }
- void set_greater(handle h, handle gh) { m_nodes[h].gt &= 0x80000000; m_nodes[h].gt |= gh; }
-
- int get_balance_factor(handle h)
- {
- if (m_nodes[h].gt & 0x80000000)
- return -1;
- return static_cast<unsigned>(m_nodes[h].lt) >> 31;
- }
-
- void set_balance_factor(handle h, int bf)
- {
- if (bf == 0) {
- m_nodes[h].lt &= 0x7FFFFFFF;
- m_nodes[h].gt &= 0x7FFFFFFF;
- } else {
- m_nodes[h].lt |= 0x80000000;
- if (bf < 0)
- m_nodes[h].gt |= 0x80000000;
- else
- m_nodes[h].gt &= 0x7FFFFFFF;
- }
- }
-
- int compare_key_key(key va, key vb)
- {
- ASSERT(!va.isUndefined());
- ASSERT(!vb.isUndefined());
-
- if (m_exec->hadException())
- return 1;
-
- double compareResult;
- if (m_cachedCall) {
- m_cachedCall->setThis(jsUndefined());
- m_cachedCall->setArgument(0, va);
- m_cachedCall->setArgument(1, vb);
- compareResult = m_cachedCall->call().toNumber(m_exec);
- } else {
- MarkedArgumentBuffer arguments;
- arguments.append(va);
- arguments.append(vb);
- compareResult = call(m_exec, m_compareFunction, m_compareCallType, *m_compareCallData, jsUndefined(), arguments).toNumber(m_exec);
- }
- return (compareResult < 0) ? -1 : 1; // Not passing equality through, because we need to store all values, even if equivalent.
- }
-
- int compare_key_node(key k, handle h) { return compare_key_key(k, m_nodes[h].value); }
- int compare_node_node(handle h1, handle h2) { return compare_key_key(m_nodes[h1].value, m_nodes[h2].value); }
-
- static handle null() { return 0x7FFFFFFF; }
-};
-
-template<IndexingType indexingType>
-void JSArray::sortVector(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
- ASSERT(!inSparseIndexingMode());
- ASSERT(indexingType == structure()->indexingType());
-
- // FIXME: This ignores exceptions raised in the compare function or in toNumber.
-
- // The maximum tree depth is compiled in - but the caller is clearly up to no good
- // if a larger array is passed.
- ASSERT(m_butterfly->publicLength() <= static_cast<unsigned>(std::numeric_limits<int>::max()));
- if (m_butterfly->publicLength() > static_cast<unsigned>(std::numeric_limits<int>::max()))
- return;
-
- unsigned usedVectorLength = relevantLength<indexingType>();
- unsigned nodeCount = usedVectorLength;
-
- if (!nodeCount)
- return;
-
- AVLTree<AVLTreeAbstractorForArrayCompare, 44> tree; // Depth 44 is enough for 2^31 items
- tree.abstractor().m_exec = exec;
- tree.abstractor().m_compareFunction = compareFunction;
- tree.abstractor().m_compareCallType = callType;
- tree.abstractor().m_compareCallData = &callData;
- tree.abstractor().m_nodes.grow(nodeCount);
-
- if (callType == CallTypeJS)
- tree.abstractor().m_cachedCall = adoptPtr(new CachedCall(exec, jsCast<JSFunction*>(compareFunction), 2));
-
- if (!tree.abstractor().m_nodes.begin()) {
- throwOutOfMemoryError(exec);
- return;
- }
-
- // FIXME: If the compare function modifies the array, the vector, map, etc. could be modified
- // right out from under us while we're building the tree here.
-
- unsigned numDefined = 0;
- unsigned numUndefined = 0;
-
- // Iterate over the array, ignoring missing values, counting undefined ones, and inserting all other ones into the tree.
- for (; numDefined < usedVectorLength; ++numDefined) {
- if (numDefined >= m_butterfly->vectorLength())
- break;
- JSValue v = getHolyIndexQuickly(numDefined);
- if (!v || v.isUndefined())
- break;
- tree.abstractor().m_nodes[numDefined].value = v;
- tree.insert(numDefined);
- }
- for (unsigned i = numDefined; i < usedVectorLength; ++i) {
- if (i >= m_butterfly->vectorLength())
- break;
- JSValue v = getHolyIndexQuickly(i);
- if (v) {
- if (v.isUndefined())
- ++numUndefined;
- else {
- tree.abstractor().m_nodes[numDefined].value = v;
- tree.insert(numDefined);
- ++numDefined;
- }
- }
- }
-
- unsigned newUsedVectorLength = numDefined + numUndefined;
-
- // The array size may have changed. Figure out the new bounds.
- unsigned newestUsedVectorLength = currentRelevantLength();
-
- unsigned elementsToExtractThreshold = min(min(newestUsedVectorLength, numDefined), static_cast<unsigned>(tree.abstractor().m_nodes.size()));
- unsigned undefinedElementsThreshold = min(newestUsedVectorLength, newUsedVectorLength);
- unsigned clearElementsThreshold = min(newestUsedVectorLength, usedVectorLength);
-
- // Copy the values back into m_storage.
- AVLTree<AVLTreeAbstractorForArrayCompare, 44>::Iterator iter;
- iter.start_iter_least(tree);
- VM& vm = exec->vm();
- for (unsigned i = 0; i < elementsToExtractThreshold; ++i) {
- ASSERT(i < butterfly()->vectorLength());
- if (structure()->indexingType() == ArrayWithDouble)
- butterfly()->contiguousDouble()[i] = tree.abstractor().m_nodes[*iter].value.asNumber();
- else
- currentIndexingData()[i].set(vm, this, tree.abstractor().m_nodes[*iter].value);
- ++iter;
- }
- // Put undefined values back in.
- switch (structure()->indexingType()) {
- case ArrayWithInt32:
- case ArrayWithDouble:
- ASSERT(elementsToExtractThreshold == undefinedElementsThreshold);
- break;
-
- default:
- for (unsigned i = elementsToExtractThreshold; i < undefinedElementsThreshold; ++i) {
- ASSERT(i < butterfly()->vectorLength());
- currentIndexingData()[i].setUndefined();
- }
- }
-
- // Ensure that unused values in the vector are zeroed out.
- for (unsigned i = undefinedElementsThreshold; i < clearElementsThreshold; ++i) {
- ASSERT(i < butterfly()->vectorLength());
- if (structure()->indexingType() == ArrayWithDouble)
- butterfly()->contiguousDouble()[i] = QNaN;
- else
- currentIndexingData()[i].clear();
- }
-
- if (hasArrayStorage(structure()->indexingType()))
- arrayStorage()->m_numValuesInVector = newUsedVectorLength;
-}
-
-void JSArray::sort(ExecState* exec, JSValue compareFunction, CallType callType, const CallData& callData)
-{
- ASSERT(!inSparseIndexingMode());
-
- switch (structure()->indexingType()) {
- case ArrayClass:
- case ArrayWithUndecided:
- return;
-
- case ArrayWithInt32:
- sortVector<ArrayWithInt32>(exec, compareFunction, callType, callData);
- return;
-
- case ArrayWithDouble:
- sortVector<ArrayWithDouble>(exec, compareFunction, callType, callData);
- return;
-
- case ArrayWithContiguous:
- sortVector<ArrayWithContiguous>(exec, compareFunction, callType, callData);
- return;
-
- case ArrayWithArrayStorage:
- sortVector<ArrayWithArrayStorage>(exec, compareFunction, callType, callData);
- return;
-
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
-}
-
void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
{
unsigned i = 0;
unsigned vectorEnd;
WriteBarrier<Unknown>* vector;
+
+ Butterfly* butterfly = m_butterfly.get();
- switch (structure()->indexingType()) {
+ switch (indexingType()) {
case ArrayClass:
return;
@@ -1498,16 +1272,16 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
case ArrayWithInt32:
case ArrayWithContiguous: {
- vectorEnd = m_butterfly->publicLength();
- vector = m_butterfly->contiguous().data();
+ vectorEnd = butterfly->publicLength();
+ vector = butterfly->contiguous().data();
break;
}
case ArrayWithDouble: {
vector = 0;
vectorEnd = 0;
- for (; i < m_butterfly->publicLength(); ++i) {
- double v = butterfly()->contiguousDouble()[i];
+ for (; i < butterfly->publicLength(); ++i) {
+ double v = butterfly->contiguousDouble()[i];
if (v != v)
break;
args.append(JSValue(JSValue::EncodeAsDouble, v));
@@ -1516,7 +1290,7 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
}
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
- ArrayStorage* storage = m_butterfly->arrayStorage();
+ ArrayStorage* storage = butterfly->arrayStorage();
vector = storage->m_vector;
vectorEnd = min(storage->length(), storage->vectorLength());
@@ -1525,9 +1299,11 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
default:
CRASH();
+#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
vector = 0;
vectorEnd = 0;
break;
+#endif
}
for (; i < vectorEnd; ++i) {
@@ -1536,19 +1312,27 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
break;
args.append(v.get());
}
-
+
+ // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
for (; i < length(); ++i)
args.append(get(exec, i));
}
-void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t length)
+void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
{
- unsigned i = 0;
+ VM& vm = exec->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ unsigned i = offset;
WriteBarrier<Unknown>* vector;
unsigned vectorEnd;
-
+ length += offset; // We like to think of the length as being our length, rather than the output length.
+
+ // FIXME: What prevents this from being called with a RuntimeArray? The length function will always return 0 in that case.
ASSERT(length == this->length());
- switch (structure()->indexingType()) {
+
+ Butterfly* butterfly = m_butterfly.get();
+ switch (indexingType()) {
case ArrayClass:
return;
@@ -1560,26 +1344,26 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
case ArrayWithInt32:
case ArrayWithContiguous: {
- vector = m_butterfly->contiguous().data();
- vectorEnd = m_butterfly->publicLength();
+ vector = butterfly->contiguous().data();
+ vectorEnd = butterfly->publicLength();
break;
}
case ArrayWithDouble: {
vector = 0;
vectorEnd = 0;
- for (; i < m_butterfly->publicLength(); ++i) {
- ASSERT(i < butterfly()->vectorLength());
- double v = m_butterfly->contiguousDouble()[i];
+ for (; i < butterfly->publicLength(); ++i) {
+ ASSERT(i < butterfly->vectorLength());
+ double v = butterfly->contiguousDouble()[i];
if (v != v)
break;
- callFrame->setArgument(i, JSValue(JSValue::EncodeAsDouble, v));
+ exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
}
break;
}
case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES: {
- ArrayStorage* storage = m_butterfly->arrayStorage();
+ ArrayStorage* storage = butterfly->arrayStorage();
vector = storage->m_vector;
vectorEnd = min(length, storage->vectorLength());
break;
@@ -1587,111 +1371,24 @@ void JSArray::copyToArguments(ExecState* exec, CallFrame* callFrame, uint32_t le
default:
CRASH();
+#if COMPILER_QUIRK(CONSIDERS_UNREACHABLE_CODE)
vector = 0;
vectorEnd = 0;
break;
+#endif
}
for (; i < vectorEnd; ++i) {
WriteBarrier<Unknown>& v = vector[i];
if (!v)
break;
- callFrame->setArgument(i, v.get());
+ exec->r(firstElementDest + i - offset) = v.get();
}
- for (; i < length; ++i)
- callFrame->setArgument(i, get(exec, i));
-}
-
-template<IndexingType indexingType>
-void JSArray::compactForSorting(unsigned& numDefined, unsigned& newRelevantLength)
-{
- ASSERT(!inSparseIndexingMode());
- ASSERT(indexingType == structure()->indexingType());
-
- unsigned myRelevantLength = relevantLength<indexingType>();
-
- numDefined = 0;
- unsigned numUndefined = 0;
-
- for (; numDefined < myRelevantLength; ++numDefined) {
- ASSERT(numDefined < m_butterfly->vectorLength());
- if (indexingType == ArrayWithInt32) {
- JSValue v = m_butterfly->contiguousInt32()[numDefined].get();
- if (!v)
- break;
- ASSERT(v.isInt32());
- continue;
- }
- if (indexingType == ArrayWithDouble) {
- double v = m_butterfly->contiguousDouble()[numDefined];
- if (v != v)
- break;
- continue;
- }
- JSValue v = indexingData<indexingType>()[numDefined].get();
- if (!v || v.isUndefined())
- break;
- }
-
- for (unsigned i = numDefined; i < myRelevantLength; ++i) {
- ASSERT(i < m_butterfly->vectorLength());
- if (indexingType == ArrayWithInt32) {
- JSValue v = m_butterfly->contiguousInt32()[i].get();
- if (!v)
- continue;
- ASSERT(v.isInt32());
- ASSERT(numDefined < m_butterfly->vectorLength());
- m_butterfly->contiguousInt32()[numDefined++].setWithoutWriteBarrier(v);
- continue;
- }
- if (indexingType == ArrayWithDouble) {
- double v = m_butterfly->contiguousDouble()[i];
- if (v != v)
- continue;
- ASSERT(numDefined < m_butterfly->vectorLength());
- m_butterfly->contiguousDouble()[numDefined++] = v;
- continue;
- }
- JSValue v = indexingData<indexingType>()[i].get();
- if (v) {
- if (v.isUndefined())
- ++numUndefined;
- else {
- ASSERT(numDefined < m_butterfly->vectorLength());
- indexingData<indexingType>()[numDefined++].setWithoutWriteBarrier(v);
- }
- }
- }
-
- newRelevantLength = numDefined + numUndefined;
-
- if (hasArrayStorage(indexingType))
- RELEASE_ASSERT(!arrayStorage()->m_sparseMap);
-
- switch (indexingType) {
- case ArrayWithInt32:
- case ArrayWithDouble:
- RELEASE_ASSERT(numDefined == newRelevantLength);
- break;
-
- default:
- for (unsigned i = numDefined; i < newRelevantLength; ++i) {
- ASSERT(i < m_butterfly->vectorLength());
- indexingData<indexingType>()[i].setUndefined();
- }
- break;
+ for (; i < length; ++i) {
+ exec->r(firstElementDest + i - offset) = get(exec, i);
+ RETURN_IF_EXCEPTION(scope, void());
}
- for (unsigned i = newRelevantLength; i < myRelevantLength; ++i) {
- ASSERT(i < m_butterfly->vectorLength());
- if (indexingType == ArrayWithDouble)
- m_butterfly->contiguousDouble()[i] = QNaN;
- else
- indexingData<indexingType>()[i].clear();
- }
-
- if (hasArrayStorage(indexingType))
- arrayStorage()->m_numValuesInVector = newRelevantLength;
}
} // namespace JSC