mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-09 02:10:43 +00:00
320 lines
13 KiB
C++
320 lines
13 KiB
C++
/*
|
|
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
|
|
* Copyright (C) 2003-2020 Apple Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "ArgList.h"
|
|
#include "ArrayConventions.h"
|
|
#include "Butterfly.h"
|
|
#include "JSCell.h"
|
|
#include "JSObject.h"
|
|
|
|
namespace JSC {
|
|
|
|
class JSArray;
|
|
class LLIntOffsetsExtractor;
|
|
|
|
extern const ASCIILiteral LengthExceededTheMaximumArrayLengthError;
|
|
|
|
class JSArray : public JSNonFinalObject {
|
|
friend class LLIntOffsetsExtractor;
|
|
friend class Walker;
|
|
friend class JIT;
|
|
|
|
public:
|
|
typedef JSNonFinalObject Base;
|
|
static constexpr unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetOwnSpecialPropertyNames;
|
|
|
|
static size_t allocationSize(Checked<size_t> inlineCapacity)
|
|
{
|
|
ASSERT_UNUSED(inlineCapacity, !inlineCapacity);
|
|
return sizeof(JSArray);
|
|
}
|
|
|
|
template<typename CellType, SubspaceAccess>
|
|
static IsoSubspace* subspaceFor(VM& vm)
|
|
{
|
|
return &vm.arraySpace;
|
|
}
|
|
|
|
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*);
|
|
|
|
// tryCreateUninitializedRestricted is used for fast construction of arrays whose size and
|
|
// contents are known at time of creation. This is a restricted API for careful use only in
|
|
// performance critical code paths. If you don't have a good reason to use it, you probably
|
|
// shouldn't use it. Instead, you should go with
|
|
// - JSArray::tryCreate() or JSArray::create() instead of tryCreateUninitializedRestricted(), and
|
|
// - putDirectIndex() instead of initializeIndex().
|
|
//
|
|
// Clients of this interface must:
|
|
// - null-check the result (indicating out of memory, or otherwise unable to allocate vector).
|
|
// - call 'initializeIndex' for all properties in sequence, for 0 <= i < initialLength.
|
|
// - Provide a valid GCDefferalContext* if they might garbage collect when initializing properties,
|
|
// otherwise the caller can provide a null GCDefferalContext*.
|
|
// - Provide a local stack instance of ObjectInitializationScope at the call site.
|
|
//
|
|
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);
|
|
}
|
|
|
|
static void eagerlyInitializeButterfly(ObjectInitializationScope&, JSArray*, unsigned initialLength);
|
|
|
|
JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, JSGlobalObject*, PropertyName, const PropertyDescriptor&, bool throwException);
|
|
|
|
JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, PropertyName, PropertySlot&);
|
|
|
|
DECLARE_EXPORT_INFO;
|
|
|
|
// OK if we know this is a JSArray, but not if it could be an object of a derived class; for RuntimeArray this always returns 0.
|
|
unsigned length() const { return getArrayLength(); }
|
|
|
|
// OK to use on new arrays, but not if it might be a RegExpMatchArray or RuntimeArray.
|
|
JS_EXPORT_PRIVATE bool setLength(JSGlobalObject*, unsigned, bool throwException = false);
|
|
|
|
void pushInline(JSGlobalObject*, JSValue);
|
|
JS_EXPORT_PRIVATE void push(JSGlobalObject*, JSValue);
|
|
JS_EXPORT_PRIVATE JSValue pop(JSGlobalObject*);
|
|
|
|
JSArray* fastSlice(JSGlobalObject*, unsigned startIndex, unsigned count);
|
|
|
|
bool canFastCopy(VM&, JSArray* otherArray);
|
|
bool canDoFastIndexedAccess(VM&);
|
|
// This function returns NonArray if the indexing types are not compatable for copying.
|
|
IndexingType mergeIndexingTypeForCopying(IndexingType other);
|
|
bool appendMemcpy(JSGlobalObject*, VM&, unsigned startIndex, JSArray* otherArray);
|
|
|
|
enum ShiftCountMode {
|
|
// This form of shift hints that we're doing queueing. With this assumption in hand,
|
|
// we convert to ArrayStorage, which has queue optimizations.
|
|
ShiftCountForShift,
|
|
|
|
// This form of shift hints that we're just doing care and feeding on an array that
|
|
// is probably typically used for ordinary accesses. With this assumption in hand,
|
|
// we try to preserve whatever indexing type it has already.
|
|
ShiftCountForSplice
|
|
};
|
|
|
|
bool shiftCountForShift(JSGlobalObject* globalObject, unsigned startIndex, unsigned count)
|
|
{
|
|
VM& vm = getVM(globalObject);
|
|
return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
|
|
}
|
|
bool shiftCountForSplice(JSGlobalObject* globalObject, unsigned& startIndex, unsigned count)
|
|
{
|
|
return shiftCountWithAnyIndexingType(globalObject, startIndex, count);
|
|
}
|
|
template<ShiftCountMode shiftCountMode>
|
|
bool shiftCount(JSGlobalObject* globalObject, unsigned& startIndex, unsigned count)
|
|
{
|
|
switch (shiftCountMode) {
|
|
case ShiftCountForShift:
|
|
return shiftCountForShift(globalObject, startIndex, count);
|
|
case ShiftCountForSplice:
|
|
return shiftCountForSplice(globalObject, startIndex, count);
|
|
default:
|
|
CRASH();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool unshiftCountForShift(JSGlobalObject* globalObject, unsigned startIndex, unsigned count)
|
|
{
|
|
return unshiftCountWithArrayStorage(globalObject, startIndex, count, ensureArrayStorage(getVM(globalObject)));
|
|
}
|
|
bool unshiftCountForSplice(JSGlobalObject* globalObject, unsigned startIndex, unsigned count)
|
|
{
|
|
return unshiftCountWithAnyIndexingType(globalObject, startIndex, count);
|
|
}
|
|
template<ShiftCountMode shiftCountMode>
|
|
bool unshiftCount(JSGlobalObject* globalObject, unsigned startIndex, unsigned count)
|
|
{
|
|
switch (shiftCountMode) {
|
|
case ShiftCountForShift:
|
|
return unshiftCountForShift(globalObject, startIndex, count);
|
|
case ShiftCountForSplice:
|
|
return unshiftCountForSplice(globalObject, startIndex, count);
|
|
default:
|
|
CRASH();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
JS_EXPORT_PRIVATE void fillArgList(JSGlobalObject*, MarkedArgumentBuffer&);
|
|
JS_EXPORT_PRIVATE void copyToArguments(JSGlobalObject*, JSValue* 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(jsDynamicCast<JSArray*>(vm, this));
|
|
ASSERT_WITH_MESSAGE(type() == ArrayType || type() == DerivedArrayType, "Instance inheriting JSArray should have either ArrayType or DerivedArrayType");
|
|
}
|
|
|
|
static bool put(JSCell*, JSGlobalObject*, PropertyName, JSValue, PutPropertySlot&);
|
|
|
|
static bool deleteProperty(JSCell*, JSGlobalObject*, PropertyName, DeletePropertySlot&);
|
|
JS_EXPORT_PRIVATE static void getOwnSpecialPropertyNames(JSObject*, JSGlobalObject*, PropertyNameArray&, DontEnumPropertiesMode);
|
|
|
|
private:
|
|
bool isLengthWritable()
|
|
{
|
|
ArrayStorage* storage = arrayStorageOrNull();
|
|
if (!storage)
|
|
return true;
|
|
SparseArrayValueMap* map = storage->m_sparseMap.get();
|
|
return !map || !map->lengthIsReadOnly();
|
|
}
|
|
|
|
bool shiftCountWithAnyIndexingType(JSGlobalObject*, unsigned& startIndex, unsigned count);
|
|
JS_EXPORT_PRIVATE bool shiftCountWithArrayStorage(VM&, unsigned startIndex, unsigned count, ArrayStorage*);
|
|
|
|
bool unshiftCountWithAnyIndexingType(JSGlobalObject*, unsigned startIndex, unsigned count);
|
|
bool unshiftCountWithArrayStorage(JSGlobalObject*, unsigned startIndex, unsigned count, ArrayStorage*);
|
|
bool unshiftCountSlowCase(const AbstractLocker&, VM&, DeferGC&, bool, unsigned);
|
|
|
|
bool setLengthWithArrayStorage(JSGlobalObject*, unsigned newLength, bool throwException, ArrayStorage*);
|
|
void setLengthWritable(JSGlobalObject*, bool writable);
|
|
};
|
|
|
|
inline Butterfly* tryCreateArrayButterfly(VM& vm, JSObject* 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;
|
|
}
|
|
|
|
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<JSArray>(cell->vm()));
|
|
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()); }
|
|
|
|
JS_EXPORT_PRIVATE JSArray* constructArray(JSGlobalObject*, Structure*, const ArgList& values);
|
|
JS_EXPORT_PRIVATE JSArray* constructArray(JSGlobalObject*, Structure*, const JSValue* values, unsigned length);
|
|
JS_EXPORT_PRIVATE JSArray* constructArrayNegativeIndexed(JSGlobalObject*, Structure*, const JSValue* values, unsigned length);
|
|
|
|
} // namespace JSC
|