|  | /* | 
|  | *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | 
|  | *  Copyright (C) 2003-2021 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 | OverridesPut; | 
|  |  | 
|  | 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*); | 
|  |  | 
|  | static JSArray* fastSlice(JSGlobalObject*, JSObject* source, uint64_t startIndex, uint64_t 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().allocate( | 
|  | 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, 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 |