+ ensureLength(vm, i + 1);
+
+ RELEASE_ASSERT(i < m_butterfly->vectorLength());
+ switch (indexingShape) {
+ case Int32Shape:
+ ASSERT(value.isInt32());
+ m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value);
+ break;
+
+ case DoubleShape: {
+ ASSERT(value.isNumber());
+ double valueAsDouble = value.asNumber();
+ ASSERT(valueAsDouble == valueAsDouble);
+ m_butterfly->contiguousDouble()[i] = valueAsDouble;
+ break;
+ }
+
+ case ContiguousShape:
+ m_butterfly->contiguous()[i].set(vm, this, value);
+ break;
+
+ default:
+ CRASH();
+ }
+}
+
+void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage)
+{
+ VM& vm = exec->vm();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(i <= MAX_ARRAY_INDEX);
+ ASSERT(i >= storage->vectorLength());
+
+ SparseArrayValueMap* map = storage->m_sparseMap.get();
+
+ // First, handle cases where we don't currently have a sparse map.
+ if (LIKELY(!map)) {
+ // If the array is not extensible, we should have entered dictionary mode, and created the sparse map.
+ ASSERT(isExtensible());
+
+ // Update m_length if necessary.
+ if (i >= storage->length())
+ storage->setLength(i + 1);
+
+ // Check that it is sensible to still be using a vector, and then try to grow the vector.
+ if (LIKELY(!indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength())
+ && isDenseEnoughForVector(i, storage->m_numValuesInVector)
+ && increaseVectorLength(vm, i + 1))) {
+ // success! - reread m_storage since it has likely been reallocated, and store to the vector.
+ storage = arrayStorage();
+ storage->m_vector[i].set(vm, this, value);
+ ++storage->m_numValuesInVector;
+ return;
+ }
+ // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
+ map = allocateSparseIndexMap(exec->vm());
+ map->putEntry(exec, this, i, value, shouldThrow);
+ return;
+ }
+
+ // Update m_length if necessary.
+ unsigned length = storage->length();
+ if (i >= length) {
+ // Prohibit growing the array if length is not writable.
+ if (map->lengthIsReadOnly() || !isExtensible()) {
+ if (shouldThrow)
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+ return;
+ }
+ length = i + 1;
+ storage->setLength(length);
+ }
+
+ // We are currently using a map - check whether we still want to be doing so.
+ // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
+ unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
+ if (map->sparseMode() || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length)) {
+ map->putEntry(exec, this, i, value, shouldThrow);
+ return;
+ }
+
+ // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
+ storage = arrayStorage();
+ storage->m_numValuesInVector = numValuesInArray;
+
+ // Copy all values from the map into the vector, and delete the map.
+ WriteBarrier<Unknown>* vector = storage->m_vector;
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
+ vector[it->key].set(vm, this, it->value.getNonSparseMode());
+ deallocateSparseIndexMap();
+
+ // Store the new property into the vector.
+ WriteBarrier<Unknown>& valueSlot = vector[i];
+ if (!valueSlot)
+ ++storage->m_numValuesInVector;
+ valueSlot.set(vm, this, value);
+}
+
+void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, bool shouldThrow)
+{
+ VM& vm = exec->vm();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(i <= MAX_ARRAY_INDEX);
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES: {
+ if (indexingShouldBeSparse()) {
+ putByIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, shouldThrow,
+ ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
+ break;
+ }
+ if (indexIsSufficientlyBeyondLengthForSparseMap(i, 0) || i >= MIN_SPARSE_ARRAY_INDEX) {
+ putByIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0));
+ break;
+ }
+ if (structure(vm)->needsSlowPutIndexing()) {
+ ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
+ storage->m_vector[i].set(vm, this, value);
+ storage->m_numValuesInVector++;
+ break;
+ }
+
+ createInitialForValueAndSet(vm, i, value);
+ break;
+ }
+
+ case ALL_UNDECIDED_INDEXING_TYPES: {
+ CRASH();
+ break;
+ }
+
+ case ALL_INT32_INDEXING_TYPES: {
+ putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
+ break;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
+ break;
+ }
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
+ break;
+ }
+
+ case NonArrayWithSlowPutArrayStorage:
+ case ArrayWithSlowPutArrayStorage: {
+ // No own property present in the vector, but there might be in the sparse map!
+ SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get();
+ if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow))
+ return;
+ FALLTHROUGH;
+ }
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage:
+ putByIndexBeyondVectorLengthWithArrayStorage(exec, i, value, shouldThrow, arrayStorage());
+ break;
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+}
+
+bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode, ArrayStorage* storage)
+{
+ VM& vm = exec->vm();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(hasAnyArrayStorage(indexingType()));
+ ASSERT(arrayStorage() == storage);
+ ASSERT(i >= storage->vectorLength() || attributes);
+ ASSERT(i <= MAX_ARRAY_INDEX);
+
+ SparseArrayValueMap* map = storage->m_sparseMap.get();
+
+ // First, handle cases where we don't currently have a sparse map.
+ if (LIKELY(!map)) {
+ // If the array is not extensible, we should have entered dictionary mode, and created the spare map.
+ ASSERT(isExtensible());
+
+ // Update m_length if necessary.
+ if (i >= storage->length())
+ storage->setLength(i + 1);
+
+ // Check that it is sensible to still be using a vector, and then try to grow the vector.
+ if (LIKELY(
+ !attributes
+ && (isDenseEnoughForVector(i, storage->m_numValuesInVector))
+ && !indexIsSufficientlyBeyondLengthForSparseMap(i, storage->vectorLength()))
+ && increaseVectorLength(vm, i + 1)) {
+ // success! - reread m_storage since it has likely been reallocated, and store to the vector.
+ storage = arrayStorage();
+ storage->m_vector[i].set(vm, this, value);
+ ++storage->m_numValuesInVector;
+ return true;
+ }
+ // We don't want to, or can't use a vector to hold this property - allocate a sparse map & add the value.
+ map = allocateSparseIndexMap(exec->vm());
+ return map->putDirect(exec, this, i, value, attributes, mode);
+ }
+
+ // Update m_length if necessary.
+ unsigned length = storage->length();
+ if (i >= length) {
+ if (mode != PutDirectIndexLikePutDirect) {
+ // Prohibit growing the array if length is not writable.
+ if (map->lengthIsReadOnly())
+ return reject(exec, mode == PutDirectIndexShouldThrow, StrictModeReadonlyPropertyWriteError);
+ if (!isExtensible())
+ return reject(exec, mode == PutDirectIndexShouldThrow, "Attempting to define property on object that is not extensible.");
+ }
+ length = i + 1;
+ storage->setLength(length);
+ }
+
+ // We are currently using a map - check whether we still want to be doing so.
+ // We will continue to use a sparse map if SparseMode is set, a vector would be too sparse, or if allocation fails.
+ unsigned numValuesInArray = storage->m_numValuesInVector + map->size();
+ if (map->sparseMode() || attributes || !isDenseEnoughForVector(length, numValuesInArray) || !increaseVectorLength(exec->vm(), length))
+ return map->putDirect(exec, this, i, value, attributes, mode);
+
+ // Reread m_storage after increaseVectorLength, update m_numValuesInVector.
+ storage = arrayStorage();
+ storage->m_numValuesInVector = numValuesInArray;
+
+ // Copy all values from the map into the vector, and delete the map.
+ WriteBarrier<Unknown>* vector = storage->m_vector;
+ SparseArrayValueMap::const_iterator end = map->end();
+ for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it)
+ vector[it->key].set(vm, this, it->value.getNonSparseMode());
+ deallocateSparseIndexMap();
+
+ // Store the new property into the vector.
+ WriteBarrier<Unknown>& valueSlot = vector[i];
+ if (!valueSlot)
+ ++storage->m_numValuesInVector;
+ valueSlot.set(vm, this, value);
+ return true;
+}
+
+bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue value, unsigned attributes, PutDirectIndexMode mode)
+{
+ VM& vm = exec->vm();
+
+ // i should be a valid array index that is outside of the current vector.
+ ASSERT(i <= MAX_ARRAY_INDEX);
+
+ if (attributes & (ReadOnly | Accessor))
+ notifyPresenceOfIndexedAccessors(vm);
+
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES: {
+ if (indexingShouldBeSparse() || attributes) {
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, attributes, mode,
+ ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm));
+ }
+ if (i >= MIN_SPARSE_ARRAY_INDEX) {
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0));
+ }
+ if (structure(vm)->needsSlowPutIndexing()) {
+ ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1));
+ storage->m_vector[i].set(vm, this, value);
+ storage->m_numValuesInVector++;
+ return true;
+ }
+
+ createInitialForValueAndSet(vm, i, value);
+ return true;
+ }
+
+ case ALL_UNDECIDED_INDEXING_TYPES: {
+ convertUndecidedForValue(exec->vm(), value);
+ // Reloop.
+ return putDirectIndex(exec, i, value, attributes, mode);
+ }
+
+ case ALL_INT32_INDEXING_TYPES: {
+ if (attributes & (ReadOnly | Accessor)) {
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm));
+ }
+ if (!value.isInt32()) {
+ convertInt32ForValue(vm, value);
+ return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
+ }
+ putByIndexBeyondVectorLengthWithoutAttributes<Int32Shape>(exec, i, value);
+ return true;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ if (attributes & (ReadOnly | Accessor)) {
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm));
+ }
+ if (!value.isNumber()) {
+ convertDoubleToContiguous(vm);
+ return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
+ }
+ double valueAsDouble = value.asNumber();
+ if (valueAsDouble != valueAsDouble) {
+ convertDoubleToContiguous(vm);
+ return putDirectIndexBeyondVectorLength(exec, i, value, attributes, mode);
+ }
+ putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(exec, i, value);
+ return true;
+ }
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ if (attributes & (ReadOnly | Accessor)) {
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(
+ exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm));
+ }
+ putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(exec, i, value);
+ return true;
+ }
+
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage());
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
+void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
+{
+ StringImpl* name = propertyName.publicName();
+ if (!name)
+ name = vm.propertyNames->anonymous.impl();
+ ASSERT(name);
+
+ JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
+ putDirect(vm, propertyName, function, attributes);
+}
+
+JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
+{
+ StringImpl* name = propertyName.publicName();
+ if (!name)
+ name = vm.propertyNames->anonymous.impl();
+ ASSERT(name);
+ JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
+ putDirect(vm, propertyName, function, attributes);
+ return function;
+}
+
+JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes)
+{
+ StringImpl* name = propertyName.publicName();
+ if (!name)
+ name = vm.propertyNames->anonymous.impl();
+ ASSERT(name);
+ JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast<FunctionExecutable*>(functionExecutable), globalObject);
+ putDirectWithoutTransition(vm, propertyName, function, attributes);
+ return function;
+}
+
+void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes)
+{
+ StringImpl* name = propertyName.publicName();
+ ASSERT(name);
+
+ JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic);
+ putDirectWithoutTransition(vm, propertyName, function, attributes);
+}
+
+ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength)
+{
+ ASSERT(desiredLength <= MAX_STORAGE_VECTOR_LENGTH);
+
+ unsigned increasedLength;
+ unsigned maxInitLength = std::min(currentLength, 100000U);
+
+ if (desiredLength < maxInitLength)
+ increasedLength = maxInitLength;
+ else if (!currentVectorLength)
+ increasedLength = std::max(desiredLength, lastArraySize);
+ else {
+ increasedLength = timesThreePlusOneDividedByTwo(desiredLength);
+ }
+
+ ASSERT(increasedLength >= desiredLength);
+
+ lastArraySize = std::min(increasedLength, FIRST_VECTOR_GROW);
+
+ return std::min(increasedLength, MAX_STORAGE_VECTOR_LENGTH);
+}