#pragma once
#include "ArgList.h"
#include "ArrayConventions.h"
#include "ButterflyInlines.h"
#include "JSCellInlines.h"
#include "JSObject.h"
namespace JSC {
class JSArray;
class LLIntOffsetsExtractor;
extern const char* const LengthExceededTheMaximumArrayLengthError;
class JSArray : public JSNonFinalObject {
friend class LLIntOffsetsExtractor;
friend class Walker;
friend class JIT;
public:
typedef JSNonFinalObject Base;
static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
static size_t allocationSize(Checked<size_t> inlineCapacity)
{
ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
return sizeof(JSArray);
}
protected:
explicit JSArray(VM& vm, Structure* structure, Butterfly* butterfly)
: JSNonFinalObject(vm, structure, butterfly)
{
}
public:
static JSArray* tryCreate(VM&, Structure*, unsigned initialLength = 0);
static JSArray* tryCreate(VM&, Structure*, unsigned initialLength, unsigned vectorLengthHint);
static JSArray* create(VM&, Structure*, unsigned initialLength = 0);
static JSArray* createWithButterfly(VM&, GCDeferralContext*, Structure*, Butterfly*);
JS_EXPORT_PRIVATE static JSArray* tryCreateUninitializedRestricted(ObjectInitializationScope&, GCDeferralContext*, Structure*, unsigned initialLength);
static JSArray* tryCreateUninitializedRestricted(ObjectInitializationScope& scope, Structure* structure, unsigned initialLength)
{
return tryCreateUninitializedRestricted(scope, nullptr, structure, initialLength);
}
JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool throwException);
JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
DECLARE_EXPORT_INFO;
unsigned length() const { return getArrayLength(); }
JS_EXPORT_PRIVATE bool setLength(ExecState*, unsigned, bool throwException = false);
void pushInline(ExecState*, JSValue);
JS_EXPORT_PRIVATE void push(ExecState*, JSValue);
JS_EXPORT_PRIVATE JSValue pop(ExecState*);
JSArray* fastSlice(ExecState&, unsigned startIndex, unsigned count);
bool canFastCopy(VM&, JSArray* otherArray);
IndexingType mergeIndexingTypeForCopying(IndexingType other);
bool appendMemcpy(ExecState*, VM&, unsigned startIndex, JSArray* otherArray);
enum ShiftCountMode {
ShiftCountForShift,
ShiftCountForSplice
};
bool shiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count)
{
VM& vm = exec->vm();
return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
}
bool shiftCountForSplice(ExecState* exec, unsigned& startIndex, unsigned count)
{
return shiftCountWithAnyIndexingType(exec, startIndex, count);
}
template<ShiftCountMode shiftCountMode>
bool shiftCount(ExecState* exec, unsigned& startIndex, unsigned count)
{
switch (shiftCountMode) {
case ShiftCountForShift:
return shiftCountForShift(exec, startIndex, count);
case ShiftCountForSplice:
return shiftCountForSplice(exec, startIndex, count);
default:
CRASH();
return false;
}
}
bool unshiftCountForShift(ExecState* exec, unsigned startIndex, unsigned count)
{
return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(exec->vm()));
}
bool unshiftCountForSplice(ExecState* exec, unsigned startIndex, unsigned count)
{
return unshiftCountWithAnyIndexingType(exec, startIndex, count);
}
template<ShiftCountMode shiftCountMode>
bool unshiftCount(ExecState* exec, unsigned startIndex, unsigned count)
{
switch (shiftCountMode) {
case ShiftCountForShift:
return unshiftCountForShift(exec, startIndex, count);
case ShiftCountForSplice:
return unshiftCountForSplice(exec, startIndex, count);
default:
CRASH();
return false;
}
}
JS_EXPORT_PRIVATE void fillArgList(ExecState*, MarkedArgumentBuffer&);
JS_EXPORT_PRIVATE void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length);
JS_EXPORT_PRIVATE bool isIteratorProtocolFastAndNonObservable();
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType)
{
return Structure::create(vm, globalObject, prototype, TypeInfo(ArrayType, StructureFlags), info(), indexingType);
}
protected:
void finishCreation(VM& vm)
{
Base::finishCreation(vm);
ASSERT_WITH_MESSAGE(type() == ArrayType || type() == DerivedArrayType, "Instance inheriting JSArray should have either ArrayType or DerivedArrayType");
}
static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
static bool deleteProperty(JSCell*, ExecState*, PropertyName);
JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
private:
bool isLengthWritable()
{
ArrayStorage* storage = arrayStorageOrNull();
if (!storage)
return true;
SparseArrayValueMap* map = storage->m_sparseMap.get();
return !map || !map->lengthIsReadOnly();
}
bool shiftCountWithAnyIndexingType(ExecState*, unsigned& startIndex, unsigned count);
JS_EXPORT_PRIVATE bool shiftCountWithArrayStorage(VM&, unsigned startIndex, unsigned count, ArrayStorage*);
bool unshiftCountWithAnyIndexingType(ExecState*, unsigned startIndex, unsigned count);
bool unshiftCountWithArrayStorage(ExecState*, unsigned startIndex, unsigned count, ArrayStorage*);
bool unshiftCountSlowCase(const AbstractLocker&, VM&, DeferGC&, bool, unsigned);
bool setLengthWithArrayStorage(ExecState*, unsigned newLength, bool throwException, ArrayStorage*);
void setLengthWritable(ExecState*, bool writable);
};
inline Butterfly* tryCreateArrayButterfly(VM& vm, JSCell* intendedOwner, unsigned initialLength)
{
Butterfly* butterfly = Butterfly::tryCreate(
vm, intendedOwner, 0, 0, true, baseIndexingHeaderForArrayStorage(initialLength),
ArrayStorage::sizeFor(BASE_ARRAY_STORAGE_VECTOR_LEN));
if (!butterfly)
return nullptr;
ArrayStorage* storage = butterfly->arrayStorage();
storage->m_sparseMap.clear();
storage->m_indexBias = 0;
storage->m_numValuesInVector = 0;
return butterfly;
}
Butterfly* createArrayButterflyInDictionaryIndexingMode(
VM&, JSCell* intendedOwner, unsigned initialLength);
inline JSArray* JSArray::tryCreate(VM& vm, Structure* structure, unsigned initialLength, unsigned vectorLengthHint)
{
ASSERT(vectorLengthHint >= initialLength);
unsigned outOfLineStorage = structure->outOfLineCapacity();
Butterfly* butterfly;
IndexingType indexingType = structure->indexingType();
if (LIKELY(!hasAnyArrayStorage(indexingType))) {
ASSERT(
hasUndecided(indexingType)
|| hasInt32(indexingType)
|| hasDouble(indexingType)
|| hasContiguous(indexingType));
if (UNLIKELY(vectorLengthHint > MAX_STORAGE_VECTOR_LENGTH))
return nullptr;
unsigned vectorLength = Butterfly::optimalContiguousVectorLength(structure, vectorLengthHint);
void* temp = vm.jsValueGigacageAuxiliarySpace.allocateNonVirtual(
vm,
Butterfly::totalSize(0, outOfLineStorage, true, vectorLength * sizeof(EncodedJSValue)),
nullptr, AllocationFailureMode::ReturnNull);
if (!temp)
return nullptr;
butterfly = Butterfly::fromBase(temp, 0, outOfLineStorage);
butterfly->setVectorLength(vectorLength);
butterfly->setPublicLength(initialLength);
if (hasDouble(indexingType))
clearArray(butterfly->contiguousDouble().data(), vectorLength);
else
clearArray(butterfly->contiguous().data(), vectorLength);
} else {
ASSERT(
indexingType == ArrayWithSlowPutArrayStorage
|| indexingType == ArrayWithArrayStorage);
butterfly = tryCreateArrayButterfly(vm, nullptr, initialLength);
if (!butterfly)
return nullptr;
for (unsigned i = 0; i < BASE_ARRAY_STORAGE_VECTOR_LEN; ++i)
butterfly->arrayStorage()->m_vector[i].clear();
}
return createWithButterfly(vm, nullptr, structure, butterfly);
}
inline JSArray* JSArray::tryCreate(VM& vm, Structure* structure, unsigned initialLength)
{
return tryCreate(vm, structure, initialLength, initialLength);
}
inline JSArray* JSArray::create(VM& vm, Structure* structure, unsigned initialLength)
{
JSArray* result = JSArray::tryCreate(vm, structure, initialLength);
RELEASE_ASSERT(result);
return result;
}
inline JSArray* JSArray::createWithButterfly(VM& vm, GCDeferralContext* deferralContext, Structure* structure, Butterfly* butterfly)
{
JSArray* array = new (NotNull, allocateCell<JSArray>(vm.heap, deferralContext)) JSArray(vm, structure, butterfly);
array->finishCreation(vm);
return array;
}
JSArray* asArray(JSValue);
inline JSArray* asArray(JSCell* cell)
{
ASSERT(cell->inherits(*cell->vm(), JSArray::info()));
return jsCast<JSArray*>(cell);
}
inline JSArray* asArray(JSValue value)
{
return asArray(value.asCell());
}
inline bool isJSArray(JSCell* cell)
{
ASSERT((cell->classInfo(*cell->vm()) == JSArray::info()) == (cell->type() == ArrayType));
return cell->type() == ArrayType;
}
inline bool isJSArray(JSValue v) { return v.isCell() && isJSArray(v.asCell()); }
inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const ArgList& values)
{
VM& vm = exec->vm();
unsigned length = values.size();
ObjectInitializationScope scope(vm);
JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
RELEASE_ASSERT(array);
for (unsigned i = 0; i < length; ++i)
array->initializeIndex(scope, i, values.at(i));
return array;
}
inline JSArray* constructArray(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
{
VM& vm = exec->vm();
ObjectInitializationScope scope(vm);
JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
RELEASE_ASSERT(array);
for (unsigned i = 0; i < length; ++i)
array->initializeIndex(scope, i, values[i]);
return array;
}
inline JSArray* constructArrayNegativeIndexed(ExecState* exec, Structure* arrayStructure, const JSValue* values, unsigned length)
{
VM& vm = exec->vm();
ObjectInitializationScope scope(vm);
JSArray* array = JSArray::tryCreateUninitializedRestricted(scope, arrayStructure, length);
RELEASE_ASSERT(array);
for (int i = 0; i < static_cast<int>(length); ++i)
array->initializeIndex(scope, i, values[-i]);
return array;
}
}