+void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
+{
+ JSObject* thisObject = jsCast<JSObject*>(cell);
+
+ if (propertyName > MAX_ARRAY_INDEX) {
+ PutPropertySlot slot(cell, shouldThrow);
+ thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+ return;
+ }
+
+ switch (thisObject->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ break;
+
+ case ALL_UNDECIDED_INDEXING_TYPES: {
+ thisObject->convertUndecidedForValue(exec->vm(), value);
+ // Reloop.
+ putByIndex(cell, exec, propertyName, value, shouldThrow);
+ return;
+ }
+
+ case ALL_INT32_INDEXING_TYPES: {
+ if (!value.isInt32()) {
+ thisObject->convertInt32ForValue(exec->vm(), value);
+ putByIndex(cell, exec, propertyName, value, shouldThrow);
+ return;
+ }
+ FALLTHROUGH;
+ }
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ Butterfly* butterfly = thisObject->butterfly();
+ if (propertyName >= butterfly->vectorLength())
+ break;
+ butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value);
+ if (propertyName >= butterfly->publicLength())
+ butterfly->setPublicLength(propertyName + 1);
+ return;
+ }
+
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ if (!value.isNumber()) {
+ thisObject->convertDoubleToContiguous(exec->vm());
+ // Reloop.
+ putByIndex(cell, exec, propertyName, value, shouldThrow);
+ return;
+ }
+ double valueAsDouble = value.asNumber();
+ if (valueAsDouble != valueAsDouble) {
+ thisObject->convertDoubleToContiguous(exec->vm());
+ // Reloop.
+ putByIndex(cell, exec, propertyName, value, shouldThrow);
+ return;
+ }
+ Butterfly* butterfly = thisObject->butterfly();
+ if (propertyName >= butterfly->vectorLength())
+ break;
+ butterfly->contiguousDouble()[propertyName] = valueAsDouble;
+ if (propertyName >= butterfly->publicLength())
+ butterfly->setPublicLength(propertyName + 1);
+ return;
+ }
+
+ case NonArrayWithArrayStorage:
+ case ArrayWithArrayStorage: {
+ ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
+
+ if (propertyName >= storage->vectorLength())
+ break;
+
+ WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
+ unsigned length = storage->length();
+
+ // Update length & m_numValuesInVector as necessary.
+ if (propertyName >= length) {
+ length = propertyName + 1;
+ storage->setLength(length);
+ ++storage->m_numValuesInVector;
+ } else if (!valueSlot)
+ ++storage->m_numValuesInVector;
+
+ valueSlot.set(exec->vm(), thisObject, value);
+ return;
+ }
+
+ case NonArrayWithSlowPutArrayStorage:
+ case ArrayWithSlowPutArrayStorage: {
+ ArrayStorage* storage = thisObject->m_butterfly->arrayStorage();
+
+ if (propertyName >= storage->vectorLength())
+ break;
+
+ WriteBarrier<Unknown>& valueSlot = storage->m_vector[propertyName];
+ unsigned length = storage->length();
+
+ // Update length & m_numValuesInVector as necessary.
+ if (propertyName >= length) {
+ if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
+ return;
+ length = propertyName + 1;
+ storage->setLength(length);
+ ++storage->m_numValuesInVector;
+ } else if (!valueSlot) {
+ if (thisObject->attemptToInterceptPutByIndexOnHole(exec, propertyName, value, shouldThrow))
+ return;
+ ++storage->m_numValuesInVector;
+ }
+
+ valueSlot.set(exec->vm(), thisObject, value);
+ return;
+ }
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ thisObject->putByIndexBeyondVectorLength(exec, propertyName, value, shouldThrow);
+}
+
+ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM& vm, ArrayStorage* storage)
+{
+ SparseArrayValueMap* map = storage->m_sparseMap.get();
+
+ if (!map)
+ map = allocateSparseIndexMap(vm);
+
+ if (map->sparseMode())
+ return storage;
+
+ map->setSparseMode();
+
+ unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength());
+ for (unsigned i = 0; i < usedVectorLength; ++i) {
+ JSValue value = storage->m_vector[i].get();
+ // This will always be a new entry in the map, so no need to check we can write,
+ // and attributes are default so no need to set them.
+ if (value)
+ map->add(this, i).iterator->value.set(vm, map, value);
+ }
+
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0));
+ RELEASE_ASSERT(newButterfly);
+ newButterfly->arrayStorage()->m_indexBias = 0;
+ newButterfly->arrayStorage()->setVectorLength(0);
+ newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
+ setButterflyWithoutChangingStructure(vm, newButterfly);
+
+ return newButterfly->arrayStorage();
+}
+
+void JSObject::enterDictionaryIndexingMode(VM& vm)
+{
+ switch (indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ case ALL_INT32_INDEXING_TYPES:
+ case ALL_DOUBLE_INDEXING_TYPES:
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize
+ // this case if we ever cared.
+ enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, ensureArrayStorageSlow(vm));
+ break;
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage());
+ break;
+
+ default:
+ break;
+ }
+}
+
+void JSObject::notifyPresenceOfIndexedAccessors(VM& vm)
+{
+ if (mayInterceptIndexedAccesses())
+ return;
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors));
+
+ if (!vm.prototypeMap.isPrototype(this))
+ return;
+
+ globalObject()->haveABadTime(vm);
+}
+
+Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize)
+{
+ ASSERT(length < MAX_ARRAY_INDEX);
+ IndexingType oldType = indexingType();
+ ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
+ ASSERT(!structure()->needsSlowPutIndexing());
+ ASSERT(!indexingShouldBeSparse());
+ unsigned vectorLength = std::max(length, BASE_VECTOR_LEN);
+ Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
+ m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0,
+ elementSize * vectorLength);
+ newButterfly->setPublicLength(length);
+ newButterfly->setVectorLength(vectorLength);
+ return newButterfly;
+}
+
+Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
+{
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided);
+ setStructureAndButterfly(vm, newStructure, newButterfly);
+ return newButterfly;
+}
+
+ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
+{
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32);
+ setStructureAndButterfly(vm, newStructure, newButterfly);
+ return newButterfly->contiguousInt32();
+}
+
+ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
+{
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
+ for (unsigned i = newButterfly->vectorLength(); i--;)
+ newButterfly->contiguousDouble()[i] = PNaN;
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);
+ setStructureAndButterfly(vm, newStructure, newButterfly);
+ return newButterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
+{
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous);
+ setStructureAndButterfly(vm, newStructure, newButterfly);
+ return newButterfly->contiguous();
+}
+
+ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
+{
+ DeferGC deferGC(vm.heap);
+ Structure* structure = this->structure(vm);
+ IndexingType oldType = indexingType();
+ ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
+ Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(
+ m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0,
+ ArrayStorage::sizeFor(vectorLength));
+ RELEASE_ASSERT(newButterfly);
+
+ ArrayStorage* result = newButterfly->arrayStorage();
+ result->setLength(length);
+ result->setVectorLength(vectorLength);
+ result->m_sparseMap.clear();
+ result->m_numValuesInVector = 0;
+ result->m_indexBias = 0;
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition());
+ setStructureAndButterfly(vm, newStructure, newButterfly);
+ return result;
+}
+
+ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
+{
+ return createArrayStorage(vm, 0, BASE_VECTOR_LEN);
+}
+
+ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
+{
+ ASSERT(hasUndecided(indexingType()));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32));
+ return m_butterfly->contiguousInt32();
+}
+
+ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
+{
+ ASSERT(hasUndecided(indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;)
+ m_butterfly->contiguousDouble()[i] = PNaN;
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
+ return m_butterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
+{
+ ASSERT(hasUndecided(indexingType()));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ return m_butterfly->contiguous();
+}
+
+ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
+{
+ Structure* structure = this->structure(vm);
+ unsigned publicLength = m_butterfly->publicLength();
+ unsigned propertyCapacity = structure->outOfLineCapacity();
+ unsigned propertySize = structure->outOfLineSize();
+
+ Butterfly* newButterfly = Butterfly::createUninitialized(
+ vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength));
+
+ memcpy(
+ newButterfly->propertyStorage() - propertySize,
+ m_butterfly->propertyStorage() - propertySize,
+ propertySize * sizeof(EncodedJSValue));
+
+ ArrayStorage* newStorage = newButterfly->arrayStorage();
+ newStorage->setVectorLength(neededLength);
+ newStorage->setLength(publicLength);
+ newStorage->m_sparseMap.clear();
+ newStorage->m_indexBias = 0;
+ newStorage->m_numValuesInVector = 0;
+
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ DeferGC deferGC(vm.heap);
+ ASSERT(hasUndecided(indexingType()));
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ // No need to copy elements.
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+ setStructureAndButterfly(vm, newStructure, storage->butterfly());
+ return storage;
+}
+
+ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
+{
+ return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
+}
+
+ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
+{
+ ASSERT(hasInt32(indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;) {
+ WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i];
+ double* currentAsDouble = bitwise_cast<double*>(current);
+ JSValue v = current->get();
+ if (!v) {
+ *currentAsDouble = PNaN;
+ continue;
+ }
+ ASSERT(v.isInt32());
+ *currentAsDouble = v.asInt32();
+ }
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));
+ return m_butterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
+{
+ ASSERT(hasInt32(indexingType()));
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ return m_butterfly->contiguous();
+}
+
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ DeferGC deferGC(vm.heap);
+ ASSERT(hasInt32(indexingType()));
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
+ JSValue v = m_butterfly->contiguous()[i].get();
+ if (v) {
+ newStorage->m_vector[i].setWithoutWriteBarrier(v);
+ newStorage->m_numValuesInVector++;
+ } else
+ ASSERT(newStorage->m_vector[i].get().isEmpty());
+ }
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+ setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
+{
+ return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
+}
+
+ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
+{
+ ASSERT(hasDouble(indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;) {
+ double* current = &m_butterfly->contiguousDouble()[i];
+ WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
+ double value = *current;
+ if (value != value) {
+ currentAsValue->clear();
+ continue;
+ }
+ JSValue v = JSValue(JSValue::EncodeAsDouble, value);
+ currentAsValue->setWithoutWriteBarrier(v);
+ }
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));
+ return m_butterfly->contiguous();
+}
+
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ DeferGC deferGC(vm.heap);
+ ASSERT(hasDouble(indexingType()));
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
+ double value = m_butterfly->contiguousDouble()[i];
+ if (value == value) {
+ newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
+ newStorage->m_numValuesInVector++;
+ } else
+ ASSERT(newStorage->m_vector[i].get().isEmpty());
+ }
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+ setStructureAndButterfly(vm, newStructure, newStorage->butterfly());
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm)
+{
+ return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
+}
+
+ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)