+ JSObject* thisObject = jsCast<JSObject*>(cell);
+
+ if (propertyName > MAX_ARRAY_INDEX) {
+ PutPropertySlot slot(shouldThrow);
+ thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+ return;
+ }
+
+ switch (thisObject->structure()->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;
+ }
+ // Fall through.
+ }
+
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ Butterfly* butterfly = thisObject->m_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->m_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, this, value);
+ }
+
+ Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, structure(), 0, ArrayStorage::sizeFor(0));
+ RELEASE_ASSERT(newButterfly);
+
+ m_butterfly = newButterfly;
+ newButterfly->arrayStorage()->m_indexBias = 0;
+ newButterfly->arrayStorage()->setVectorLength(0);
+ newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map);
+
+ return newButterfly->arrayStorage();
+}
+
+void JSObject::enterDictionaryIndexingMode(VM& vm)
+{
+ switch (structure()->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(), AddIndexedAccessors), m_butterfly);
+
+ 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 = structure()->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,
+ vm, structure(), structure()->outOfLineCapacity(), false, 0,
+ elementSize * vectorLength);
+ newButterfly->setPublicLength(length);
+ newButterfly->setVectorLength(vectorLength);
+ return newButterfly;
+}
+
+Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length)
+{
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateUndecided);
+ setButterfly(vm, newButterfly, newStructure);
+ return newButterfly;
+}
+
+ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
+{
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateInt32);
+ setButterfly(vm, newButterfly, newStructure);
+ return newButterfly->contiguousInt32();
+}
+
+ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
+{
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double));
+ for (unsigned i = newButterfly->vectorLength(); i--;)
+ newButterfly->contiguousDouble()[i] = QNaN;
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateDouble);
+ setButterfly(vm, newButterfly, newStructure);
+ return newButterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
+{
+ Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue));
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateContiguous);
+ setButterfly(vm, newButterfly, newStructure);
+ return newButterfly->contiguous();
+}
+
+ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength)
+{
+ IndexingType oldType = structure()->indexingType();
+ ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType));
+ Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(m_butterfly,
+ vm, 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());
+ setButterfly(vm, newButterfly, newStructure);
+ return result;
+}
+
+ArrayStorage* JSObject::createInitialArrayStorage(VM& vm)
+{
+ return createArrayStorage(vm, 0, BASE_VECTOR_LEN);
+}
+
+ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
+{
+ ASSERT(hasUndecided(structure()->indexingType()));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32), m_butterfly);
+ return m_butterfly->contiguousInt32();
+}
+
+ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
+{
+ ASSERT(hasUndecided(structure()->indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;)
+ m_butterfly->contiguousDouble()[i] = QNaN;
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly);
+ return m_butterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
+{
+ ASSERT(hasUndecided(structure()->indexingType()));
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
+ return m_butterfly->contiguous();
+}
+
+ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength)
+{
+ unsigned publicLength = m_butterfly->publicLength();
+ unsigned propertyCapacity = structure()->outOfLineCapacity();
+ unsigned propertySize = structure()->outOfLineSize();
+
+ Butterfly* newButterfly = Butterfly::createUninitialized(
+ vm, 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, unsigned neededLength)
+{
+ ASSERT(hasUndecided(structure()->indexingType()));
+
+ ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
+ // No need to copy elements.
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition);
+ setButterfly(vm, storage->butterfly(), newStructure);
+ return storage;
+}
+
+ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ return convertUndecidedToArrayStorage(vm, transition, m_butterfly->vectorLength());
+}
+
+ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
+{
+ return convertUndecidedToArrayStorage(vm, structure()->suggestedArrayStorageTransition());
+}
+
+ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
+{
+ ASSERT(hasInt32(structure()->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 = QNaN;
+ continue;
+ }
+ ASSERT(v.isInt32());
+ *currentAsDouble = v.asInt32();
+ }
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly);
+ return m_butterfly->contiguousDouble();
+}
+
+ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm)
+{
+ ASSERT(hasInt32(structure()->indexingType()));
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
+ return m_butterfly->contiguous();
+}
+
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+{
+ ASSERT(hasInt32(structure()->indexingType()));
+
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
+ for (unsigned i = m_butterfly->publicLength(); i--;) {
+ JSValue v = m_butterfly->contiguous()[i].get();
+ if (!v)
+ continue;
+ newStorage->m_vector[i].setWithoutWriteBarrier(v);
+ newStorage->m_numValuesInVector++;
+ }
+
+ Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition);
+ setButterfly(vm, newStorage->butterfly(), newStructure);
+ return newStorage;
+}
+
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
+{
+ return convertInt32ToArrayStorage(vm, transition, m_butterfly->vectorLength());
+}
+
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
+{
+ return convertInt32ToArrayStorage(vm, structure()->suggestedArrayStorageTransition());
+}
+
+template<JSObject::DoubleToContiguousMode mode>
+ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
+{
+ ASSERT(hasDouble(structure()->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;
+ switch (mode) {
+ case EncodeValueAsDouble:
+ v = JSValue(JSValue::EncodeAsDouble, value);
+ break;
+ case RageConvertDoubleToValue:
+ v = jsNumber(value);
+ break;
+ }
+ ASSERT(v.isNumber());
+ currentAsValue->setWithoutWriteBarrier(v);
+ }
+
+ setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly);
+ return m_butterfly->contiguous();