We should have a CoW storage for NewArrayBuffer arrays.
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 May 2018 18:04:31 +0000 (18:04 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 May 2018 18:04:31 +0000 (18:04 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185003

Reviewed by Filip Pizlo.

JSTests:

* stress/cow-convert-contiguous-to-array-storage.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/cow-convert-double-to-array-storage.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/cow-convert-double-to-contiguous.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/cow-convert-int32-to-array-storage.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/cow-convert-int32-to-contiguous.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/cow-convert-int32-to-double.js: Added.
(createBuffer):
(shouldBe):
(test):
* stress/put-on-cow-prototype.js: Added.
(putByVal):
(putById):

Source/JavaScriptCore:

This patch adds copy on write storage for new array buffers. In
order to do this there needed to be significant changes to the
layout of IndexingType. The new indexing type has the following
shape:

struct IndexingTypeAndMisc {
    struct IndexingModeIncludingHistory {
        struct IndexingMode {
            struct IndexingType {
                uint8_t isArray:1;          // bit 0
                uint8_t shape:3;            // bit 1 - 3
            };
            uint8_t copyOnWrite:1;          // bit 4
        };
        uint8_t mayHaveIndexedAccessors:1;  // bit 5
    };
    uint8_t cellLockBits:2;                 // bit 6 - 7
};

For simplicity ArrayStorage shapes cannot be CoW. So the only
valid CoW indexing shapes are ArrayWithInt32, ArrayWithDouble, and
ArrayWithContiguous.

The backing store for a CoW array is a new class
JSImmutableButterfly, which looks exactly the same as a normal
butterfly except that it has a JSCell header. Like other
butterflies, JSImmutableButterfies are allocated out of the
Auxiliary Gigacage and are pointed to by JSCells in the same
way. However, when marking JSImmutableButterflies they are marked
as if they were a property.

With CoW arrays, the new_array_buffer bytecode will reallocate the
shared JSImmutableButterfly if it sees from the allocation profile
that the last array it allocated has transitioned to a different
indexing type. From then on, all arrays created by that
new_array_buffer bytecode will have the promoted indexing
type. This is more or less the same as what we used to do. The
only difference is that we don't promote all the way to array
storage even if we have seen it before.

Transitioning from a CoW indexing mode occurs whenever someone
tries to store to an element, grow the array, or add properties.
Storing or growing the array will call into code that does the
stupid thing of copying the butterfly then continue into the old
code. This doesn't end up costing us as future allocations will
use any upgraded indexing shape.  We get adding properties for
free by just changing the indexing mode on transition (our C++
code always updates the indexing mode).

* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecode/ArrayAllocationProfile.cpp:
(JSC::ArrayAllocationProfile::updateProfile):
* bytecode/ArrayAllocationProfile.h:
(JSC::ArrayAllocationProfile::initializeIndexingMode):
* bytecode/ArrayProfile.cpp:
(JSC::dumpArrayModes):
(JSC::ArrayProfile::briefDescriptionWithoutUpdating):
* bytecode/ArrayProfile.h:
(JSC::asArrayModes):
(JSC::arrayModeFromStructure):
(JSC::arrayModesInclude):
(JSC::hasSeenCopyOnWriteArray):
* bytecode/BytecodeList.json:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::finishCreation):
* bytecode/InlineAccess.cpp:
(JSC::InlineAccess::generateArrayLength):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addArrayAllocationProfile):
(JSC::UnlinkedCodeBlock::decompressArrayAllocationProfile):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::newArrayAllocationProfile):
(JSC::BytecodeGenerator::emitNewArrayBuffer):
(JSC::BytecodeGenerator::emitNewArray):
(JSC::BytecodeGenerator::emitNewArrayWithSize):
(JSC::BytecodeGenerator::emitExpectedFunctionSnippet):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::ArrayNode::emitBytecode):
(JSC::ArrayPatternNode::bindValue const):
(JSC::ArrayPatternNode::emitDirectBinding):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGArgumentsEliminationPhase.cpp:
* dfg/DFGArgumentsUtilities.cpp:
(JSC::DFG::emitCodeToGetArgumentsArrayLength):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::fromObserved):
(JSC::DFG::ArrayMode::refine const):
(JSC::DFG::ArrayMode::alreadyChecked const):
* dfg/DFGArrayMode.h:
(JSC::DFG::ArrayMode::ArrayMode):
(JSC::DFG::ArrayMode::action const):
(JSC::DFG::ArrayMode::withSpeculation const):
(JSC::DFG::ArrayMode::withArrayClass const):
(JSC::DFG::ArrayMode::withType const):
(JSC::DFG::ArrayMode::withConversion const):
(JSC::DFG::ArrayMode::withTypeAndConversion const):
(JSC::DFG::ArrayMode::arrayModesThatPassFiltering const):
(JSC::DFG::ArrayMode::arrayModesWithIndexingShape const):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
(JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::attemptToForceStringArrayModeByToStringConversion):
(JSC::DFG::FixupPhase::attemptToMakeGetArrayLength):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
* dfg/DFGNode.h:
(JSC::DFG::Node::indexingType):
(JSC::DFG::Node::indexingMode):
* dfg/DFGOSRExit.cpp:
(JSC::DFG::OSRExit::compileExit):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::emitAllocateRawObject):
(JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode):
(JSC::DFG::SpeculativeJIT::arrayify):
(JSC::DFG::SpeculativeJIT::compileGetByValOnString):
(JSC::DFG::SpeculativeJIT::compileGetByValOnDirectArguments):
(JSC::DFG::SpeculativeJIT::compileGetByValOnScopedArguments):
(JSC::DFG::SpeculativeJIT::compileGetArrayLength):
(JSC::DFG::SpeculativeJIT::compileCreateRest):
(JSC::DFG::SpeculativeJIT::compileArraySlice):
(JSC::DFG::SpeculativeJIT::compileNewArrayBuffer):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compilePutStructure):
(JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayBuffer):
(JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
(JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargsWithSpread):
(JSC::FTL::DFG::LowerDFGToB3::storeStructure):
(JSC::FTL::DFG::LowerDFGToB3::isArrayTypeForArrayify):
* ftl/FTLOperations.cpp:
(JSC::FTL::operationMaterializeObjectInOSR):
* generate-bytecode-files:
* interpreter/Interpreter.cpp:
(JSC::sizeOfVarargs):
(JSC::loadVarargs):
* jit/AssemblyHelpers.cpp:
(JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
* jit/JITOperations.cpp:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_put_by_val):
(JSC::JIT::emitSlow_op_put_by_val):
* jit/Repatch.cpp:
(JSC::tryCachePutByID):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/Butterfly.h:
(JSC::ContiguousData::Data::Data):
(JSC::ContiguousData::Data::operator bool const):
(JSC::ContiguousData::Data::operator=):
(JSC::ContiguousData::Data::operator const T& const):
(JSC::ContiguousData::Data::set):
(JSC::ContiguousData::Data::setWithoutWriteBarrier):
(JSC::ContiguousData::Data::clear):
(JSC::ContiguousData::Data::get const):
(JSC::ContiguousData::atUnsafe):
(JSC::ContiguousData::at const): Deleted.
(JSC::ContiguousData::at): Deleted.
* runtime/ButterflyInlines.h:
(JSC::ContiguousData<T>::at const):
(JSC::ContiguousData<T>::at):
* runtime/ClonedArguments.cpp:
(JSC::ClonedArguments::createEmpty):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
(JSC::CommonSlowPaths::allocateNewArrayBuffer):
* runtime/IndexingType.cpp:
(JSC::leastUpperBoundOfIndexingTypeAndType):
(JSC::leastUpperBoundOfIndexingTypeAndValue):
(JSC::dumpIndexingType):
* runtime/IndexingType.h:
(JSC::hasIndexedProperties):
(JSC::hasUndecided):
(JSC::hasInt32):
(JSC::hasDouble):
(JSC::hasContiguous):
(JSC::hasArrayStorage):
(JSC::hasAnyArrayStorage):
(JSC::hasSlowPutArrayStorage):
(JSC::shouldUseSlowPut):
(JSC::isCopyOnWrite):
(JSC::arrayIndexFromIndexingType):
* runtime/JSArray.cpp:
(JSC::JSArray::tryCreateUninitializedRestricted):
(JSC::JSArray::put):
(JSC::JSArray::appendMemcpy):
(JSC::JSArray::setLength):
(JSC::JSArray::pop):
(JSC::JSArray::fastSlice):
(JSC::JSArray::shiftCountWithAnyIndexingType):
(JSC::JSArray::unshiftCountWithAnyIndexingType):
(JSC::JSArray::fillArgList):
(JSC::JSArray::copyToArguments):
* runtime/JSArrayInlines.h:
(JSC::JSArray::pushInline):
* runtime/JSCell.h:
* runtime/JSCellInlines.h:
(JSC::JSCell::JSCell):
(JSC::JSCell::finishCreation):
(JSC::JSCell::indexingType const):
(JSC::JSCell::indexingMode const):
(JSC::JSCell::setStructure):
* runtime/JSFixedArray.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::haveABadTime):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::originalArrayStructureForIndexingType const):
(JSC::JSGlobalObject::arrayStructureForIndexingTypeDuringAllocation const):
(JSC::JSGlobalObject::isOriginalArrayStructure):
* runtime/JSImmutableButterfly.cpp: Added.
(JSC::JSImmutableButterfly::visitChildren):
(JSC::JSImmutableButterfly::copyToArguments):
* runtime/JSImmutableButterfly.h: Added.
(JSC::JSImmutableButterfly::createStructure):
(JSC::JSImmutableButterfly::tryCreate):
(JSC::JSImmutableButterfly::create):
(JSC::JSImmutableButterfly::publicLength const):
(JSC::JSImmutableButterfly::vectorLength const):
(JSC::JSImmutableButterfly::length const):
(JSC::JSImmutableButterfly::toButterfly const):
(JSC::JSImmutableButterfly::fromButterfly):
(JSC::JSImmutableButterfly::get const):
(JSC::JSImmutableButterfly::subspaceFor):
(JSC::JSImmutableButterfly::setIndex):
(JSC::JSImmutableButterfly::allocationSize):
(JSC::JSImmutableButterfly::JSImmutableButterfly):
* runtime/JSObject.cpp:
(JSC::JSObject::markAuxiliaryAndVisitOutOfLineProperties):
(JSC::JSObject::visitButterflyImpl):
(JSC::JSObject::getOwnPropertySlotByIndex):
(JSC::JSObject::putByIndex):
(JSC::JSObject::createInitialInt32):
(JSC::JSObject::createInitialDouble):
(JSC::JSObject::createInitialContiguous):
(JSC::JSObject::convertUndecidedToInt32):
(JSC::JSObject::convertUndecidedToDouble):
(JSC::JSObject::convertUndecidedToContiguous):
(JSC::JSObject::convertInt32ToDouble):
(JSC::JSObject::convertInt32ToArrayStorage):
(JSC::JSObject::convertDoubleToContiguous):
(JSC::JSObject::convertDoubleToArrayStorage):
(JSC::JSObject::convertContiguousToArrayStorage):
(JSC::JSObject::createInitialForValueAndSet):
(JSC::JSObject::convertInt32ForValue):
(JSC::JSObject::convertFromCopyOnWrite):
(JSC::JSObject::ensureWritableInt32Slow):
(JSC::JSObject::ensureWritableDoubleSlow):
(JSC::JSObject::ensureWritableContiguousSlow):
(JSC::JSObject::ensureArrayStorageSlow):
(JSC::JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode):
(JSC::JSObject::switchToSlowPutArrayStorage):
(JSC::JSObject::deletePropertyByIndex):
(JSC::JSObject::getOwnPropertyNames):
(JSC::canDoFastPutDirectIndex):
(JSC::JSObject::defineOwnIndexedProperty):
(JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes):
(JSC::JSObject::putByIndexBeyondVectorLengthWithArrayStorage):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::countElements):
(JSC::JSObject::ensureLengthSlow):
(JSC::JSObject::getEnumerableLength):
(JSC::JSObject::ensureInt32Slow): Deleted.
(JSC::JSObject::ensureDoubleSlow): Deleted.
(JSC::JSObject::ensureContiguousSlow): Deleted.
* runtime/JSObject.h:
(JSC::JSObject::putDirectIndex):
(JSC::JSObject::canGetIndexQuickly):
(JSC::JSObject::getIndexQuickly):
(JSC::JSObject::tryGetIndexQuickly const):
(JSC::JSObject::canSetIndexQuickly):
(JSC::JSObject::setIndexQuickly):
(JSC::JSObject::initializeIndex):
(JSC::JSObject::initializeIndexWithoutBarrier):
(JSC::JSObject::ensureWritableInt32):
(JSC::JSObject::ensureWritableDouble):
(JSC::JSObject::ensureWritableContiguous):
(JSC::JSObject::ensureLength):
(JSC::JSObject::ensureInt32): Deleted.
(JSC::JSObject::ensureDouble): Deleted.
(JSC::JSObject::ensureContiguous): Deleted.
* runtime/JSObjectInlines.h:
(JSC::JSObject::putDirectInternal):
* runtime/JSType.h:
* runtime/RegExpMatchesArray.h:
(JSC::tryCreateUninitializedRegExpMatchesArray):
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::addNewPropertyTransition):
(JSC::Structure::nonPropertyTransition):
* runtime/Structure.h:
* runtime/StructureIDBlob.h:
(JSC::StructureIDBlob::StructureIDBlob):
(JSC::StructureIDBlob::indexingModeIncludingHistory const):
(JSC::StructureIDBlob::setIndexingModeIncludingHistory):
(JSC::StructureIDBlob::indexingModeIncludingHistoryOffset):
(JSC::StructureIDBlob::indexingTypeIncludingHistory const): Deleted.
(JSC::StructureIDBlob::setIndexingTypeIncludingHistory): Deleted.
(JSC::StructureIDBlob::indexingTypeIncludingHistoryOffset): Deleted.
* runtime/StructureTransitionTable.h:
(JSC::newIndexingType):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

Source/WebCore:

* bindings/js/JSDOMConvertSequences.h:
(WebCore::Detail::NumericSequenceConverter::convertArray):
(WebCore::Detail::SequenceConverter::convertArray):

LayoutTests:

Test should have a real error that gives you the stack.

* js/slow-stress/script-tests/variadic-closure-call.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@232070 268f45cc-cd09-0410-ab3c-d52691b4dbfc

84 files changed:
JSTests/ChangeLog
JSTests/stress/cow-convert-contiguous-to-array-storage.js [new file with mode: 0644]
JSTests/stress/cow-convert-double-to-array-storage.js [new file with mode: 0644]
JSTests/stress/cow-convert-double-to-contiguous.js [new file with mode: 0644]
JSTests/stress/cow-convert-int32-to-array-storage.js [new file with mode: 0644]
JSTests/stress/cow-convert-int32-to-contiguous.js [new file with mode: 0644]
JSTests/stress/cow-convert-int32-to-double.js [new file with mode: 0644]
JSTests/stress/put-on-cow-prototype.js [new file with mode: 0644]
LayoutTests/ChangeLog
LayoutTests/js/slow-stress/script-tests/variadic-closure-call.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecode/ArrayAllocationProfile.cpp
Source/JavaScriptCore/bytecode/ArrayAllocationProfile.h
Source/JavaScriptCore/bytecode/ArrayProfile.cpp
Source/JavaScriptCore/bytecode/ArrayProfile.h
Source/JavaScriptCore/bytecode/BytecodeList.json
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/InlineAccess.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
Source/JavaScriptCore/dfg/DFGArgumentsUtilities.cpp
Source/JavaScriptCore/dfg/DFGArrayMode.cpp
Source/JavaScriptCore/dfg/DFGArrayMode.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGOSRExit.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGValidate.cpp
Source/JavaScriptCore/ftl/FTLAbstractHeapRepository.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOperations.cpp
Source/JavaScriptCore/generate-bytecode-files
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/Butterfly.h
Source/JavaScriptCore/runtime/ButterflyInlines.h
Source/JavaScriptCore/runtime/ClonedArguments.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/IndexingType.cpp
Source/JavaScriptCore/runtime/IndexingType.h
Source/JavaScriptCore/runtime/JSArray.cpp
Source/JavaScriptCore/runtime/JSArrayInlines.h
Source/JavaScriptCore/runtime/JSCell.h
Source/JavaScriptCore/runtime/JSCellInlines.h
Source/JavaScriptCore/runtime/JSFixedArray.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSImmutableButterfly.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSImmutableButterfly.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSObjectInlines.h
Source/JavaScriptCore/runtime/JSType.h
Source/JavaScriptCore/runtime/RegExpMatchesArray.h
Source/JavaScriptCore/runtime/Structure.cpp
Source/JavaScriptCore/runtime/Structure.h
Source/JavaScriptCore/runtime/StructureIDBlob.h
Source/JavaScriptCore/runtime/StructureTransitionTable.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMConvertSequences.h

index 8686322..7224c06 100644 (file)
@@ -1,3 +1,38 @@
+2018-05-22  Keith Miller  <keith_miller@apple.com>
+
+        We should have a CoW storage for NewArrayBuffer arrays.
+        https://bugs.webkit.org/show_bug.cgi?id=185003
+
+        Reviewed by Filip Pizlo.
+
+        * stress/cow-convert-contiguous-to-array-storage.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/cow-convert-double-to-array-storage.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/cow-convert-double-to-contiguous.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/cow-convert-int32-to-array-storage.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/cow-convert-int32-to-contiguous.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/cow-convert-int32-to-double.js: Added.
+        (createBuffer):
+        (shouldBe):
+        (test):
+        * stress/put-on-cow-prototype.js: Added.
+        (putByVal):
+        (putById):
+
 2018-05-21  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         Unreviewed, reland InById cache
diff --git a/JSTests/stress/cow-convert-contiguous-to-array-storage.js b/JSTests/stress/cow-convert-contiguous-to-array-storage.js
new file mode 100644 (file)
index 0000000..03c6170
--- /dev/null
@@ -0,0 +1,25 @@
+function createBuffer() {
+    return [NaN, 2.0585345];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[1000000] = "test";
+    shouldBe(createBuffer()[1000000], undefined);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[1000000] = "test";
+    shouldBe(array[1000000], undefined);
+    shouldBe(createBuffer()[1000000], undefined);
+    shouldBe(Object.create(createBuffer())[1000000], undefined);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/cow-convert-double-to-array-storage.js b/JSTests/stress/cow-convert-double-to-array-storage.js
new file mode 100644 (file)
index 0000000..9782c09
--- /dev/null
@@ -0,0 +1,25 @@
+function createBuffer() {
+    return [3.90295335646, 2.0585345];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[1000000] = "test";
+    shouldBe(createBuffer()[1000000], undefined);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[1000000] = "test";
+    shouldBe(array[1000000], undefined);
+    shouldBe(createBuffer()[1000000], undefined);
+    shouldBe(Object.create(createBuffer())[1000000], undefined);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/cow-convert-double-to-contiguous.js b/JSTests/stress/cow-convert-double-to-contiguous.js
new file mode 100644 (file)
index 0000000..55896b1
--- /dev/null
@@ -0,0 +1,28 @@
+function createBuffer() {
+    return [23.23421684, 2.0585345];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[-1] = "test";
+    shouldBe(createBuffer()[-1], undefined);
+    array = createBuffer();
+    array[1] = "test";
+    shouldBe(createBuffer()[1], 2.0585345);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[1] = "test";
+    shouldBe(array[1], 2.0585345);
+    shouldBe(createBuffer()[1], 2.0585345);
+    shouldBe(Object.create(createBuffer())[1], 2.0585345);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/cow-convert-int32-to-array-storage.js b/JSTests/stress/cow-convert-int32-to-array-storage.js
new file mode 100644 (file)
index 0000000..90a744d
--- /dev/null
@@ -0,0 +1,25 @@
+function createBuffer() {
+    return [1, 2];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[100000000] = "test";
+    shouldBe(createBuffer()[100000000], undefined);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[100000000] = "test";
+    shouldBe(array[100000000], undefined);
+    shouldBe(createBuffer()[100000000], undefined);
+    shouldBe(Object.create(createBuffer())[100000000], undefined);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/cow-convert-int32-to-contiguous.js b/JSTests/stress/cow-convert-int32-to-contiguous.js
new file mode 100644 (file)
index 0000000..62e321b
--- /dev/null
@@ -0,0 +1,28 @@
+function createBuffer() {
+    return [1, 2];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[-1] = "test";
+    shouldBe(createBuffer()[-1], undefined);
+    array = createBuffer();
+    array[1] = "test";
+    shouldBe(createBuffer()[1], 2);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[1] = "test";
+    shouldBe(array[1], 2);
+    shouldBe(createBuffer()[1], 2);
+    shouldBe(Object.create(createBuffer())[1], 2);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/cow-convert-int32-to-double.js b/JSTests/stress/cow-convert-int32-to-double.js
new file mode 100644 (file)
index 0000000..4dd3787
--- /dev/null
@@ -0,0 +1,28 @@
+function createBuffer() {
+    return [1, 2];
+}
+noInline(createBuffer);
+
+function shouldBe(a, b) {
+    if (a !== b)
+        throw new Error(a + " should be === to " + b);
+}
+
+function test() {
+    let array = createBuffer();
+    array[-1] = 7.43;
+    shouldBe(createBuffer()[-1], undefined);
+    array = createBuffer();
+    array[1] = 6.9023;
+    shouldBe(createBuffer()[1], 2);
+    array = createBuffer();
+    let o = Object.create(array);
+    o[1] = 5.43;
+    shouldBe(array[1], 2);
+    shouldBe(createBuffer()[1], 2);
+    shouldBe(Object.create(createBuffer())[1], 2);
+}
+noInline(test);
+
+for (let i = 0; i < 10000; i++)
+    test();
diff --git a/JSTests/stress/put-on-cow-prototype.js b/JSTests/stress/put-on-cow-prototype.js
new file mode 100644 (file)
index 0000000..6bfa7b2
--- /dev/null
@@ -0,0 +1,18 @@
+function putByVal() {
+    let proto = [0,1];
+    let object = Object.create(proto);
+    object[0] = 5;
+}
+noInline(putByVal);
+
+function putById() {
+    let proto = [0,1];
+    let object = Object.create(proto);
+    object.foo = 5;
+}
+noInline(putById);
+
+for (let i = 0; i < 10000; i++) {
+    putByVal();
+    putById();
+}
index 26b9e3d..f6b3911 100644 (file)
@@ -1,3 +1,14 @@
+2018-05-22  Keith Miller  <keith_miller@apple.com>
+
+        We should have a CoW storage for NewArrayBuffer arrays.
+        https://bugs.webkit.org/show_bug.cgi?id=185003
+
+        Reviewed by Filip Pizlo.
+
+        Test should have a real error that gives you the stack.
+
+        * js/slow-stress/script-tests/variadic-closure-call.js:
+
 2018-05-22  Manuel Rego Casasnovas  <rego@igalia.com>
 
         [css-text] W3C test suite gardening
index 220a708..5e802f8 100644 (file)
@@ -17,7 +17,7 @@ for (var i = 0; i < 100000; ++i) {
         args.push(j);
     var result = foo.apply(null, args);
     if (("" + result) != ("" + [294, 336, 378, 420, 462, 504, 546, 588][n - 8]))
-        throw "Error: bad result for i = " + i + ": " + result;
+        throw new Error("bad result for i = " + i + ": " + result);
 }
 
 // Start failing some arity checks.
@@ -28,6 +28,6 @@ for (var i = 0; i < 100000; ++i) {
         args.push(j);
     var result = foo.apply(null, args);
     if (("" + result) != ("" + [0/0, 0/0, 0/0, 3, 126, 168, 210, 252, 294, 336, 378, 420, 462, 504, 546, 588][n]))
-        throw "Error: bad result for i = " + i + ": " + result;
+        throw new Error("bad result for i = " + i + ": " + result);
 }
 
index 591c3dd..20484aa 100644 (file)
@@ -1,3 +1,338 @@
+2018-05-22  Keith Miller  <keith_miller@apple.com>
+
+        We should have a CoW storage for NewArrayBuffer arrays.
+        https://bugs.webkit.org/show_bug.cgi?id=185003
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds copy on write storage for new array buffers. In
+        order to do this there needed to be significant changes to the
+        layout of IndexingType. The new indexing type has the following
+        shape:
+
+        struct IndexingTypeAndMisc {
+            struct IndexingModeIncludingHistory {
+                struct IndexingMode {
+                    struct IndexingType {
+                        uint8_t isArray:1;          // bit 0
+                        uint8_t shape:3;            // bit 1 - 3
+                    };
+                    uint8_t copyOnWrite:1;          // bit 4
+                };
+                uint8_t mayHaveIndexedAccessors:1;  // bit 5
+            };
+            uint8_t cellLockBits:2;                 // bit 6 - 7
+        };
+
+        For simplicity ArrayStorage shapes cannot be CoW. So the only
+        valid CoW indexing shapes are ArrayWithInt32, ArrayWithDouble, and
+        ArrayWithContiguous.
+
+        The backing store for a CoW array is a new class
+        JSImmutableButterfly, which looks exactly the same as a normal
+        butterfly except that it has a JSCell header. Like other
+        butterflies, JSImmutableButterfies are allocated out of the
+        Auxiliary Gigacage and are pointed to by JSCells in the same
+        way. However, when marking JSImmutableButterflies they are marked
+        as if they were a property.
+
+        With CoW arrays, the new_array_buffer bytecode will reallocate the
+        shared JSImmutableButterfly if it sees from the allocation profile
+        that the last array it allocated has transitioned to a different
+        indexing type. From then on, all arrays created by that
+        new_array_buffer bytecode will have the promoted indexing
+        type. This is more or less the same as what we used to do. The
+        only difference is that we don't promote all the way to array
+        storage even if we have seen it before.
+
+        Transitioning from a CoW indexing mode occurs whenever someone
+        tries to store to an element, grow the array, or add properties.
+        Storing or growing the array will call into code that does the
+        stupid thing of copying the butterfly then continue into the old
+        code. This doesn't end up costing us as future allocations will
+        use any upgraded indexing shape.  We get adding properties for
+        free by just changing the indexing mode on transition (our C++
+        code always updates the indexing mode).
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * bytecode/ArrayAllocationProfile.cpp:
+        (JSC::ArrayAllocationProfile::updateProfile):
+        * bytecode/ArrayAllocationProfile.h:
+        (JSC::ArrayAllocationProfile::initializeIndexingMode):
+        * bytecode/ArrayProfile.cpp:
+        (JSC::dumpArrayModes):
+        (JSC::ArrayProfile::briefDescriptionWithoutUpdating):
+        * bytecode/ArrayProfile.h:
+        (JSC::asArrayModes):
+        (JSC::arrayModeFromStructure):
+        (JSC::arrayModesInclude):
+        (JSC::hasSeenCopyOnWriteArray):
+        * bytecode/BytecodeList.json:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::finishCreation):
+        * bytecode/InlineAccess.cpp:
+        (JSC::InlineAccess::generateArrayLength):
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::addArrayAllocationProfile):
+        (JSC::UnlinkedCodeBlock::decompressArrayAllocationProfile):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::newArrayAllocationProfile):
+        (JSC::BytecodeGenerator::emitNewArrayBuffer):
+        (JSC::BytecodeGenerator::emitNewArray):
+        (JSC::BytecodeGenerator::emitNewArrayWithSize):
+        (JSC::BytecodeGenerator::emitExpectedFunctionSnippet):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ArrayNode::emitBytecode):
+        (JSC::ArrayPatternNode::bindValue const):
+        (JSC::ArrayPatternNode::emitDirectBinding):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGArgumentsEliminationPhase.cpp:
+        * dfg/DFGArgumentsUtilities.cpp:
+        (JSC::DFG::emitCodeToGetArgumentsArrayLength):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::ArrayMode::fromObserved):
+        (JSC::DFG::ArrayMode::refine const):
+        (JSC::DFG::ArrayMode::alreadyChecked const):
+        * dfg/DFGArrayMode.h:
+        (JSC::DFG::ArrayMode::ArrayMode):
+        (JSC::DFG::ArrayMode::action const):
+        (JSC::DFG::ArrayMode::withSpeculation const):
+        (JSC::DFG::ArrayMode::withArrayClass const):
+        (JSC::DFG::ArrayMode::withType const):
+        (JSC::DFG::ArrayMode::withConversion const):
+        (JSC::DFG::ArrayMode::withTypeAndConversion const):
+        (JSC::DFG::ArrayMode::arrayModesThatPassFiltering const):
+        (JSC::DFG::ArrayMode::arrayModesWithIndexingShape const):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        (JSC::DFG::ByteCodeParser::handleIntrinsicGetter):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::attemptToForceStringArrayModeByToStringConversion):
+        (JSC::DFG::FixupPhase::attemptToMakeGetArrayLength):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::dump):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::indexingType):
+        (JSC::DFG::Node::indexingMode):
+        * dfg/DFGOSRExit.cpp:
+        (JSC::DFG::OSRExit::compileExit):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::emitAllocateRawObject):
+        (JSC::DFG::SpeculativeJIT::jumpSlowForUnwantedArrayMode):
+        (JSC::DFG::SpeculativeJIT::arrayify):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnString):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnDirectArguments):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnScopedArguments):
+        (JSC::DFG::SpeculativeJIT::compileGetArrayLength):
+        (JSC::DFG::SpeculativeJIT::compileCreateRest):
+        (JSC::DFG::SpeculativeJIT::compileArraySlice):
+        (JSC::DFG::SpeculativeJIT::compileNewArrayBuffer):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compilePutStructure):
+        (JSC::FTL::DFG::LowerDFGToB3::compileArraySlice):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileNewArrayBuffer):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallOrConstructVarargsSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::compileForwardVarargsWithSpread):
+        (JSC::FTL::DFG::LowerDFGToB3::storeStructure):
+        (JSC::FTL::DFG::LowerDFGToB3::isArrayTypeForArrayify):
+        * ftl/FTLOperations.cpp:
+        (JSC::FTL::operationMaterializeObjectInOSR):
+        * generate-bytecode-files:
+        * interpreter/Interpreter.cpp:
+        (JSC::sizeOfVarargs):
+        (JSC::loadVarargs):
+        * jit/AssemblyHelpers.cpp:
+        (JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo):
+        * jit/JITOperations.cpp:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_put_by_val):
+        (JSC::JIT::emitSlow_op_put_by_val):
+        * jit/Repatch.cpp:
+        (JSC::tryCachePutByID):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/Butterfly.h:
+        (JSC::ContiguousData::Data::Data):
+        (JSC::ContiguousData::Data::operator bool const):
+        (JSC::ContiguousData::Data::operator=):
+        (JSC::ContiguousData::Data::operator const T& const):
+        (JSC::ContiguousData::Data::set):
+        (JSC::ContiguousData::Data::setWithoutWriteBarrier):
+        (JSC::ContiguousData::Data::clear):
+        (JSC::ContiguousData::Data::get const):
+        (JSC::ContiguousData::atUnsafe):
+        (JSC::ContiguousData::at const): Deleted.
+        (JSC::ContiguousData::at): Deleted.
+        * runtime/ButterflyInlines.h:
+        (JSC::ContiguousData<T>::at const):
+        (JSC::ContiguousData<T>::at):
+        * runtime/ClonedArguments.cpp:
+        (JSC::ClonedArguments::createEmpty):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/CommonSlowPaths.h:
+        (JSC::CommonSlowPaths::allocateNewArrayBuffer):
+        * runtime/IndexingType.cpp:
+        (JSC::leastUpperBoundOfIndexingTypeAndType):
+        (JSC::leastUpperBoundOfIndexingTypeAndValue):
+        (JSC::dumpIndexingType):
+        * runtime/IndexingType.h:
+        (JSC::hasIndexedProperties):
+        (JSC::hasUndecided):
+        (JSC::hasInt32):
+        (JSC::hasDouble):
+        (JSC::hasContiguous):
+        (JSC::hasArrayStorage):
+        (JSC::hasAnyArrayStorage):
+        (JSC::hasSlowPutArrayStorage):
+        (JSC::shouldUseSlowPut):
+        (JSC::isCopyOnWrite):
+        (JSC::arrayIndexFromIndexingType):
+        * runtime/JSArray.cpp:
+        (JSC::JSArray::tryCreateUninitializedRestricted):
+        (JSC::JSArray::put):
+        (JSC::JSArray::appendMemcpy):
+        (JSC::JSArray::setLength):
+        (JSC::JSArray::pop):
+        (JSC::JSArray::fastSlice):
+        (JSC::JSArray::shiftCountWithAnyIndexingType):
+        (JSC::JSArray::unshiftCountWithAnyIndexingType):
+        (JSC::JSArray::fillArgList):
+        (JSC::JSArray::copyToArguments):
+        * runtime/JSArrayInlines.h:
+        (JSC::JSArray::pushInline):
+        * runtime/JSCell.h:
+        * runtime/JSCellInlines.h:
+        (JSC::JSCell::JSCell):
+        (JSC::JSCell::finishCreation):
+        (JSC::JSCell::indexingType const):
+        (JSC::JSCell::indexingMode const):
+        (JSC::JSCell::setStructure):
+        * runtime/JSFixedArray.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::haveABadTime):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::originalArrayStructureForIndexingType const):
+        (JSC::JSGlobalObject::arrayStructureForIndexingTypeDuringAllocation const):
+        (JSC::JSGlobalObject::isOriginalArrayStructure):
+        * runtime/JSImmutableButterfly.cpp: Added.
+        (JSC::JSImmutableButterfly::visitChildren):
+        (JSC::JSImmutableButterfly::copyToArguments):
+        * runtime/JSImmutableButterfly.h: Added.
+        (JSC::JSImmutableButterfly::createStructure):
+        (JSC::JSImmutableButterfly::tryCreate):
+        (JSC::JSImmutableButterfly::create):
+        (JSC::JSImmutableButterfly::publicLength const):
+        (JSC::JSImmutableButterfly::vectorLength const):
+        (JSC::JSImmutableButterfly::length const):
+        (JSC::JSImmutableButterfly::toButterfly const):
+        (JSC::JSImmutableButterfly::fromButterfly):
+        (JSC::JSImmutableButterfly::get const):
+        (JSC::JSImmutableButterfly::subspaceFor):
+        (JSC::JSImmutableButterfly::setIndex):
+        (JSC::JSImmutableButterfly::allocationSize):
+        (JSC::JSImmutableButterfly::JSImmutableButterfly):
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::markAuxiliaryAndVisitOutOfLineProperties):
+        (JSC::JSObject::visitButterflyImpl):
+        (JSC::JSObject::getOwnPropertySlotByIndex):
+        (JSC::JSObject::putByIndex):
+        (JSC::JSObject::createInitialInt32):
+        (JSC::JSObject::createInitialDouble):
+        (JSC::JSObject::createInitialContiguous):
+        (JSC::JSObject::convertUndecidedToInt32):
+        (JSC::JSObject::convertUndecidedToDouble):
+        (JSC::JSObject::convertUndecidedToContiguous):
+        (JSC::JSObject::convertInt32ToDouble):
+        (JSC::JSObject::convertInt32ToArrayStorage):
+        (JSC::JSObject::convertDoubleToContiguous):
+        (JSC::JSObject::convertDoubleToArrayStorage):
+        (JSC::JSObject::convertContiguousToArrayStorage):
+        (JSC::JSObject::createInitialForValueAndSet):
+        (JSC::JSObject::convertInt32ForValue):
+        (JSC::JSObject::convertFromCopyOnWrite):
+        (JSC::JSObject::ensureWritableInt32Slow):
+        (JSC::JSObject::ensureWritableDoubleSlow):
+        (JSC::JSObject::ensureWritableContiguousSlow):
+        (JSC::JSObject::ensureArrayStorageSlow):
+        (JSC::JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode):
+        (JSC::JSObject::switchToSlowPutArrayStorage):
+        (JSC::JSObject::deletePropertyByIndex):
+        (JSC::JSObject::getOwnPropertyNames):
+        (JSC::canDoFastPutDirectIndex):
+        (JSC::JSObject::defineOwnIndexedProperty):
+        (JSC::JSObject::putByIndexBeyondVectorLengthWithoutAttributes):
+        (JSC::JSObject::putByIndexBeyondVectorLengthWithArrayStorage):
+        (JSC::JSObject::putByIndexBeyondVectorLength):
+        (JSC::JSObject::countElements):
+        (JSC::JSObject::ensureLengthSlow):
+        (JSC::JSObject::getEnumerableLength):
+        (JSC::JSObject::ensureInt32Slow): Deleted.
+        (JSC::JSObject::ensureDoubleSlow): Deleted.
+        (JSC::JSObject::ensureContiguousSlow): Deleted.
+        * runtime/JSObject.h:
+        (JSC::JSObject::putDirectIndex):
+        (JSC::JSObject::canGetIndexQuickly):
+        (JSC::JSObject::getIndexQuickly):
+        (JSC::JSObject::tryGetIndexQuickly const):
+        (JSC::JSObject::canSetIndexQuickly):
+        (JSC::JSObject::setIndexQuickly):
+        (JSC::JSObject::initializeIndex):
+        (JSC::JSObject::initializeIndexWithoutBarrier):
+        (JSC::JSObject::ensureWritableInt32):
+        (JSC::JSObject::ensureWritableDouble):
+        (JSC::JSObject::ensureWritableContiguous):
+        (JSC::JSObject::ensureLength):
+        (JSC::JSObject::ensureInt32): Deleted.
+        (JSC::JSObject::ensureDouble): Deleted.
+        (JSC::JSObject::ensureContiguous): Deleted.
+        * runtime/JSObjectInlines.h:
+        (JSC::JSObject::putDirectInternal):
+        * runtime/JSType.h:
+        * runtime/RegExpMatchesArray.h:
+        (JSC::tryCreateUninitializedRegExpMatchesArray):
+        * runtime/Structure.cpp:
+        (JSC::Structure::Structure):
+        (JSC::Structure::addNewPropertyTransition):
+        (JSC::Structure::nonPropertyTransition):
+        * runtime/Structure.h:
+        * runtime/StructureIDBlob.h:
+        (JSC::StructureIDBlob::StructureIDBlob):
+        (JSC::StructureIDBlob::indexingModeIncludingHistory const):
+        (JSC::StructureIDBlob::setIndexingModeIncludingHistory):
+        (JSC::StructureIDBlob::indexingModeIncludingHistoryOffset):
+        (JSC::StructureIDBlob::indexingTypeIncludingHistory const): Deleted.
+        (JSC::StructureIDBlob::setIndexingTypeIncludingHistory): Deleted.
+        (JSC::StructureIDBlob::indexingTypeIncludingHistoryOffset): Deleted.
+        * runtime/StructureTransitionTable.h:
+        (JSC::newIndexingType):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+
 2018-05-22  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r232052.
index 2802ef6..0e4cb35 100644 (file)
                53E777E41E92E265007CBEC4 /* WasmModuleInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E777E21E92E265007CBEC4 /* WasmModuleInformation.h */; };
                53E9E0AC1EAE83DF00FEE251 /* WasmMachineThreads.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E9E0AA1EAE83DE00FEE251 /* WasmMachineThreads.h */; };
                53E9E0AF1EAEC45700FEE251 /* WasmTierUpCount.h in Headers */ = {isa = PBXBuildFile; fileRef = 53E9E0AE1EAEC45700FEE251 /* WasmTierUpCount.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               53F11F41209138D700E411A7 /* JSImmutableButterfly.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F11F40209138D700E411A7 /* JSImmutableButterfly.h */; };
                53F40E851D58F9770099A1B6 /* WasmSections.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E841D58F9770099A1B6 /* WasmSections.h */; };
                53F40E8B1D5901BB0099A1B6 /* WasmFunctionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */; };
                53F40E8D1D5901F20099A1B6 /* WasmParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8C1D5901F20099A1B6 /* WasmParser.h */; };
                535557131D9D9EA5006D583B /* WasmMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmMemory.h; sourceTree = "<group>"; };
                535557151D9DFA32006D583B /* WasmMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMemory.cpp; sourceTree = "<group>"; };
                535C246B1F7A1777006EC40E /* UnifiedSource136.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource136.cpp; sourceTree = "<group>"; };
+               53696E5720A3A70200D7E01E /* BytecodeStructs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeStructs.h; sourceTree = "<group>"; };
                536B30871F71C5380037FC33 /* UnifiedSource119.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource119.cpp; sourceTree = "<group>"; };
                536B30881F71C5380037FC33 /* UnifiedSource125.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource125.cpp; sourceTree = "<group>"; };
                536B30891F71C5380037FC33 /* UnifiedSource131.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnifiedSource131.cpp; sourceTree = "<group>"; };
                53E9E0A91EAE83DE00FEE251 /* WasmMachineThreads.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMachineThreads.cpp; sourceTree = "<group>"; };
                53E9E0AA1EAE83DE00FEE251 /* WasmMachineThreads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmMachineThreads.h; sourceTree = "<group>"; };
                53E9E0AE1EAEC45700FEE251 /* WasmTierUpCount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmTierUpCount.h; sourceTree = "<group>"; };
+               53F11F40209138D700E411A7 /* JSImmutableButterfly.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSImmutableButterfly.h; sourceTree = "<group>"; };
+               53F11F422091749800E411A7 /* JSImmutableButterfly.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = JSImmutableButterfly.cpp; sourceTree = "<group>"; };
                53F256E11B87E28000B4B768 /* JSTypedArrayViewPrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSTypedArrayViewPrototype.cpp; sourceTree = "<group>"; };
                53F40E841D58F9770099A1B6 /* WasmSections.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmSections.h; sourceTree = "<group>"; };
                53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmFunctionParser.h; sourceTree = "<group>"; };
                                14BD5A2B0A3E91F600BAF59C /* JSValueRef.cpp */,
                                1482B6EA0A4300B300517CFC /* JSValueRef.h */,
                                86E3C60F167BAB87006D760A /* JSVirtualMachine.h */,
-                               795AC61720A2354B0052C76C /* JSVirtualMachinePrivate.h */,
                                86E3C610167BAB87006D760A /* JSVirtualMachine.mm */,
                                86E3C611167BAB87006D760A /* JSVirtualMachineInternal.h */,
+                               795AC61720A2354B0052C76C /* JSVirtualMachinePrivate.h */,
                                A7482E37116A697B003B0712 /* JSWeakObjectMapRefInternal.h */,
                                A7482B7A1166CDEA003B0712 /* JSWeakObjectMapRefPrivate.cpp */,
                                A7482B791166CDEA003B0712 /* JSWeakObjectMapRefPrivate.h */,
                                8B3BF5E31E3D365A0076A87A /* AsyncGeneratorPrototype.lut.h */,
                                996B73071BD9FA2C00331B84 /* BooleanPrototype.lut.h */,
                                6514F21718B3E1670098FF8B /* Bytecodes.h */,
+                               53696E5720A3A70200D7E01E /* BytecodeStructs.h */,
                                A53243951856A475002ED692 /* CombinedDomains.json */,
                                996B73081BD9FA2C00331B84 /* DateConstructor.lut.h */,
                                BCD203E70E1718F4002C7E82 /* DatePrototype.lut.h */,
                                865A30F0135007E100CDB49E /* JSCJSValueInlines.h */,
                                FE2B0B681FD0D2970075DA5F /* JSCPoison.cpp */,
                                FE2B0B701FD8C4630075DA5F /* JSCPoison.h */,
+                               FE7497E5209001B00003565B /* JSCPtrTag.h */,
                                72AAF7CB1D0D318B005E60BE /* JSCustomGetterSetterFunction.cpp */,
                                72AAF7CC1D0D318B005E60BE /* JSCustomGetterSetterFunction.h */,
                                0F2B66BD17B6B5AB00A7AE3F /* JSDataView.cpp */,
                                BC756FC60E2031B200DE7D12 /* JSGlobalObjectFunctions.cpp */,
                                BC756FC70E2031B200DE7D12 /* JSGlobalObjectFunctions.h */,
                                79B819921DD25CF500DDC714 /* JSGlobalObjectInlines.h */,
+                               53F11F422091749800E411A7 /* JSImmutableButterfly.cpp */,
+                               53F11F40209138D700E411A7 /* JSImmutableButterfly.h */,
                                0F2B66CA17B6B5AB00A7AE3F /* JSInt16Array.h */,
                                0F2B66CB17B6B5AB00A7AE3F /* JSInt32Array.h */,
                                0F2B66C917B6B5AB00A7AE3F /* JSInt8Array.h */,
                                2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */,
                                862553CE16136AA5009F17D0 /* JSProxy.cpp */,
                                862553CF16136AA5009F17D0 /* JSProxy.h */,
-                               FE7497E5209001B00003565B /* JSCPtrTag.h */,
                                534638721E70D01500F12AC1 /* JSRunLoopTimer.cpp */,
                                534638701E70CF3D00F12AC1 /* JSRunLoopTimer.h */,
                                14874AE115EBDE4A002E3587 /* JSScope.cpp */,
                                0FFB921C16D02F110055A5DB /* DFGOSRExitCompilationInfo.h in Headers */,
                                0F7025AA1714B0FC00382C0E /* DFGOSRExitCompilerCommon.h in Headers */,
                                0F392C8A1B46188400844728 /* DFGOSRExitFuzz.h in Headers */,
-                               795AC61820A2355E0052C76C /* JSVirtualMachinePrivate.h in Headers */,
                                0FEFC9AB1681A3B600567F53 /* DFGOSRExitJumpPlaceholder.h in Headers */,
                                0F235BEE17178E7300690C7F /* DFGOSRExitPreparation.h in Headers */,
                                0F6237981AE45CA700D402EA /* DFGPhantomInsertionPhase.h in Headers */,
                                A55D93AC18514F7900400DED /* InspectorProtocolTypes.h in Headers */,
                                A50E4B6218809DD50068A46D /* InspectorRuntimeAgent.h in Headers */,
                                A55165D31BDF0B9E003B75C1 /* InspectorScriptProfilerAgent.h in Headers */,
+                               0F49E9AA20AB4D00001CA0AA /* InstanceOfAccessCase.h in Headers */,
                                969A07990ED1D3AE00F1F681 /* Instruction.h in Headers */,
                                A7A8AF3B17ADB5F3005AB174 /* Int16Array.h in Headers */,
                                A7A8AF3C17ADB5F3005AB174 /* Int32Array.h in Headers */,
                                FE3A06A61C10B72D00390FDD /* JITBitOrGenerator.h in Headers */,
                                FE3A06B41C10CB9300390FDD /* JITBitXorGenerator.h in Headers */,
                                86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */,
+                               0FFB80BC20A794730006AAF6 /* JITCodeInlines.h in Headers */,
                                FE476FF4207E85D50093CA2D /* JITCodeMap.h in Headers */,
                                0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */,
                                0FAF7EFE165BA91F000C8455 /* JITDisassembler.h in Headers */,
                                A5D2E665195E174000A518E7 /* JSContextRefInternal.h in Headers */,
                                148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */,
                                FE2B0B731FD9EF700075DA5F /* JSCPoison.h in Headers */,
+                               FE7497E6209001B10003565B /* JSCPtrTag.h in Headers */,
                                A72028B81797601E0098028C /* JSCTestRunnerUtils.h in Headers */,
                                72AAF7CE1D0D31B3005E60BE /* JSCustomGetterSetterFunction.h in Headers */,
                                0F2B66EC17B6B5AB00A7AE3F /* JSDataView.h in Headers */,
                                A50E4B6418809DD50068A46D /* JSGlobalObjectRuntimeAgent.h in Headers */,
                                A503FA2A188F105900110F14 /* JSGlobalObjectScriptDebugServer.h in Headers */,
                                0F0CAEFC1EC4DA6B00970D12 /* JSHeapFinalizerPrivate.h in Headers */,
+                               53F11F41209138D700E411A7 /* JSImmutableButterfly.h in Headers */,
                                A513E5C0185BFACC007E95AD /* JSInjectedScriptHost.h in Headers */,
                                A513E5C2185BFACC007E95AD /* JSInjectedScriptHostPrototype.h in Headers */,
                                0F2B66F817B6B5AB00A7AE3F /* JSInt16Array.h in Headers */,
                                7C008CDB187124BB00955C24 /* JSPromiseDeferred.h in Headers */,
                                7C184E1F17BEE22E007CB63A /* JSPromisePrototype.h in Headers */,
                                996B731F1BDA08EF00331B84 /* JSPromisePrototype.lut.h in Headers */,
-                               FE7497E6209001B10003565B /* JSCPtrTag.h in Headers */,
                                2A05ABD61961DF2400341750 /* JSPropertyNameEnumerator.h in Headers */,
                                862553D216136E1A009F17D0 /* JSProxy.h in Headers */,
                                A552C3801ADDB8FE00139726 /* JSRemoteInspector.h in Headers */,
                                BC18C42C0E16F5CD00B34460 /* JSValueRef.h in Headers */,
                                86E3C615167BABD7006D760A /* JSVirtualMachine.h in Headers */,
                                86E3C61D167BABEE006D760A /* JSVirtualMachineInternal.h in Headers */,
+                               795AC61820A2355E0052C76C /* JSVirtualMachinePrivate.h in Headers */,
                                A7CA3AE817DA41AE006538AF /* JSWeakMap.h in Headers */,
                                A7482E93116A7CAD003B0712 /* JSWeakObjectMapRefInternal.h in Headers */,
                                A7482B9311671147003B0712 /* JSWeakObjectMapRefPrivate.h in Headers */,
                                7C008CE7187631B600955C24 /* Microtask.h in Headers */,
                                FE2A87601F02381600EB31B2 /* MinimumReservedZoneSize.h in Headers */,
                                86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */,
-                               0F49E9AA20AB4D00001CA0AA /* InstanceOfAccessCase.h in Headers */,
                                C4703CD7192844CC0013FBEA /* models.py in Headers */,
                                E3794E761B77EB97005543AE /* ModuleAnalyzer.h in Headers */,
                                9F63434577274FAFB9336C38 /* ModuleNamespaceAccessCase.h in Headers */,
                                E34E657520668EAA00FB81AC /* ParseHash.h in Headers */,
                                37C738D21EDB56E4003F2B0B /* ParseInt.h in Headers */,
                                BC18C44B0E16F5CD00B34460 /* Parser.h in Headers */,
-                               0FFB80BC20A794730006AAF6 /* JITCodeInlines.h in Headers */,
                                93052C350FB792190048FDC3 /* ParserArena.h in Headers */,
                                0FCCAE4516D0CF7400D0C65B /* ParserError.h in Headers */,
                                A77F1825164192C700640A47 /* ParserModes.h in Headers */,
index 6ad1a45..0b15189 100644 (file)
@@ -806,6 +806,7 @@ runtime/JSGlobalLexicalEnvironment.cpp
 runtime/JSGlobalObject.cpp
 runtime/JSGlobalObjectDebuggable.cpp
 runtime/JSGlobalObjectFunctions.cpp
+runtime/JSImmutableButterfly.cpp
 runtime/JSInternalPromise.cpp
 runtime/JSInternalPromiseConstructor.cpp
 runtime/JSInternalPromiseDeferred.cpp
index 41ce23c..bd73651 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "JSCInlines.h"
 
+#include <algorithm>
+
 namespace JSC {
 
 void ArrayAllocationProfile::updateProfile()
@@ -50,7 +52,14 @@ void ArrayAllocationProfile::updateProfile()
     if (!lastArray)
         return;
     if (LIKELY(Options::useArrayAllocationProfiling())) {
-        m_currentIndexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType, lastArray->indexingType());
+        // The basic model here is that we will upgrade ourselves to whatever the CoW version of lastArray is except ArrayStorage since we don't have CoW ArrayStorage.
+        IndexingType indexingType = leastUpperBoundOfIndexingTypes(m_currentIndexingType & IndexingTypeMask, lastArray->indexingType());
+        if (isCopyOnWrite(m_currentIndexingType)) {
+            if (indexingType > ArrayWithContiguous)
+                indexingType = ArrayWithContiguous;
+            indexingType |= CopyOnWrite;
+        }
+        m_currentIndexingType = indexingType;
         m_largestSeenVectorLength = std::min(std::max(m_largestSeenVectorLength, lastArray->getVectorLength()), BASE_CONTIGUOUS_VECTOR_LEN_MAX);
     }
     m_lastArray = nullptr;
index 1bfb470..c233deb 100644 (file)
@@ -56,7 +56,7 @@ public:
     }
     
     JS_EXPORT_PRIVATE void updateProfile();
-    
+
     static IndexingType selectIndexingTypeFor(ArrayAllocationProfile* profile)
     {
         if (!profile)
@@ -71,6 +71,8 @@ public:
         return lastArray;
     }
 
+    void initializeIndexingMode(IndexingType recommendedIndexingMode) { m_currentIndexingType = recommendedIndexingMode; }
+
 private:
     
     IndexingType m_currentIndexingType { ArrayWithUndecided };
index c1c7dbd..d071b0d 100644 (file)
@@ -72,6 +72,12 @@ void dumpArrayModes(PrintStream& out, ArrayModes arrayModes)
         out.print(comma, "ArrayWithArrayStorage");
     if (arrayModes & asArrayModes(ArrayWithSlowPutArrayStorage))
         out.print(comma, "ArrayWithSlowPutArrayStorage");
+    if (arrayModes & asArrayModes(CopyOnWriteArrayWithInt32))
+        out.print(comma, "CopyOnWriteArrayWithInt32");
+    if (arrayModes & asArrayModes(CopyOnWriteArrayWithDouble))
+        out.print(comma, "CopyOnWriteArrayWithDouble");
+    if (arrayModes & asArrayModes(CopyOnWriteArrayWithContiguous))
+        out.print(comma, "CopyOnWriteArrayWithContiguous");
 
     if (arrayModes & Int8ArrayMode)
         out.print(comma, "Int8ArrayMode");
@@ -147,46 +153,19 @@ CString ArrayProfile::briefDescription(const ConcurrentJSLocker& locker, CodeBlo
 CString ArrayProfile::briefDescriptionWithoutUpdating(const ConcurrentJSLocker&)
 {
     StringPrintStream out;
-    
-    bool hasPrinted = false;
-    
-    if (m_observedArrayModes) {
-        if (hasPrinted)
-            out.print(", ");
-        out.print(ArrayModesDump(m_observedArrayModes));
-        hasPrinted = true;
-    }
-    
-    if (m_mayStoreToHole) {
-        if (hasPrinted)
-            out.print(", ");
-        out.print("Hole");
-        hasPrinted = true;
-    }
-    
-    if (m_outOfBounds) {
-        if (hasPrinted)
-            out.print(", ");
-        out.print("OutOfBounds");
-        hasPrinted = true;
-    }
-    
-    if (m_mayInterceptIndexedAccesses) {
-        if (hasPrinted)
-            out.print(", ");
-        out.print("Intercept");
-        hasPrinted = true;
-    }
-    
-    if (m_usesOriginalArrayStructures) {
-        if (hasPrinted)
-            out.print(", ");
-        out.print("Original");
-        hasPrinted = true;
-    }
-    
-    UNUSED_PARAM(hasPrinted);
-    
+    CommaPrinter comma;
+
+    if (m_observedArrayModes)
+        out.print(comma, ArrayModesDump(m_observedArrayModes));
+    if (m_mayStoreToHole)
+        out.print(comma, "Hole");
+    if (m_outOfBounds)
+        out.print(comma, "OutOfBounds");
+    if (m_mayInterceptIndexedAccesses)
+        out.print(comma, "Intercept");
+    if (m_usesOriginalArrayStructures)
+        out.print(comma, "Original");
+
     return out.toCString();
 }
 
index 73eb882..408960f 100644 (file)
@@ -35,22 +35,42 @@ class CodeBlock;
 class LLIntOffsetsExtractor;
 
 // This is a bitfield where each bit represents an type of array access that we have seen.
-// There are 16 indexing types that use the lower bits.
+// There are 19 indexing types that use the lower bits.
 // There are 9 typed array types taking the bits 16 to 25.
 typedef unsigned ArrayModes;
 
-const ArrayModes Int8ArrayMode = 1 << 16;
-const ArrayModes Int16ArrayMode = 1 << 17;
-const ArrayModes Int32ArrayMode = 1 << 18;
-const ArrayModes Uint8ArrayMode = 1 << 19;
-const ArrayModes Uint8ClampedArrayMode = 1 << 20;
-const ArrayModes Uint16ArrayMode = 1 << 21;
-const ArrayModes Uint32ArrayMode = 1 << 22;
-const ArrayModes Float32ArrayMode = 1 << 23;
-const ArrayModes Float64ArrayMode = 1 << 24;
+const ArrayModes CopyOnWriteArrayWithInt32ArrayMode = 1 << 16;
+const ArrayModes CopyOnWriteArrayWithDoubleArrayMode = 1 << 17;
+const ArrayModes CopyOnWriteArrayWithContiguousArrayMode = 1 << 18;
 
-#define asArrayModes(type) \
-    (static_cast<unsigned>(1) << static_cast<unsigned>(type))
+const ArrayModes Int8ArrayMode = 1 << 19;
+const ArrayModes Int16ArrayMode = 1 << 20;
+const ArrayModes Int32ArrayMode = 1 << 21;
+const ArrayModes Uint8ArrayMode = 1 << 22;
+const ArrayModes Uint8ClampedArrayMode = 1 << 23;
+const ArrayModes Uint16ArrayMode = 1 << 24;
+const ArrayModes Uint32ArrayMode = 1 << 25;
+const ArrayModes Float32ArrayMode = 1 << 26;
+const ArrayModes Float64ArrayMode = 1 << 27;
+
+inline constexpr ArrayModes asArrayModes(IndexingType indexingMode)
+{
+    if (isCopyOnWrite(indexingMode)) {
+        switch (indexingMode) {
+        case CopyOnWriteArrayWithInt32:
+            return CopyOnWriteArrayWithInt32ArrayMode;
+        case CopyOnWriteArrayWithDouble:
+            return CopyOnWriteArrayWithDoubleArrayMode;
+        case CopyOnWriteArrayWithContiguous:
+            return CopyOnWriteArrayWithContiguousArrayMode;
+        default:
+            UNREACHABLE_FOR_PLATFORM();
+            return 0;
+        }
+    }
+
+    return static_cast<unsigned>(1) << static_cast<unsigned>(indexingMode);
+}
 
 #define ALL_TYPED_ARRAY_MODES \
     (Int8ArrayMode            \
@@ -73,6 +93,11 @@ const ArrayModes Float64ArrayMode = 1 << 24;
     | asArrayModes(NonArrayWithSlowPutArrayStorage)     \
     | ALL_TYPED_ARRAY_MODES)
 
+#define ALL_COPY_ON_WRITE_ARRAY_MODES                   \
+    (CopyOnWriteArrayWithInt32ArrayMode                 \
+    | CopyOnWriteArrayWithDoubleArrayMode               \
+    | CopyOnWriteArrayWithContiguousArrayMode)
+
 #define ALL_ARRAY_ARRAY_MODES                           \
     (asArrayModes(ArrayClass)                           \
     | asArrayModes(ArrayWithUndecided)                  \
@@ -80,7 +105,8 @@ const ArrayModes Float64ArrayMode = 1 << 24;
     | asArrayModes(ArrayWithDouble)                     \
     | asArrayModes(ArrayWithContiguous)                 \
     | asArrayModes(ArrayWithArrayStorage)               \
-    | asArrayModes(ArrayWithSlowPutArrayStorage))
+    | asArrayModes(ArrayWithSlowPutArrayStorage)        \
+    | ALL_COPY_ON_WRITE_ARRAY_MODES)
 
 #define ALL_ARRAY_MODES (ALL_NON_ARRAY_ARRAY_MODES | ALL_ARRAY_ARRAY_MODES)
 
@@ -109,7 +135,8 @@ inline ArrayModes arrayModeFromStructure(Structure* structure)
     case NotTypedArray:
         break;
     }
-    return asArrayModes(structure->indexingType());
+
+    return asArrayModes(structure->indexingMode());
 }
 
 void dumpArrayModes(PrintStream&, ArrayModes);
@@ -137,7 +164,10 @@ inline bool arrayModesAlreadyChecked(ArrayModes proven, ArrayModes expected)
 
 inline bool arrayModesInclude(ArrayModes arrayModes, IndexingType shape)
 {
-    return !!(arrayModes & (asArrayModes(NonArray | shape) | asArrayModes(ArrayClass | shape)));
+    ArrayModes modes = asArrayModes(NonArray | shape) | asArrayModes(ArrayClass | shape);
+    if (hasInt32(shape) || hasDouble(shape) || hasContiguous(shape))
+        modes |= asArrayModes(ArrayClass | shape | CopyOnWrite);
+    return !!(arrayModes & modes);
 }
 
 inline bool shouldUseSlowPutArrayStorage(ArrayModes arrayModes)
@@ -175,6 +205,11 @@ inline bool hasSeenNonArray(ArrayModes arrayModes)
     return arrayModes & ALL_NON_ARRAY_ARRAY_MODES;
 }
 
+inline bool hasSeenCopyOnWriteArray(ArrayModes arrayModes)
+{
+    return arrayModes & ALL_COPY_ON_WRITE_ARRAY_MODES;
+}
+
 class ArrayProfile {
 public:
     ArrayProfile()
@@ -228,7 +263,7 @@ public:
     bool outOfBounds(const ConcurrentJSLocker&) const { return m_outOfBounds; }
     
     bool usesOriginalArrayStructures(const ConcurrentJSLocker&) const { return m_usesOriginalArrayStructures; }
-    
+
     CString briefDescription(const ConcurrentJSLocker&, CodeBlock*);
     CString briefDescriptionWithoutUpdating(const ConcurrentJSLocker&);
     
index 16f1fb6..f5bdc49 100644 (file)
             { "name" : "op_new_object", "length" : 4 },
             { "name" : "op_new_array", "length" : 5 },
             { "name" : "op_new_array_with_size", "length" : 4 },
+            { "name" : "op_new_array_buffer", "offsets" :
+                       [{"dst" : "int"},
+                        {"immutableButterfly" : "int"},
+                        {"profile" : "ArrayAllocationProfile*"}]},
             { "name" : "op_new_array_with_spread", "length" : 5 },
             { "name" : "op_spread", "length" : 3 },
-            { "name" : "op_new_array_buffer", "length" : 4 },
             { "name" : "op_new_regexp", "length" : 3 },
             { "name" : "op_mov", "length" : 3 },
             { "name" : "op_not", "length" : 3 },
index cce0b6a..8bd7fe7 100644 (file)
@@ -35,6 +35,7 @@
 #include "BytecodeDumper.h"
 #include "BytecodeGenerator.h"
 #include "BytecodeLivenessAnalysis.h"
+#include "BytecodeStructs.h"
 #include "BytecodeUseDef.h"
 #include "CallLinkStatus.h"
 #include "CodeBlockSet.h"
@@ -599,12 +600,19 @@ bool CodeBlock::finishCreation(VM& vm, ScriptExecutable* ownerExecutable, Unlink
         }
 
         case op_new_array:
-        case op_new_array_buffer:
-        case op_new_array_with_size: {
-            int arrayAllocationProfileIndex = pc[opLength - 1].u.operand;
-            instructions[i + opLength - 1] = &m_arrayAllocationProfiles[arrayAllocationProfileIndex];
+        case op_new_array_with_size:
+        case op_new_array_buffer: {
+            unsigned arrayAllocationProfileIndex;
+            IndexingType recommendedIndexingType;
+            std::tie(arrayAllocationProfileIndex, recommendedIndexingType) = UnlinkedCodeBlock::decompressArrayAllocationProfile(pc[opLength - 1].u.operand);
+
+            ArrayAllocationProfile* profile = &m_arrayAllocationProfiles[arrayAllocationProfileIndex];
+            if (pc[0].u.opcode == op_new_array_buffer)
+                profile->initializeIndexingMode(recommendedIndexingType);
+            instructions[i + opLength - 1] = profile;
             break;
         }
+
         case op_new_object: {
             int objectAllocationProfileIndex = pc[opLength - 1].u.operand;
             ObjectAllocationProfile* objectAllocationProfile = &m_objectAllocationProfiles[objectAllocationProfileIndex];
index 660e52c..bd86fa3 100644 (file)
@@ -263,7 +263,7 @@ bool InlineAccess::generateArrayLength(StructureStubInfo& stubInfo, JSArray* arr
     GPRReg scratch = getScratchRegister(stubInfo);
 
     jit.load8(CCallHelpers::Address(base, JSCell::indexingTypeAndMiscOffset()), scratch);
-    jit.and32(CCallHelpers::TrustedImm32(IsArray | IndexingShapeMask), scratch);
+    jit.and32(CCallHelpers::TrustedImm32(IndexingTypeMask), scratch);
     auto branchToSlowPath = jit.patchableBranch32(
         CCallHelpers::NotEqual, scratch, CCallHelpers::TrustedImm32(array->indexingType()));
     jit.loadPtr(CCallHelpers::Address(base, JSObject::butterflyOffset()), value.payloadGPR());
index 6acb500..5ae594b 100644 (file)
@@ -302,9 +302,16 @@ public:
 
     UnlinkedArrayProfile addArrayProfile() { return m_arrayProfileCount++; }
     unsigned numberOfArrayProfiles() { return m_arrayProfileCount; }
-    UnlinkedArrayAllocationProfile addArrayAllocationProfile() { return m_arrayAllocationProfileCount++; }
+    UnlinkedArrayAllocationProfile addArrayAllocationProfile(IndexingType recommendedIndexingType) { return (m_arrayAllocationProfileCount++) | recommendedIndexingType << 24; }
     unsigned numberOfArrayAllocationProfiles() { return m_arrayAllocationProfileCount; }
     UnlinkedObjectAllocationProfile addObjectAllocationProfile() { return m_objectAllocationProfileCount++; }
+    static std::tuple<unsigned, IndexingType> decompressArrayAllocationProfile(UnlinkedArrayAllocationProfile compressedProfile)
+    {
+        unsigned profile = (compressedProfile << 8) >> 8;
+        IndexingType recommendedIndexingType = compressedProfile >> 24;
+        return std::make_tuple<unsigned, IndexingType>(WTFMove(profile), WTFMove(recommendedIndexingType));
+
+    }
     unsigned numberOfObjectAllocationProfiles() { return m_objectAllocationProfileCount; }
     UnlinkedValueProfile addValueProfile() { return m_valueProfileCount++; }
     unsigned numberOfValueProfiles() { return m_valueProfileCount; }
index e199e8f..6fc2f50 100644 (file)
@@ -45,6 +45,7 @@
 #include "JSFixedArray.h"
 #include "JSFunction.h"
 #include "JSGeneratorFunction.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSTemplateObjectDescriptor.h"
 #include "LowLevelInterpreter.h"
@@ -1282,9 +1283,9 @@ UnlinkedArrayProfile BytecodeGenerator::newArrayProfile()
     return m_codeBlock->addArrayProfile();
 }
 
-UnlinkedArrayAllocationProfile BytecodeGenerator::newArrayAllocationProfile()
+UnlinkedArrayAllocationProfile BytecodeGenerator::newArrayAllocationProfile(IndexingType recommendedIndexingType)
 {
-    return m_codeBlock->addArrayAllocationProfile();
+    return m_codeBlock->addArrayAllocationProfile(recommendedIndexingType);
 }
 
 UnlinkedObjectAllocationProfile BytecodeGenerator::newObjectAllocationProfile()
@@ -3186,16 +3187,16 @@ RegisterID* BytecodeGenerator::addTemplateObjectConstant(Ref<TemplateObjectDescr
     return &m_constantPoolRegisters[index];
 }
 
-RegisterID* BytecodeGenerator::emitNewArrayBuffer(RegisterID* dst, JSFixedArray* array)
+RegisterID* BytecodeGenerator::emitNewArrayBuffer(RegisterID* dst, JSImmutableButterfly* array, IndexingType recommendedIndexingType)
 {
     emitOpcode(op_new_array_buffer);
     instructions().append(dst->index());
     instructions().append(addConstantValue(array)->index());
-    instructions().append(newArrayAllocationProfile());
+    instructions().append(newArrayAllocationProfile(recommendedIndexingType));
     return dst;
 }
 
-RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements, unsigned length)
+RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements, unsigned length, IndexingType recommendedIndexingType)
 {
     Vector<RefPtr<RegisterID>, 16, UnsafeVectorOverflow> argv;
     for (ElementNode* n = elements; n; n = n->next()) {
@@ -3213,7 +3214,7 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen
     instructions().append(dst->index());
     instructions().append(argv.size() ? argv[0]->index() : 0); // argv
     instructions().append(argv.size()); // argc
-    instructions().append(newArrayAllocationProfile());
+    instructions().append(newArrayAllocationProfile(recommendedIndexingType));
     return dst;
 }
 
@@ -3265,7 +3266,7 @@ RegisterID* BytecodeGenerator::emitNewArrayWithSize(RegisterID* dst, RegisterID*
     emitOpcode(op_new_array_with_size);
     instructions().append(dst->index());
     instructions().append(length->index());
-    instructions().append(newArrayAllocationProfile());
+    instructions().append(newArrayAllocationProfile(ArrayWithUndecided));
 
     return dst;
 }
@@ -3459,7 +3460,7 @@ ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst,
                 instructions().append(dst->index());
                 instructions().append(0);
                 instructions().append(0);
-                instructions().append(newArrayAllocationProfile());
+                instructions().append(newArrayAllocationProfile(ArrayWithUndecided));
             }
         }
         break;
index 97828ca..86aed60 100644 (file)
@@ -54,6 +54,7 @@
 
 namespace JSC {
 
+    class JSImmutableButterfly;
     class Identifier;
 
     enum ExpectedFunction {
@@ -666,10 +667,11 @@ namespace JSC {
         void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope);
         void liftTDZCheckIfPossible(const Variable&);
         RegisterID* emitNewObject(RegisterID* dst);
-        RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision
+        RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length, IndexingType recommendedIndexingType); // stops at first elision
+        RegisterID* emitNewArrayBuffer(RegisterID* dst, JSImmutableButterfly*, IndexingType recommendedIndexingType);
+        // FIXME: new_array_with_spread should use an array allocation profile and take a recommendedIndexingType
         RegisterID* emitNewArrayWithSpread(RegisterID* dst, ElementNode*);
         RegisterID* emitNewArrayWithSize(RegisterID* dst, RegisterID* length);
-        RegisterID* emitNewArrayBuffer(RegisterID* dst, JSFixedArray*);
 
         RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*);
         RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*);
@@ -1006,7 +1008,7 @@ namespace JSC {
         Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int symbolTableConstantIndex, bool isLexicallyScoped);
 
         void emitOpcode(OpcodeID);
-        UnlinkedArrayAllocationProfile newArrayAllocationProfile();
+        UnlinkedArrayAllocationProfile newArrayAllocationProfile(IndexingType);
         UnlinkedObjectAllocationProfile newObjectAllocationProfile();
         UnlinkedValueProfile emitProfiledOpcode(OpcodeID);
         int kill(RegisterID* dst)
index a5fa525..8408b08 100644 (file)
@@ -36,6 +36,7 @@
 #include "JSFunction.h"
 #include "JSGeneratorFunction.h"
 #include "JSGlobalObject.h"
+#include "JSImmutableButterfly.h"
 #include "LabelScope.h"
 #include "Lexer.h"
 #include "Parser.h"
@@ -388,26 +389,32 @@ RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds
 {
     bool hadVariableExpression = false;
     unsigned length = 0;
+
+    IndexingType recommendedIndexingType = ArrayWithUndecided;
     ElementNode* firstPutElement;
     for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) {
         if (firstPutElement->elision() || firstPutElement->value()->isSpreadExpression())
             break;
         if (!firstPutElement->value()->isConstant())
             hadVariableExpression = true;
+        else
+            recommendedIndexingType = leastUpperBoundOfIndexingTypeAndValue(recommendedIndexingType, static_cast<ConstantNode*>(firstPutElement->value())->jsValue(generator));
+
         ++length;
     }
 
-    auto newArray = [&generator] (RegisterID* dst, ElementNode* elements, unsigned length, bool hadVariableExpression) {
+    auto newArray = [&] (RegisterID* dst, ElementNode* elements, unsigned length, bool hadVariableExpression) {
         if (length && !hadVariableExpression) {
-            auto* array = JSFixedArray::create(*generator.vm(), length);
+            recommendedIndexingType |= CopyOnWrite;
+            auto* array = JSImmutableButterfly::create(*generator.vm(), recommendedIndexingType, length);
             unsigned index = 0;
             for (ElementNode* element = elements; index < length; element = element->next()) {
                 ASSERT(element->value()->isConstant());
-                array->set(*generator.vm(), index++, static_cast<ConstantNode*>(element->value())->jsValue(generator));
+                array->setIndex(*generator.vm(), index++, static_cast<ConstantNode*>(element->value())->jsValue(generator));
             }
-            return generator.emitNewArrayBuffer(dst, array);
+            return generator.emitNewArrayBuffer(dst, array, recommendedIndexingType);
         }
-        return generator.emitNewArray(dst, elements, length);
+        return generator.emitNewArray(dst, elements, length, recommendedIndexingType);
     };
 
     if (!firstPutElement && !m_elision)
@@ -4157,7 +4164,7 @@ void ArrayPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs)
         }
 
         case BindingType::RestElement: {
-            RefPtr<RegisterID> array = generator.emitNewArray(generator.newTemporary(), nullptr, 0);
+            RefPtr<RegisterID> array = generator.emitNewArray(generator.newTemporary(), nullptr, 0, ArrayWithUndecided);
 
             Ref<Label> iterationDone = generator.newLabel();
             if (!done)
@@ -4209,7 +4216,7 @@ RegisterID* ArrayPatternNode::emitDirectBinding(BytecodeGenerator& generator, Re
 
     RefPtr<RegisterID> resultRegister;
     if (dst && dst != generator.ignoredResult())
-        resultRegister = generator.emitNewArray(generator.newTemporary(), nullptr, 0);
+        resultRegister = generator.emitNewArray(generator.newTemporary(), nullptr, 0, ArrayWithUndecided);
     if (m_targetPatterns.size() != elements.size())
         return nullptr;
     Vector<RefPtr<RegisterID>> registers;
index c4c92dd..ea3ebe4 100644 (file)
@@ -2236,7 +2236,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     }
             
     case NewArray:
-        setForNode(node, 
+        ASSERT(node->indexingMode() == node->indexingType()); // Copy on write arrays should only be created by NewArrayBuffer.
+        setForNode(node,
             m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
         break;
 
@@ -2272,8 +2273,8 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
         
     case NewArrayBuffer:
-        setForNode(node, 
-            m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType()));
+        setForNode(node,
+            m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingMode()));
         break;
 
     case NewArrayWithSize:
index 3f21314..a3d04ad 100644 (file)
@@ -148,7 +148,7 @@ private:
                 }
 
                 case NewArrayBuffer: {
-                    if (m_graph.isWatchingHavingABadTimeWatchpoint(node) && !hasAnyArrayStorage(node->indexingType()))
+                    if (m_graph.isWatchingHavingABadTimeWatchpoint(node) && !hasAnyArrayStorage(node->indexingMode()))
                         m_candidates.add(node);
                     break;
                 }
@@ -429,9 +429,9 @@ private:
                         break;
                     case NewArrayBuffer: {
                         ASSERT(m_graph.isWatchingHavingABadTimeWatchpoint(target));
-                        IndexingType indexingType = target->indexingType();
-                        ASSERT(!hasAnyArrayStorage(indexingType));
-                        structure = globalObject->originalArrayStructureForIndexingType(indexingType);
+                        IndexingType indexingMode = target->indexingMode();
+                        ASSERT(!hasAnyArrayStorage(indexingMode));
+                        structure = globalObject->originalArrayStructureForIndexingType(indexingMode);
                         break;
                     }
                     default:
@@ -867,7 +867,7 @@ private:
                                 }
 
                                 if (candidate->op() == PhantomNewArrayBuffer)
-                                    return candidate->castOperand<JSFixedArray*>()->length();
+                                    return candidate->castOperand<JSImmutableButterfly*>()->length();
 
                                 ASSERT(candidate->op() == PhantomCreateRest);
                                 unsigned numberOfArgumentsToSkip = candidate->numberOfArgumentsToSkip();
@@ -904,7 +904,7 @@ private:
                                     }
 
                                     if (candidate->op() == PhantomNewArrayBuffer) {
-                                        auto* array = candidate->castOperand<JSFixedArray*>();
+                                        auto* array = candidate->castOperand<JSImmutableButterfly*>();
                                         for (unsigned index = 0; index < array->length(); ++index) {
                                             JSValue constant;
                                             if (candidate->indexingType() == ArrayWithDouble)
@@ -1121,7 +1121,7 @@ private:
 
                                 if (candidate->op() == PhantomNewArrayBuffer) {
                                     bool canExit = true;
-                                    auto* array = candidate->castOperand<JSFixedArray*>();
+                                    auto* array = candidate->castOperand<JSImmutableButterfly*>();
                                     for (unsigned index = 0; index < array->length(); ++index) {
                                         JSValue constant;
                                         if (candidate->indexingType() == ArrayWithDouble)
index d129989..cc75f47 100644 (file)
@@ -103,7 +103,7 @@ Node* emitCodeToGetArgumentsArrayLength(
 
     if (arguments->op() == NewArrayBuffer || arguments->op() == PhantomNewArrayBuffer) {
         return insertionSet.insertConstant(
-            nodeIndex, origin, jsNumber(arguments->castOperand<JSFixedArray*>()->length()));
+            nodeIndex, origin, jsNumber(arguments->castOperand<JSImmutableButterfly*>()->length()));
     }
     
     InlineCallFrame* inlineCallFrame = arguments->origin.semantic.inlineCallFrame;
index 216bbeb..0e88d2a 100644 (file)
@@ -42,85 +42,110 @@ ArrayMode ArrayMode::fromObserved(const ConcurrentJSLocker& locker, ArrayProfile
         nonArray = Array::OriginalNonArray;
     else
         nonArray = Array::NonArray;
-    
+
+    auto handleContiguousModes = [&] (Array::Type type, ArrayModes observed) {
+        Array::Class isArray;
+        Array::Conversion converts;
+
+        RELEASE_ASSERT((observed & (asArrayModes(toIndexingShape(type)) | asArrayModes(toIndexingShape(type) | ArrayClass) | asArrayModes(toIndexingShape(type) | ArrayClass | CopyOnWrite))) == observed);
+
+        if (observed & asArrayModes(toIndexingShape(type))) {
+            if ((observed & asArrayModes(toIndexingShape(type))) == observed)
+                isArray = nonArray;
+            else
+                isArray = Array::PossiblyArray;
+        } else
+            isArray = Array::Array;
+
+        if (action == Array::Write && (observed & asArrayModes(toIndexingShape(type) | ArrayClass | CopyOnWrite)))
+            converts = Array::Convert;
+        else
+            converts = Array::AsIs;
+
+        return ArrayMode(type, isArray, converts, action).withProfile(locker, profile, makeSafe);
+    };
+
     ArrayModes observed = profile->observedArrayModes(locker);
     switch (observed) {
     case 0:
         return ArrayMode(Array::Unprofiled);
     case asArrayModes(NonArray):
         if (action == Array::Write && !profile->mayInterceptIndexedAccesses(locker))
-            return ArrayMode(Array::SelectUsingArguments, nonArray, Array::OutOfBounds, Array::Convert);
-        return ArrayMode(Array::SelectUsingPredictions, nonArray).withSpeculationFromProfile(locker, profile, makeSafe);
+            return ArrayMode(Array::SelectUsingArguments, nonArray, Array::OutOfBounds, Array::Convert, action);
+        return ArrayMode(Array::SelectUsingPredictions, nonArray, action).withSpeculationFromProfile(locker, profile, makeSafe);
 
     case asArrayModes(ArrayWithUndecided):
         if (action == Array::Write)
-            return ArrayMode(Array::SelectUsingArguments, Array::Array, Array::OutOfBounds, Array::Convert);
-        return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::AsIs).withProfile(locker, profile, makeSafe);
+            return ArrayMode(Array::SelectUsingArguments, Array::Array, Array::OutOfBounds, Array::Convert, action);
+        return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::AsIs, action).withProfile(locker, profile, makeSafe);
         
     case asArrayModes(NonArray) | asArrayModes(ArrayWithUndecided):
         if (action == Array::Write && !profile->mayInterceptIndexedAccesses(locker))
-            return ArrayMode(Array::SelectUsingArguments, Array::PossiblyArray, Array::OutOfBounds, Array::Convert);
-        return ArrayMode(Array::SelectUsingPredictions).withSpeculationFromProfile(locker, profile, makeSafe);
+            return ArrayMode(Array::SelectUsingArguments, Array::PossiblyArray, Array::OutOfBounds, Array::Convert, action);
+        return ArrayMode(Array::SelectUsingPredictions, action).withSpeculationFromProfile(locker, profile, makeSafe);
 
     case asArrayModes(NonArrayWithInt32):
-        return ArrayMode(Array::Int32, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(ArrayWithInt32):
-        return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32):
-        return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case asArrayModes(NonArrayWithInt32) | asArrayModes(CopyOnWriteArrayWithInt32):
+    case asArrayModes(ArrayWithInt32) | asArrayModes(CopyOnWriteArrayWithInt32):
+    case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32) | asArrayModes(CopyOnWriteArrayWithInt32):
+        return handleContiguousModes(Array::Int32, observed);
 
     case asArrayModes(NonArrayWithDouble):
-        return ArrayMode(Array::Double, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(ArrayWithDouble):
-        return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble):
-        return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case asArrayModes(NonArrayWithDouble) | asArrayModes(CopyOnWriteArrayWithDouble):
+    case asArrayModes(ArrayWithDouble) | asArrayModes(CopyOnWriteArrayWithDouble):
+    case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble) | asArrayModes(CopyOnWriteArrayWithDouble):
+        return handleContiguousModes(Array::Double, observed);
 
     case asArrayModes(NonArrayWithContiguous):
-        return ArrayMode(Array::Contiguous, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(ArrayWithContiguous):
-        return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous):
-        return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case asArrayModes(NonArrayWithContiguous) | asArrayModes(CopyOnWriteArrayWithContiguous):
+    case asArrayModes(ArrayWithContiguous) | asArrayModes(CopyOnWriteArrayWithContiguous):
+    case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous) | asArrayModes(CopyOnWriteArrayWithContiguous):
+        return handleContiguousModes(Array::Contiguous, observed);
 
     case asArrayModes(NonArrayWithArrayStorage):
-        return ArrayMode(Array::ArrayStorage, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::ArrayStorage, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithSlowPutArrayStorage):
     case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage):
-        return ArrayMode(Array::SlowPutArrayStorage, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::SlowPutArrayStorage, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case asArrayModes(ArrayWithArrayStorage):
-        return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::ArrayStorage, Array::Array, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case asArrayModes(ArrayWithSlowPutArrayStorage):
     case asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
-        return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::SlowPutArrayStorage, Array::Array, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage):
-        return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
     case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
-        return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Int8ArrayMode:
-        return ArrayMode(Array::Int8Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Int8Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Int16ArrayMode:
-        return ArrayMode(Array::Int16Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Int16Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Int32ArrayMode:
-        return ArrayMode(Array::Int32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Int32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Uint8ArrayMode:
-        return ArrayMode(Array::Uint8Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Uint8Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Uint8ClampedArrayMode:
-        return ArrayMode(Array::Uint8ClampedArray, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Uint8ClampedArray, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Uint16ArrayMode:
-        return ArrayMode(Array::Uint16Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Uint16Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Uint32ArrayMode:
-        return ArrayMode(Array::Uint32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Uint32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Float32ArrayMode:
-        return ArrayMode(Array::Float32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Float32Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
     case Float64ArrayMode:
-        return ArrayMode(Array::Float64Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+        return ArrayMode(Array::Float64Array, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
 
     default:
         // If we have seen multiple TypedArray types, or a TypedArray and non-typed array, it doesn't make sense to try to convert the object since you can't convert typed arrays.
         if (observed & ALL_TYPED_ARRAY_MODES)
-            return ArrayMode(Array::Generic, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+            return ArrayMode(Array::Generic, nonArray, Array::AsIs, action).withProfile(locker, profile, makeSafe);
 
         if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses(locker))
             return ArrayMode(Array::SelectUsingPredictions).withSpeculationFromProfile(locker, profile, makeSafe);
@@ -150,7 +175,7 @@ ArrayMode ArrayMode::fromObserved(const ConcurrentJSLocker& locker, ArrayProfile
         else
             arrayClass = Array::PossiblyArray;
         
-        return ArrayMode(type, arrayClass, Array::Convert).withProfile(locker, profile, makeSafe);
+        return ArrayMode(type, arrayClass, Array::Convert, action).withProfile(locker, profile, makeSafe);
     }
 }
 
@@ -171,15 +196,15 @@ ArrayMode ArrayMode::refine(
         // happen if we inlined code based on, say, a global variable watchpoint, but later
         // realized that the callsite could not have possibly executed. It may be worthwhile
         // to fix that, but for now I'm leaving it as-is.
-        return ArrayMode(Array::ForceExit);
+        return ArrayMode(Array::ForceExit, action());
     }
     
     if (!isInt32Speculation(index))
-        return ArrayMode(Array::Generic);
+        return ArrayMode(Array::Generic, action());
     
     // If we had exited because of an exotic object behavior, then don't try to specialize.
     if (graph.hasExitSite(node->origin.semantic, ExoticObjectMode))
-        return ArrayMode(Array::Generic);
+        return ArrayMode(Array::Generic, action());
     
     // Note: our profiling currently doesn't give us good information in case we have
     // an unlikely control flow path that sets the base to a non-cell value. Value
@@ -196,7 +221,7 @@ ArrayMode ArrayMode::refine(
             // This is semantically identical to defineOwnProperty({configurable: true, writable:true, enumerable:true}),
             // which we can't model as a simple store to the typed array since typed array indexed properties
             // are non-configurable.
-            return ArrayMode(Array::Generic);
+            return ArrayMode(Array::Generic, action());
         }
         return result;
     };
@@ -227,7 +252,7 @@ ArrayMode ArrayMode::refine(
             if (globalObject->arrayPrototypeChainIsSane())
                 return withSpeculation(Array::SaneChain);
         }
-        return ArrayMode(Array::Generic);
+        return ArrayMode(Array::Generic, action());
     }
     case Array::Int32:
         if (!value || isInt32Speculation(value))
@@ -272,11 +297,11 @@ ArrayMode ArrayMode::refine(
                 // FIXME: Support OOB access for ScopedArguments.
                 // https://bugs.webkit.org/show_bug.cgi?id=179596
                 if (type == Array::DirectArguments)
-                    return ArrayMode(type, Array::NonArray, Array::OutOfBounds, Array::AsIs);
-                return ArrayMode(Array::Generic);
+                    return ArrayMode(type, Array::NonArray, Array::OutOfBounds, Array::AsIs, action());
+                return ArrayMode(Array::Generic, action());
             }
             if (isX86() && is32Bit() && isScopedArgumentsSpeculation(base))
-                return ArrayMode(Array::Generic);
+                return ArrayMode(Array::Generic, action());
             return withType(type);
         }
         
@@ -322,8 +347,8 @@ ArrayMode ArrayMode::refine(
             return typedArrayResult(result.withType(Array::Float64Array));
 
         if (type() == Array::Unprofiled)
-            return ArrayMode(Array::ForceExit);
-        return ArrayMode(Array::Generic);
+            return ArrayMode(Array::ForceExit, action());
+        return ArrayMode(Array::Generic, action());
     }
 
     default:
@@ -382,6 +407,8 @@ bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& va
             RegisteredStructure structure = value.m_structure[i];
             if ((structure->indexingType() & IndexingShapeMask) != shape)
                 return false;
+            if (isCopyOnWrite(structure->indexingMode()) && action() == Array::Write)
+                return false;
             if (!(structure->indexingType() & IsArray))
                 return false;
             if (!graph.globalObjectFor(node->origin.semantic)->isOriginalArrayStructure(structure.get()))
@@ -397,7 +424,9 @@ bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& va
             return false;
         for (unsigned i = value.m_structure.size(); i--;) {
             RegisteredStructure structure = value.m_structure[i];
-            if ((structure->indexingType() & IndexingShapeMask) != shape)
+            if ((structure->indexingMode() & IndexingShapeMask) != shape)
+                return false;
+            if (isCopyOnWrite(structure->indexingMode()) && action() == Array::Write)
                 return false;
             if (!(structure->indexingType() & IsArray))
                 return false;
@@ -412,7 +441,9 @@ bool ArrayMode::alreadyChecked(Graph& graph, Node* node, const AbstractValue& va
             return false;
         for (unsigned i = value.m_structure.size(); i--;) {
             RegisteredStructure structure = value.m_structure[i];
-            if ((structure->indexingType() & IndexingShapeMask) != shape)
+            if ((structure->indexingMode() & IndexingShapeMask) != shape)
+                return false;
+            if (isCopyOnWrite(structure->indexingMode()) && action() == Array::Write)
                 return false;
         }
         return true;
index 81bcf82..1a15fec 100644 (file)
@@ -44,12 +44,12 @@ struct Node;
 // that would otherwise occur, since we say things like "Int8Array" and "JSArray"
 // in lots of other places, to mean subtly different things.
 namespace Array {
-enum Action {
+enum Action : uint8_t {
     Read,
     Write
 };
 
-enum Type {
+enum Type : uint8_t {
     SelectUsingPredictions, // Implies that we need predictions to decide. We will never get to the backend in this mode.
     SelectUsingArguments, // Implies that we use the Node's arguments to decide. We will never get to the backend in this mode.
     Unprofiled, // Implies that array profiling didn't see anything. But that could be because the operands didn't comply with basic type assumptions (base is cell, property is int). This either becomes Generic or ForceExit depending on value profiling.
@@ -79,7 +79,7 @@ enum Type {
     AnyTypedArray
 };
 
-enum Class {
+enum Class : uint8_t {
     NonArray, // Definitely some object that is not a JSArray.
     OriginalNonArray, // Definitely some object that is not a JSArray, but that object has the original structure.
     Array, // Definitely a JSArray, and may or may not have custom properties or have undergone some other bizarre transitions.
@@ -87,19 +87,20 @@ enum Class {
     PossiblyArray // Some object that may or may not be a JSArray.
 };
 
-enum Speculation {
+enum Speculation : uint8_t {
     SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment.
     
     InBounds, // In bounds and not loading a hole.
     ToHole, // Potentially storing to a hole.
     OutOfBounds // Out-of-bounds access and anything can happen.
 };
-enum Conversion {
+enum Conversion : uint8_t {
     AsIs,
     Convert
 };
 } // namespace Array
 
+const char* arrayActionToString(Array::Action);
 const char* arrayTypeToString(Array::Type);
 const char* arrayClassToString(Array::Class);
 const char* arraySpeculationToString(Array::Speculation);
@@ -121,44 +122,50 @@ public:
         u.asBytes.arrayClass = Array::NonArray;
         u.asBytes.speculation = Array::InBounds;
         u.asBytes.conversion = Array::AsIs;
+        u.asBytes.action = Array::Write;
     }
     
-    explicit ArrayMode(Array::Type type)
+    explicit ArrayMode(Array::Type type, Array::Action action)
     {
         u.asBytes.type = type;
         u.asBytes.arrayClass = Array::NonArray;
         u.asBytes.speculation = Array::InBounds;
         u.asBytes.conversion = Array::AsIs;
+        u.asBytes.action = action;
     }
     
-    ArrayMode(Array::Type type, Array::Class arrayClass)
+    ArrayMode(Array::Type type, Array::Class arrayClass, Array::Action action)
     {
         u.asBytes.type = type;
         u.asBytes.arrayClass = arrayClass;
         u.asBytes.speculation = Array::InBounds;
         u.asBytes.conversion = Array::AsIs;
+        u.asBytes.action = action;
     }
     
-    ArrayMode(Array::Type type, Array::Class arrayClass, Array::Speculation speculation, Array::Conversion conversion)
+    ArrayMode(Array::Type type, Array::Class arrayClass, Array::Speculation speculation, Array::Conversion conversion, Array::Action action)
     {
         u.asBytes.type = type;
         u.asBytes.arrayClass = arrayClass;
         u.asBytes.speculation = speculation;
         u.asBytes.conversion = conversion;
+        u.asBytes.action = action;
     }
     
-    ArrayMode(Array::Type type, Array::Class arrayClass, Array::Conversion conversion)
+    ArrayMode(Array::Type type, Array::Class arrayClass, Array::Conversion conversion, Array::Action action)
     {
         u.asBytes.type = type;
         u.asBytes.arrayClass = arrayClass;
         u.asBytes.speculation = Array::InBounds;
         u.asBytes.conversion = conversion;
+        u.asBytes.action = action;
     }
     
     Array::Type type() const { return static_cast<Array::Type>(u.asBytes.type); }
     Array::Class arrayClass() const { return static_cast<Array::Class>(u.asBytes.arrayClass); }
     Array::Speculation speculation() const { return static_cast<Array::Speculation>(u.asBytes.speculation); }
     Array::Conversion conversion() const { return static_cast<Array::Conversion>(u.asBytes.conversion); }
+    Array::Action action() const { return static_cast<Array::Action>(u.asBytes.action); }
     
     unsigned asWord() const { return u.asWord; }
     
@@ -171,12 +178,12 @@ public:
     
     ArrayMode withSpeculation(Array::Speculation speculation) const
     {
-        return ArrayMode(type(), arrayClass(), speculation, conversion());
+        return ArrayMode(type(), arrayClass(), speculation, conversion(), action());
     }
     
     ArrayMode withArrayClass(Array::Class arrayClass) const
     {
-        return ArrayMode(type(), arrayClass, speculation(), conversion());
+        return ArrayMode(type(), arrayClass, speculation(), conversion(), action());
     }
     
     ArrayMode withSpeculationFromProfile(const ConcurrentJSLocker& locker, ArrayProfile* profile, bool makeSafe) const
@@ -210,17 +217,17 @@ public:
     
     ArrayMode withType(Array::Type type) const
     {
-        return ArrayMode(type, arrayClass(), speculation(), conversion());
+        return ArrayMode(type, arrayClass(), speculation(), conversion(), action());
     }
     
     ArrayMode withConversion(Array::Conversion conversion) const
     {
-        return ArrayMode(type(), arrayClass(), speculation(), conversion);
+        return ArrayMode(type(), arrayClass(), speculation(), conversion, action());
     }
     
     ArrayMode withTypeAndConversion(Array::Type type, Array::Conversion conversion) const
     {
-        return ArrayMode(type, arrayClass(), speculation(), conversion);
+        return ArrayMode(type, arrayClass(), speculation(), conversion, action());
     }
     
     ArrayMode refine(Graph&, Node*, SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const;
@@ -290,7 +297,7 @@ public:
     {
         return type() == Array::SlowPutArrayStorage;
     }
-    
+
     bool canCSEStorage() const
     {
         switch (type()) {
@@ -410,15 +417,19 @@ public:
     
     ArrayModes arrayModesThatPassFiltering() const
     {
+        ArrayModes result;
         switch (type()) {
         case Array::Generic:
             return ALL_ARRAY_MODES;
         case Array::Int32:
-            return arrayModesWithIndexingShape(Int32Shape);
+            result = arrayModesWithIndexingShape(Int32Shape);
+            break;
         case Array::Double:
-            return arrayModesWithIndexingShape(DoubleShape);
+            result = arrayModesWithIndexingShape(DoubleShape);
+            break;
         case Array::Contiguous:
-            return arrayModesWithIndexingShape(ContiguousShape);
+            result = arrayModesWithIndexingShape(ContiguousShape);
+            break;
         case Array::ArrayStorage:
             return arrayModesWithIndexingShape(ArrayStorageShape);
         case Array::SlowPutArrayStorage:
@@ -426,6 +437,10 @@ public:
         default:
             return asArrayModes(NonArray);
         }
+
+        if (action() == Array::Write)
+            result &= ~ALL_COPY_ON_WRITE_ARRAY_MODES;
+        return result;
     }
     
     bool getIndexedPropertyStorageMayTriggerGC() const
@@ -474,8 +489,12 @@ private:
             return asArrayModes(shape);
         case Array::Array:
         case Array::OriginalArray:
+            if (hasInt32(shape) || hasDouble(shape) || hasContiguous(shape))
+                return asArrayModes(shape | IsArray) | asArrayModes(shape | IsArray | CopyOnWrite);
             return asArrayModes(shape | IsArray);
         case Array::PossiblyArray:
+            if (hasInt32(shape) || hasDouble(shape) || hasContiguous(shape))
+                return asArrayModes(shape) | asArrayModes(shape | IsArray) | asArrayModes(shape | IsArray | CopyOnWrite);
             return asArrayModes(shape) | asArrayModes(shape | IsArray);
         default:
             // This is only necessary for C++ compilers that don't understand enums.
@@ -497,10 +516,12 @@ private:
             uint8_t type;
             uint8_t arrayClass;
             uint8_t speculation;
-            uint8_t conversion;
+            uint8_t conversion : 4;
+            uint8_t action : 4;
         } asBytes;
         unsigned asWord;
     } u;
+    static_assert(sizeof(decltype(u.asBytes)) == sizeof(decltype(u.asWord)), "the word form of ArrayMode should have the same size as the individual slices");
 };
 
 static inline bool canCSEStorage(const ArrayMode& arrayMode)
index fa097f3..0784441 100644 (file)
@@ -51,6 +51,7 @@
 #include "InstanceOfStatus.h"
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
+#include "JSImmutableButterfly.h"
 #include "JSModuleEnvironment.h"
 #include "JSModuleNamespaceObject.h"
 #include "NumberConstructor.h"
@@ -2427,6 +2428,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
             return false;
         
         NodeType op = LastNodeType;
+        Array::Action action = Array::Write;
         unsigned numArgs = 0; // Number of actual args; we add one for the backing store pointer.
         switch (intrinsic) {
         case AtomicsAddIntrinsic:
@@ -2454,6 +2456,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         case AtomicsLoadIntrinsic:
             op = AtomicsLoad;
             numArgs = 2;
+            action = Array::Read;
             break;
         case AtomicsOrIntrinsic:
             op = AtomicsOr;
@@ -2489,12 +2492,12 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         if (numArgs + 1 <= 3) {
             while (args.size() < 3)
                 args.append(nullptr);
-            result = addToGraph(op, OpInfo(ArrayMode(Array::SelectUsingPredictions).asWord()), OpInfo(prediction), args[0], args[1], args[2]);
+            result = addToGraph(op, OpInfo(ArrayMode(Array::SelectUsingPredictions, action).asWord()), OpInfo(prediction), args[0], args[1], args[2]);
         } else {
             for (Node* node : args)
                 addVarArgChild(node);
             addVarArgChild(nullptr);
-            result = addToGraph(Node::VarArg, op, OpInfo(ArrayMode(Array::SelectUsingPredictions).asWord()), OpInfo(prediction));
+            result = addToGraph(Node::VarArg, op, OpInfo(ArrayMode(Array::SelectUsingPredictions, action).asWord()), OpInfo(prediction));
         }
         
         set(VirtualRegister(resultOperand), result);
@@ -2529,7 +2532,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         insertChecks();
         VirtualRegister thisOperand = virtualRegisterForArgument(0, registerOffset);
         VirtualRegister indexOperand = virtualRegisterForArgument(1, registerOffset);
-        Node* charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), get(indexOperand));
+        Node* charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String, Array::Read).asWord()), get(thisOperand), get(indexOperand));
 
         set(VirtualRegister(resultOperand), charCode);
         return true;
@@ -2542,7 +2545,7 @@ bool ByteCodeParser::handleIntrinsicCall(Node* callee, int resultOperand, Intrin
         insertChecks();
         VirtualRegister thisOperand = virtualRegisterForArgument(0, registerOffset);
         VirtualRegister indexOperand = virtualRegisterForArgument(1, registerOffset);
-        Node* charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), get(indexOperand));
+        Node* charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String, Array::Read).asWord()), get(thisOperand), get(indexOperand));
 
         set(VirtualRegister(resultOperand), charCode);
         return true;
@@ -3206,7 +3209,7 @@ bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, SpeculatedType pre
             ASSERT(arrayType != Array::Generic);
         });
 
-        Node* lengthNode = addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType).asWord()), thisNode);
+        Node* lengthNode = addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType, Array::Read).asWord()), thisNode);
 
         if (!logSize) {
             set(VirtualRegister(resultOperand), lengthNode);
@@ -3233,7 +3236,7 @@ bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, SpeculatedType pre
             ASSERT(arrayType != Array::Generic);
         });
 
-        set(VirtualRegister(resultOperand), addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType).asWord()), thisNode));
+        set(VirtualRegister(resultOperand), addToGraph(GetArrayLength, OpInfo(ArrayMode(arrayType, Array::Read).asWord()), thisNode));
 
         return true;
 
@@ -3251,7 +3254,7 @@ bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, SpeculatedType pre
             ASSERT(arrayType != Array::Generic);
         });
 
-        set(VirtualRegister(resultOperand), addToGraph(GetTypedArrayByteOffset, OpInfo(ArrayMode(arrayType).asWord()), thisNode));
+        set(VirtualRegister(resultOperand), addToGraph(GetTypedArrayByteOffset, OpInfo(ArrayMode(arrayType, Array::Read).asWord()), thisNode));
 
         return true;
     }
@@ -4569,18 +4572,19 @@ void ByteCodeParser::parseBlock(unsigned limit)
         }
             
         case op_new_array_buffer: {
-            FrozenValue* frozen = get(VirtualRegister(currentInstruction[2].u.operand))->constant();
-            JSFixedArray* fixedArray = frozen->cast<JSFixedArray*>();
-            ArrayAllocationProfile* profile = currentInstruction[3].u.arrayAllocationProfile;
+            auto& bytecode = *reinterpret_cast<OpNewArrayBuffer*>(currentInstruction);
+            // Unfortunately, we can't allocate a new JSImmutableButterfly if the profile tells us new information because we
+            // cannot allocate from compilation threads.
+            WTF::loadLoadFence();
+            FrozenValue* frozen = get(VirtualRegister(bytecode.immutableButterfly()))->constant();
+            WTF::loadLoadFence();
+            JSImmutableButterfly* immutableButterfly = frozen->cast<JSImmutableButterfly*>();
             NewArrayBufferData data { };
-            data.indexingType = profile->selectIndexingType();
-            data.vectorLengthHint = std::max<unsigned>(profile->vectorLengthHint(), fixedArray->length());
+            data.indexingMode = immutableButterfly->indexingMode();
+            // TODO: Do I need this?
+            data.vectorLengthHint = immutableButterfly->toButterfly()->vectorLength();
 
-            // If this statement has never executed, we'll have the wrong indexing type in the profile.
-            for (unsigned index = 0; index < fixedArray->length(); ++index)
-                data.indexingType = leastUpperBoundOfIndexingTypeAndValue(data.indexingType, fixedArray->get(index));
-            
-            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(NewArrayBuffer, OpInfo(frozen), OpInfo(data.asQuadWord)));
+            set(VirtualRegister(bytecode.dst()), addToGraph(NewArrayBuffer, OpInfo(frozen), OpInfo(data.asQuadWord)));
             NEXT_OPCODE(op_new_array_buffer);
         }
             
index 3ea02a8..515c677 100644 (file)
@@ -37,6 +37,7 @@
 #include "DOMJITSignature.h"
 #include "InlineCallFrame.h"
 #include "JSFixedArray.h"
+#include "JSImmutableButterfly.h"
 
 namespace JSC { namespace DFG {
 
@@ -1425,7 +1426,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         read(HeapObjectCount);
         write(HeapObjectCount);
 
-        JSFixedArray* array = node->castOperand<JSFixedArray*>();
+        auto* array = node->castOperand<JSImmutableButterfly*>();
         unsigned numElements = array->length();
         def(HeapLocation(ArrayLengthLoc, Butterfly_publicLength, node),
             LazyNode(graph.freeze(jsNumber(numElements))));
index 757c9b4..d214df7 100644 (file)
@@ -175,8 +175,10 @@ private:
             case ArrayifyToStructure: {
                 AbstractValue& value = m_state.forNode(node->child1());
                 RegisteredStructureSet set;
-                if (node->op() == ArrayifyToStructure)
+                if (node->op() == ArrayifyToStructure) {
                     set = node->structure();
+                    ASSERT(!isCopyOnWrite(node->structure()->indexingMode()));
+                }
                 else {
                     set = node->structureSet();
                     if ((SpecCellCheck & SpecEmpty) && node->child1().useKind() == CellUse && m_state.forNode(node->child1()).m_type & SpecEmpty) {
index ecbb3d3..ae76a94 100644 (file)
@@ -606,7 +606,7 @@ private:
         case StringCharAt:
         case StringCharCodeAt: {
             // Currently we have no good way of refining these.
-            ASSERT(node->arrayMode() == ArrayMode(Array::String));
+            ASSERT(node->arrayMode() == ArrayMode(Array::String, Array::Read));
             blessArrayOperation(node->child1(), node->child2(), node->child3());
             fixEdge<KnownCellUse>(node->child1());
             fixEdge<Int32Use>(node->child2());
@@ -877,7 +877,7 @@ private:
             }
             
             if (badNews) {
-                node->setArrayMode(ArrayMode(Array::Generic));
+                node->setArrayMode(ArrayMode(Array::Generic, node->arrayMode().action()));
                 break;
             }
             
@@ -2184,13 +2184,13 @@ private:
     template<UseKind useKind>
     void attemptToForceStringArrayModeByToStringConversion(ArrayMode& arrayMode, Node* node)
     {
-        ASSERT(arrayMode == ArrayMode(Array::Generic));
+        ASSERT(arrayMode == ArrayMode(Array::Generic, Array::Read));
         
         if (!m_graph.canOptimizeStringObjectAccess(node->origin.semantic))
             return;
         
         createToString<useKind>(node, node->child1());
-        arrayMode = ArrayMode(Array::String);
+        arrayMode = ArrayMode(Array::String, Array::Read);
     }
     
     template<UseKind useKind>
@@ -3126,7 +3126,7 @@ private:
         CodeBlock* profiledBlock = m_graph.baselineCodeBlockFor(node->origin.semantic);
         ArrayProfile* arrayProfile = 
             profiledBlock->getArrayProfile(node->origin.semantic.bytecodeIndex);
-        ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions);
+        ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions, Array::Read);
         if (arrayProfile) {
             ConcurrentJSLocker locker(profiledBlock->m_lock);
             arrayProfile->computeUpdatedPrediction(locker, profiledBlock);
@@ -3139,7 +3139,7 @@ private:
                 // GetById. I.e. ForceExit = Generic. So, there is no harm - and only
                 // profit - from treating the Unprofiled case as
                 // SelectUsingPredictions.
-                arrayMode = ArrayMode(Array::SelectUsingPredictions);
+                arrayMode = ArrayMode(Array::SelectUsingPredictions, Array::Read);
             }
         }
             
index 1fe87b7..e17ac7d 100644 (file)
@@ -322,7 +322,7 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
     if (node->hasLazyJSValue())
         out.print(comma, node->lazyJSValue());
     if (node->hasIndexingType())
-        out.print(comma, IndexingTypeDump(node->indexingType()));
+        out.print(comma, IndexingTypeDump(node->indexingMode()));
     if (node->hasTypedArrayType())
         out.print(comma, node->typedArrayType());
     if (node->hasPhi())
index 6236309..005f836 100644 (file)
@@ -109,7 +109,7 @@ struct NewArrayBufferData {
     union {
         struct {
             unsigned vectorLengthHint;
-            unsigned indexingType;
+            unsigned indexingMode;
         };
         uint64_t asQuadWord;
     };
@@ -1189,7 +1189,15 @@ public:
     {
         ASSERT(hasIndexingType());
         if (op() == NewArrayBuffer || op() == PhantomNewArrayBuffer)
-            return static_cast<IndexingType>(newArrayBufferData().indexingType);
+            return static_cast<IndexingType>(newArrayBufferData().indexingMode) & IndexingTypeMask;
+        return static_cast<IndexingType>(m_opInfo.as<uint32_t>());
+    }
+
+    IndexingType indexingMode()
+    {
+        ASSERT(hasIndexingType());
+        if (op() == NewArrayBuffer || op() == PhantomNewArrayBuffer)
+            return static_cast<IndexingType>(newArrayBufferData().indexingMode);
         return static_cast<IndexingType>(m_opInfo.as<uint32_t>());
     }
     
index a471bf0..00f5977 100644 (file)
@@ -1197,7 +1197,7 @@ void OSRExit::compileExit(CCallHelpers& jit, VM& vm, const OSRExit& exit, const
 #if USE(JSVALUE64)
                 jit.load8(AssemblyHelpers::Address(value, JSCell::indexingTypeAndMiscOffset()), scratch1);
 #else
-                jit.load8(AssemblyHelpers::Address(scratch1, Structure::indexingTypeIncludingHistoryOffset()), scratch1);
+                jit.load8(AssemblyHelpers::Address(scratch1, Structure::indexingModeIncludingHistoryOffset()), scratch1);
 #endif
                 jit.move(AssemblyHelpers::TrustedImm32(1), scratch2);
                 jit.lshift32(scratch1, scratch2);
index 9e7daf4..e08d2e9 100644 (file)
@@ -53,6 +53,7 @@
 #include "JSFixedArray.h"
 #include "JSGenericTypedArrayViewConstructorInlines.h"
 #include "JSGlobalObjectFunctions.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSMap.h"
 #include "JSPropertyNameEnumerator.h"
@@ -942,7 +943,7 @@ void JIT_OPERATION operationPutByValDirectBeyondArrayBoundsNonStrict(ExecState*
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
-    
+
     if (index >= 0) {
         object->putDirectIndex(exec, index, JSValue::decode(encodedValue));
         return;
@@ -1004,7 +1005,7 @@ EncodedJSValue JIT_OPERATION operationArrayPushDoubleMultiple(ExecState* exec, J
     // If it can cause any JS interactions, we can call the caller JS function of this function and overwrite the
     // content of ScratchBuffer. If the IndexingType is now ArrayWithDouble, we can ensure
     // that there is no indexed accessors in this object and its prototype chain.
-    ASSERT(array->indexingType() == ArrayWithDouble);
+    ASSERT(array->indexingMode() == ArrayWithDouble);
 
     double* values = static_cast<double*>(buffer);
     for (int32_t i = 0; i < elementCount; ++i) {
@@ -1508,11 +1509,17 @@ char* JIT_OPERATION operationNewArrayWithSizeAndHint(ExecState* exec, Structure*
     return bitwise_cast<char*>(result);
 }
 
-JSCell* JIT_OPERATION operationNewArrayBuffer(ExecState* exec, Structure* arrayStructure, JSCell* fixedArray, size_t size)
+JSCell* JIT_OPERATION operationNewArrayBuffer(ExecState* exec, Structure* arrayStructure, JSCell* immutableButterflyCell)
 {
     VM& vm = exec->vm();
     NativeCallFrameTracer tracer(&vm, exec);
-    return constructArray(exec, arrayStructure, jsCast<JSFixedArray*>(fixedArray)->values(), size);
+    ASSERT(!arrayStructure->outOfLineCapacity());
+    auto* immutableButterfly = jsCast<JSImmutableButterfly*>(immutableButterflyCell);
+    ASSERT(arrayStructure->indexingMode() == immutableButterfly->indexingMode() || hasAnyArrayStorage(arrayStructure->indexingMode()));
+    auto* result = CommonSlowPaths::allocateNewArrayBuffer(vm, arrayStructure, immutableButterfly);
+    ASSERT(result->indexingMode() == result->structure()->indexingMode());
+    ASSERT(result->structure() == arrayStructure);
+    return result;
 }
 
 char* JIT_OPERATION operationNewInt8ArrayWithSize(
@@ -1852,8 +1859,10 @@ char* JIT_OPERATION operationEnsureInt32(ExecState* exec, JSCell* cell)
     
     if (!cell->isObject())
         return 0;
-    
-    return reinterpret_cast<char*>(asObject(cell)->ensureInt32(vm).data());
+
+    auto* result = reinterpret_cast<char*>(asObject(cell)->ensureWritableInt32(vm).data());
+    ASSERT((!isCopyOnWrite(asObject(cell)->indexingMode()) && hasInt32(cell->indexingMode())) || !result);
+    return result;
 }
 
 char* JIT_OPERATION operationEnsureDouble(ExecState* exec, JSCell* cell)
@@ -1863,8 +1872,10 @@ char* JIT_OPERATION operationEnsureDouble(ExecState* exec, JSCell* cell)
     
     if (!cell->isObject())
         return 0;
-    
-    return reinterpret_cast<char*>(asObject(cell)->ensureDouble(vm).data());
+
+    auto* result = reinterpret_cast<char*>(asObject(cell)->ensureWritableDouble(vm).data());
+    ASSERT((!isCopyOnWrite(asObject(cell)->indexingMode()) && hasDouble(cell->indexingMode())) || !result);
+    return result;
 }
 
 char* JIT_OPERATION operationEnsureContiguous(ExecState* exec, JSCell* cell)
@@ -1875,7 +1886,9 @@ char* JIT_OPERATION operationEnsureContiguous(ExecState* exec, JSCell* cell)
     if (!cell->isObject())
         return 0;
     
-    return reinterpret_cast<char*>(asObject(cell)->ensureContiguous(vm).data());
+    auto* result = reinterpret_cast<char*>(asObject(cell)->ensureWritableContiguous(vm).data());
+    ASSERT((!isCopyOnWrite(asObject(cell)->indexingMode()) && hasContiguous(cell->indexingMode())) || !result);
+    return result;
 }
 
 char* JIT_OPERATION operationEnsureArrayStorage(ExecState* exec, JSCell* cell)
@@ -1886,7 +1899,9 @@ char* JIT_OPERATION operationEnsureArrayStorage(ExecState* exec, JSCell* cell)
     if (!cell->isObject())
         return 0;
 
-    return reinterpret_cast<char*>(asObject(cell)->ensureArrayStorage(vm));
+    auto* result = reinterpret_cast<char*>(asObject(cell)->ensureArrayStorage(vm));
+    ASSERT((!isCopyOnWrite(asObject(cell)->indexingMode()) && hasAnyArrayStorage(cell->indexingMode())) || !result);
+    return result;
 }
 
 EncodedJSValue JIT_OPERATION operationHasGenericProperty(ExecState* exec, EncodedJSValue encodedBaseValue, JSCell* propertyName)
index 16ba1a2..6309ec3 100644 (file)
@@ -169,7 +169,7 @@ JSCell* JIT_OPERATION operationCreateScopedArguments(ExecState*, Structure*, Reg
 JSCell* JIT_OPERATION operationCreateClonedArgumentsDuringExit(ExecState*, InlineCallFrame*, JSFunction*, uint32_t argumentCount);
 JSCell* JIT_OPERATION operationCreateClonedArguments(ExecState*, Structure*, Register* argumentStart, uint32_t length, JSFunction* callee);
 JSCell* JIT_OPERATION operationCreateRest(ExecState*, Register* argumentStart, unsigned numberOfArgumentsToSkip, unsigned arraySize);
-JSCell* JIT_OPERATION operationNewArrayBuffer(ExecState*, Structure*, JSCell*, size_t) WTF_INTERNAL;
+JSCell* JIT_OPERATION operationNewArrayBuffer(ExecState*, Structure*, JSCell*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationSetAdd(ExecState*, JSCell*, EncodedJSValue, int32_t) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationMapSet(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue, int32_t) WTF_INTERNAL;
 void JIT_OPERATION operationWeakSetAdd(ExecState*, JSCell*, JSCell*, int32_t) WTF_INTERNAL;
index 1d56d46..d18b102 100644 (file)
@@ -54,6 +54,7 @@
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
 #include "JSGeneratorFunction.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSPropertyNameEnumerator.h"
 #include "LinkBuffer.h"
@@ -90,6 +91,7 @@ SpeculativeJIT::~SpeculativeJIT()
 
 void SpeculativeJIT::emitAllocateRawObject(GPRReg resultGPR, RegisteredStructure structure, GPRReg storageGPR, unsigned numElements, unsigned vectorLength)
 {
+    ASSERT(!isCopyOnWrite(structure->indexingMode()));
     IndexingType indexingType = structure->indexingType();
     bool hasIndexingHeader = hasIndexedProperties(indexingType);
 
@@ -729,7 +731,11 @@ void SpeculativeJIT::silentFill(const SilentRegisterSavePlan& plan)
 JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode)
 {
     JITCompiler::JumpList result;
-    
+
+    IndexingType indexingModeMask = IsArray | IndexingShapeMask;
+    if (arrayMode.action() == Array::Write)
+        indexingModeMask |= CopyOnWrite;
+
     switch (arrayMode.type()) {
     case Array::Int32:
     case Array::Double:
@@ -743,20 +749,20 @@ JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGP
             return result;
 
         case Array::Array:
-            m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR);
+            m_jit.and32(TrustedImm32(indexingModeMask), tempGPR);
             result.append(m_jit.branch32(
                 MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape)));
             return result;
 
         case Array::NonArray:
         case Array::OriginalNonArray:
-            m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR);
+            m_jit.and32(TrustedImm32(indexingModeMask), tempGPR);
             result.append(m_jit.branch32(
                 MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)));
             return result;
 
         case Array::PossiblyArray:
-            m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR);
+            m_jit.and32(TrustedImm32(indexingModeMask & ~IsArray), tempGPR);
             result.append(m_jit.branch32(MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape)));
             return result;
         }
@@ -876,6 +882,8 @@ void SpeculativeJIT::arrayify(Node* node, GPRReg baseReg, GPRReg propertyReg)
     MacroAssembler::JumpList slowPath;
     
     if (node->op() == ArrayifyToStructure) {
+        ASSERT(!isCopyOnWrite(node->structure()->indexingMode()));
+        ASSERT((node->structure()->indexingType() & IndexingShapeMask) == node->arrayMode().shapeMask());
         slowPath.append(m_jit.branchWeakStructure(
             JITCompiler::NotEqual,
             JITCompiler::Address(baseReg, JSCell::structureIDOffset()),
@@ -2164,7 +2172,7 @@ void SpeculativeJIT::compileGetByValOnString(Node* node)
     }
 #endif
 
-    ASSERT(ArrayMode(Array::String).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.child(node, 0))));
+    ASSERT(ArrayMode(Array::String, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.child(node, 0))));
 
     // unsigned comparison so we can filter out negative indices and indices that are too large
     JITCompiler::Jump outOfBounds = m_jit.branch32(
@@ -6647,7 +6655,7 @@ void SpeculativeJIT::compileGetByValOnDirectArguments(Node* node)
     if (!m_compileOkay)
         return;
     
-    ASSERT(ArrayMode(Array::DirectArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0))));
+    ASSERT(ArrayMode(Array::DirectArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0))));
     
     speculationCheck(
         ExoticObjectMode, JSValueSource(), 0,
@@ -6694,7 +6702,7 @@ void SpeculativeJIT::compileGetByValOnScopedArguments(Node* node)
     if (!m_compileOkay)
         return;
     
-    ASSERT(ArrayMode(Array::ScopedArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0))));
+    ASSERT(ArrayMode(Array::ScopedArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(m_graph.varArgChild(node, 0))));
     
     m_jit.loadPtr(
         MacroAssembler::Address(baseReg, ScopedArguments::offsetOfStorage()), resultRegs.payloadGPR());
@@ -6841,7 +6849,7 @@ void SpeculativeJIT::compileGetArrayLength(Node* node)
         if (!m_compileOkay)
             return;
         
-        ASSERT(ArrayMode(Array::DirectArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1())));
+        ASSERT(ArrayMode(Array::DirectArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1())));
         
         speculationCheck(
             ExoticObjectMode, JSValueSource(), 0,
@@ -6865,7 +6873,7 @@ void SpeculativeJIT::compileGetArrayLength(Node* node)
         if (!m_compileOkay)
             return;
         
-        ASSERT(ArrayMode(Array::ScopedArguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1())));
+        ASSERT(ArrayMode(Array::ScopedArguments, Array::Read).alreadyChecked(m_jit.graph(), node, m_state.forNode(node->child1())));
         
         m_jit.loadPtr(
             MacroAssembler::Address(baseReg, ScopedArguments::offsetOfStorage()), resultReg);
@@ -7499,7 +7507,7 @@ void SpeculativeJIT::compileCreateRest(Node* node)
         GPRReg arrayResultGPR = arrayResult.gpr();
 
         bool shouldAllowForArrayStorageStructureForLargeArrays = false;
-        ASSERT(m_jit.graph().globalObjectFor(node->origin.semantic)->restParameterStructure()->indexingType() == ArrayWithContiguous || m_jit.graph().globalObjectFor(node->origin.semantic)->isHavingABadTime());
+        ASSERT(m_jit.graph().globalObjectFor(node->origin.semantic)->restParameterStructure()->indexingMode() == ArrayWithContiguous || m_jit.graph().globalObjectFor(node->origin.semantic)->isHavingABadTime());
         compileAllocateNewArrayWithSize(m_jit.graph().globalObjectFor(node->origin.semantic), arrayResultGPR, arrayLengthGPR, ArrayWithContiguous, shouldAllowForArrayStorageStructureForLargeArrays);
 
         GPRTemporary argumentsStart(this);
@@ -8068,7 +8076,8 @@ void SpeculativeJIT::compileArraySlice(Node* node)
     {
         SpeculateCellOperand cell(this, m_jit.graph().varArgChild(node, 0));
         m_jit.load8(MacroAssembler::Address(cell.gpr(), JSCell::indexingTypeAndMiscOffset()), tempValue);
-        m_jit.and32(TrustedImm32(AllArrayTypesAndHistory), tempValue);
+        // We can ignore the writability of the cell since we won't write to the source.
+        m_jit.and32(TrustedImm32(AllWritableArrayTypesAndHistory), tempValue);
     }
 
     {
@@ -12033,47 +12042,27 @@ void SpeculativeJIT::compileStrCat(Node* node)
 void SpeculativeJIT::compileNewArrayBuffer(Node* node)
 {
     JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
-    JSFixedArray* array = node->castOperand<JSFixedArray*>();
-    unsigned numElements = array->length();
-    IndexingType indexingType = node->indexingType();
-    if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(indexingType)) {
-        unsigned vectorLengthHint = node->vectorLengthHint();
-        ASSERT(vectorLengthHint >= numElements);
+    auto* array = node->castOperand<JSImmutableButterfly*>();
 
+    IndexingType indexingMode = node->indexingMode();
+    RegisteredStructure structure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingMode));
+
+    if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(indexingMode)) {
         GPRTemporary result(this);
-        GPRTemporary storage(this);
+        GPRTemporary scratch1(this);
+        GPRTemporary scratch2(this);
 
         GPRReg resultGPR = result.gpr();
-        GPRReg storageGPR = storage.gpr();
+        GPRReg scratch1GPR = scratch1.gpr();
+        GPRReg scratch2GPR = scratch2.gpr();
 
-        emitAllocateRawObject(resultGPR, m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType)), storageGPR, numElements, vectorLengthHint);
+        MacroAssembler::JumpList slowCases;
 
-        DFG_ASSERT(m_jit.graph(), node, indexingType & IsArray, indexingType);
+        emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(structure), TrustedImmPtr(array->toButterfly()), scratch1GPR, scratch2GPR, slowCases);
 
-        for (unsigned index = 0; index < numElements; ++index) {
-#if USE(JSVALUE64)
-            int64_t value;
-            if (indexingType == ArrayWithDouble)
-                value = bitwise_cast<int64_t>(array->get(index).asNumber());
-            else
-                value = JSValue::encode(array->get(index));
-            static_assert(sizeof(double) == sizeof(JSValue), "");
-            m_jit.store64(Imm64(value), MacroAssembler::Address(storageGPR, sizeof(JSValue) * index));
-#else
-            union {
-                int32_t halves[2];
-                double doubleValue;
-                int64_t encodedValue;
-            } u;
-            if (node->indexingType() == ArrayWithDouble)
-                u.doubleValue = array->get(index).asNumber();
-            else
-                u.encodedValue = JSValue::encode(array->get(index));
-            static_assert(sizeof(double) == sizeof(JSValue), "");
-            m_jit.store32(Imm32(u.halves[0]), MacroAssembler::Address(storageGPR, sizeof(JSValue) * index));
-            m_jit.store32(Imm32(u.halves[1]), MacroAssembler::Address(storageGPR, sizeof(JSValue) * index + sizeof(int32_t)));
-#endif
-        }
+        addSlowPathGenerator(slowPathCall(slowCases, this, operationNewArrayBuffer, result.gpr(), structure, array));
+
+        DFG_ASSERT(m_jit.graph(), node, indexingMode & IsArray, indexingMode);
         cellResult(resultGPR, node);
         return;
     }
@@ -12081,7 +12070,7 @@ void SpeculativeJIT::compileNewArrayBuffer(Node* node)
     flushRegisters();
     GPRFlushedCallResult result(this);
 
-    callOperation(operationNewArrayBuffer, result.gpr(), m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())), TrustedImmPtr(node->cellOperand()), size_t(numElements));
+    callOperation(operationNewArrayBuffer, result.gpr(), m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())), TrustedImmPtr(node->cellOperand()));
     m_jit.exceptionCheck();
 
     cellResult(result.gpr(), node);
index 272c57a..b97ccbd 100644 (file)
@@ -3295,7 +3295,7 @@ void SpeculativeJIT::compile(Node* node)
         SpeculateCellOperand base(this, node->child1());
         GPRReg baseGPR = base.gpr();
         
-        ASSERT_UNUSED(oldStructure, oldStructure->indexingType() == newStructure->indexingType());
+        ASSERT_UNUSED(oldStructure, oldStructure->indexingMode() == newStructure->indexingMode());
         ASSERT(oldStructure->typeInfo().type() == newStructure->typeInfo().type());
         ASSERT(oldStructure->typeInfo().inlineTypeFlags() == newStructure->typeInfo().inlineTypeFlags());
         m_jit.storePtr(TrustedImmPtr(newStructure), MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()));
index 50f2645..513862d 100644 (file)
@@ -3576,7 +3576,7 @@ void SpeculativeJIT::compile(Node* node)
         SpeculateCellOperand base(this, node->child1());
         GPRReg baseGPR = base.gpr();
         
-        ASSERT_UNUSED(oldStructure, oldStructure->indexingType() == newStructure->indexingType());
+        ASSERT_UNUSED(oldStructure, oldStructure->indexingMode() == newStructure->indexingMode());
         ASSERT(oldStructure->typeInfo().type() == newStructure->typeInfo().type());
         ASSERT(oldStructure->typeInfo().inlineTypeFlags() == newStructure->typeInfo().inlineTypeFlags());
         m_jit.store32(MacroAssembler::TrustedImm32(newStructure->id()), MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()));
index d7726bb..b7895e5 100644 (file)
@@ -356,7 +356,7 @@ public:
                     VALIDATE((node), node->vectorLengthHint() >= node->numChildren());
                     break;
                 case NewArrayBuffer:
-                    VALIDATE((node), node->vectorLengthHint() >= node->castOperand<JSFixedArray*>()->length());
+                    VALIDATE((node), node->vectorLengthHint() >= node->castOperand<JSImmutableButterfly*>()->length());
                     break;
                 default:
                     break;
@@ -794,7 +794,7 @@ private:
 
                 case PhantomNewArrayBuffer:
                     VALIDATE((node), m_graph.m_form == SSA);
-                    VALIDATE((node), node->vectorLengthHint() >= node->castOperand<JSFixedArray*>()->length());
+                    VALIDATE((node), node->vectorLengthHint() >= node->castOperand<JSImmutableButterfly*>()->length());
                     break;
 
                 case NewArrayWithSpread: {
index a828711..5f450d1 100644 (file)
@@ -115,7 +115,7 @@ namespace JSC { namespace FTL {
     macro(Structure_prototype, Structure::prototypeOffset()) \
     macro(Structure_structureID, Structure::structureIDOffset()) \
     macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
-    macro(Structure_indexingTypeIncludingHistory, Structure::indexingTypeIncludingHistoryOffset()) \
+    macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \
index 911d62a..d98a8d8 100644 (file)
@@ -76,6 +76,7 @@
 #include "JSAsyncGeneratorFunction.h"
 #include "JSCInlines.h"
 #include "JSGeneratorFunction.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSMap.h"
 #include "OperandsInlines.h"
@@ -3047,7 +3048,7 @@ private:
         
         RegisteredStructure oldStructure = m_node->transition()->previous;
         RegisteredStructure newStructure = m_node->transition()->next;
-        ASSERT_UNUSED(oldStructure, oldStructure->indexingType() == newStructure->indexingType());
+        ASSERT_UNUSED(oldStructure, oldStructure->indexingMode() == newStructure->indexingMode());
         ASSERT(oldStructure->typeInfo().inlineTypeFlags() == newStructure->typeInfo().inlineTypeFlags());
         ASSERT(oldStructure->typeInfo().type() == newStructure->typeInfo().type());
 
@@ -4719,7 +4720,8 @@ private:
         ArrayValues arrayResult;
         {
             LValue indexingType = m_out.load8ZeroExt32(lowCell(m_graph.varArgChild(m_node, 0)), m_heaps.JSCell_indexingTypeAndMisc);
-            indexingType = m_out.bitAnd(indexingType, m_out.constInt32(AllArrayTypesAndHistory));
+            // We can ignore the writability of the cell since we won't write to the source.
+            indexingType = m_out.bitAnd(indexingType, m_out.constInt32(AllWritableArrayTypesAndHistory));
             // When we emit an ArraySlice, we dominate the use of the array by a CheckStructure
             // to ensure the incoming array is one to be one of the original array structures
             // with one of the following indexing shapes: Int32, Contiguous, Double.
@@ -5491,7 +5493,7 @@ private:
                 else {
                     Edge& child = m_graph.varArgChild(m_node, i);
                     if (child->op() == PhantomSpread && child->child1()->op() == PhantomNewArrayBuffer)
-                        startLength += child->child1()->castOperand<JSFixedArray*>()->length();
+                        startLength += child->child1()->castOperand<JSImmutableButterfly*>()->length();
                 }
             }
 
@@ -5539,7 +5541,7 @@ private:
                     if (use->op() == PhantomSpread) {
                         if (use->child1()->op() == PhantomNewArrayBuffer) {
                             IndexedAbstractHeap& heap = m_heaps.indexedContiguousProperties;
-                            auto* array = use->child1()->castOperand<JSFixedArray*>();
+                            auto* array = use->child1()->castOperand<JSImmutableButterfly*>();
                             for (unsigned i = 0; i < array->length(); ++i) {
                                 // Because resulted array from NewArrayWithSpread is always contiguous, we should not generate value
                                 // in Double form even if PhantomNewArrayBuffer's indexingType is ArrayWithDouble.
@@ -5813,39 +5815,32 @@ private:
     {
         JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
         RegisteredStructure structure = m_graph.registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(
-            m_node->indexingType()));
-        JSFixedArray* array = m_node->castOperand<JSFixedArray*>();
-        unsigned numElements = array->length();
-        
-        if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(m_node->indexingType())) {
-            unsigned vectorLengthHint = m_node->vectorLengthHint();
-           
-            ASSERT(vectorLengthHint >= numElements);
-            ArrayValues arrayValues =
-                allocateUninitializedContiguousJSArray(numElements, vectorLengthHint, structure);
-            
-            for (unsigned index = 0; index < numElements; ++index) {
-                int64_t value;
-                if (hasDouble(m_node->indexingType()))
-                    value = bitwise_cast<int64_t>(array->get(index).asNumber());
-                else
-                    value = JSValue::encode(array->get(index));
-                
-                m_out.store64(
-                    m_out.constInt64(value),
-                    arrayValues.butterfly,
-                    m_heaps.forIndexingType(m_node->indexingType())->at(index));
-            }
-            
+            m_node->indexingMode()));
+        auto* immutableButterfly = m_node->castOperand<JSImmutableButterfly*>();
+
+        if (!globalObject->isHavingABadTime() && !hasAnyArrayStorage(m_node->indexingMode())) {
+            LBasicBlock slowPath = m_out.newBlock();
+            LBasicBlock continuation = m_out.newBlock();
+
+            LValue fastArray = allocateObject<JSArray>(structure, m_out.constIntPtr(immutableButterfly->toButterfly()), slowPath);
+            ValueFromBlock fastResult = m_out.anchor(fastArray);
+            m_out.jump(continuation);
+
+            m_out.appendTo(slowPath, continuation);
+            LValue slowArray = vmCall(Int64, m_out.operation(operationNewArrayBuffer), m_callFrame, weakStructure(structure), m_out.weakPointer(m_node->cellOperand()));
+            ValueFromBlock slowResult = m_out.anchor(slowArray);
+            m_out.jump(continuation);
+
+            m_out.appendTo(continuation);
+
             mutatorFence();
-            setJSValue(arrayValues.array);
+            setJSValue(m_out.phi(pointerType(), slowResult, fastResult));
             return;
         }
         
         setJSValue(vmCall(
             Int64, m_out.operation(operationNewArrayBuffer), m_callFrame,
-            weakStructure(structure), m_out.weakPointer(m_node->cellOperand()),
-            m_out.constIntPtr(numElements)));
+            weakStructure(structure), m_out.weakPointer(m_node->cellOperand())));
     }
 
     void compileNewArrayWithSize()
@@ -7624,7 +7619,7 @@ private:
             }
 
             if (target->op() == PhantomNewArrayBuffer) {
-                staticArgumentCount += target->castOperand<JSFixedArray*>()->length();
+                staticArgumentCount += target->castOperand<JSImmutableButterfly*>()->length();
                 return;
             }
 
@@ -7775,7 +7770,7 @@ private:
                         }
 
                         if (target->op() == PhantomNewArrayBuffer) {
-                            auto* array = target->castOperand<JSFixedArray*>();
+                            auto* array = target->castOperand<JSImmutableButterfly*>();
                             Checked<int32_t> offsetCount { 1 };
                             for (unsigned i = array->length(); i--; ++offsetCount) {
                                 // Because varargs values are drained as JSValue, we should not generate value
@@ -8462,7 +8457,7 @@ private:
             }
 
             if (target->op() == PhantomNewArrayBuffer) {
-                numberOfStaticArguments += target->castOperand<JSFixedArray*>()->length();
+                numberOfStaticArguments += target->castOperand<JSImmutableButterfly*>()->length();
                 return;
             }
 
@@ -8507,7 +8502,7 @@ private:
             }
 
             if (target->op() == PhantomNewArrayBuffer) {
-                auto* array = target->castOperand<JSFixedArray*>();
+                auto* array = target->castOperand<JSImmutableButterfly*>();
                 for (unsigned i = 0; i < array->length(); i++) {
                     // Because forwarded values are drained as JSValue, we should not generate value
                     // in Double form even if PhantomNewArrayBuffer's indexingType is ArrayWithDouble.
@@ -12618,7 +12613,7 @@ private:
         LValue id = m_out.load32(structure, m_heaps.Structure_structureID);
         m_out.store32(id, object, m_heaps.JSCell_structureID);
 
-        LValue blob = m_out.load32(structure, m_heaps.Structure_indexingTypeIncludingHistory);
+        LValue blob = m_out.load32(structure, m_heaps.Structure_indexingModeIncludingHistory);
         m_out.store32(blob, object, m_heaps.JSCell_usefulBytes);
     }
 
@@ -15030,6 +15025,10 @@ private:
         case Array::Contiguous:
         case Array::Undecided:
         case Array::ArrayStorage: {
+            IndexingType indexingModeMask = IsArray | IndexingShapeMask;
+            if (arrayMode.action() == Array::Write)
+                indexingModeMask |= CopyOnWrite;
+
             IndexingType shape = arrayMode.shapeMask();
             LValue indexingType = m_out.load8ZeroExt32(cell, m_heaps.JSCell_indexingTypeAndMisc);
 
@@ -15040,18 +15039,18 @@ private:
 
             case Array::Array:
                 return m_out.equal(
-                    m_out.bitAnd(indexingType, m_out.constInt32(IsArray | IndexingShapeMask)),
+                    m_out.bitAnd(indexingType, m_out.constInt32(indexingModeMask)),
                     m_out.constInt32(IsArray | shape));
 
             case Array::NonArray:
             case Array::OriginalNonArray:
                 return m_out.equal(
-                    m_out.bitAnd(indexingType, m_out.constInt32(IsArray | IndexingShapeMask)),
+                    m_out.bitAnd(indexingType, m_out.constInt32(indexingModeMask)),
                     m_out.constInt32(shape));
 
             case Array::PossiblyArray:
                 return m_out.equal(
-                    m_out.bitAnd(indexingType, m_out.constInt32(IndexingShapeMask)),
+                    m_out.bitAnd(indexingType, m_out.constInt32(indexingModeMask & ~IsArray)),
                     m_out.constInt32(shape));
             }
             break;
index 7508bb0..a77655b 100644 (file)
@@ -39,6 +39,7 @@
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
 #include "JSGeneratorFunction.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "RegExpObject.h"
 
@@ -458,11 +459,11 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
     }
 
     case PhantomNewArrayBuffer: {
-        JSFixedArray* array = nullptr;
+        JSImmutableButterfly* array = nullptr;
         for (unsigned i = materialization->properties().size(); i--;) {
             const ExitPropertyValue& property = materialization->properties()[i];
             if (property.location().kind() == NewArrayBufferPLoc) {
-                array = jsCast<JSFixedArray*>(JSValue::decode(values[i]));
+                array = jsCast<JSImmutableButterfly*>(JSValue::decode(values[i]));
                 break;
             }
         }
@@ -473,7 +474,9 @@ extern "C" JSCell* JIT_OPERATION operationMaterializeObjectInOSR(
         CodeBlock* codeBlock = baselineCodeBlockForOriginAndBaselineCodeBlock(materialization->origin(), exec->codeBlock());
         Instruction* currentInstruction = &codeBlock->instructions()[materialization->origin().bytecodeIndex];
         RELEASE_ASSERT(Interpreter::getOpcodeID(currentInstruction[0].u.opcode) == op_new_array_buffer);
-        return constructArray(exec, currentInstruction[3].u.arrayAllocationProfile, array->values(), array->length());
+        Structure* structure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(currentInstruction[3].u.arrayAllocationProfile->selectIndexingType());
+        ASSERT(!structure->outOfLineCapacity());
+        return JSArray::createWithButterfly(vm, nullptr, structure, array->toButterfly());
     }
 
     case PhantomNewArrayWithSpread: {
index 8300bd0..fa25fd2 100644 (file)
@@ -112,13 +112,13 @@ def toCpp(name):
 
 
 def writeInstructionAccessor(bytecodeHFile, typeName, name):
-    bytecodeHFile.write("    {0}& {1}() {{ return *reinterpret_cast<{0}*>(&m_{1}); }}\n".format(typeName, name))
-    bytecodeHFile.write("    const {0}& {1}() const {{ return *reinterpret_cast<const {0}*>(&m_{1}); }}\n".format(typeName, name))
+    bytecodeHFile.write("    {0}& {1}() {{ return *bitwise_cast<{0}*>(&m_{1}); }}\n".format(typeName, name))
+    bytecodeHFile.write("    const {0}& {1}() const {{ return *bitwise_cast<const {0}*>(&m_{1}); }}\n".format(typeName, name))
 
 
 def writeInstructionMember(bytecodeHFile, typeName, name):
     bytecodeHFile.write("    std::aligned_storage<sizeof({0}), sizeof(Instruction)>::type m_{1};\n".format(typeName, name))
-
+    bytecodeHFile.write("    static_assert(sizeof({0}) <= sizeof(Instruction), \"Size of {0} shouldn't be bigger than an Instruction.\");\n".format(typeName, name))
 
 def writeStruct(bytecodeHFile, bytecode):
     bytecodeHFile.write("struct {0} {{\n".format(toCpp(bytecode["name"])))
index 48a911c..bb623ca 100644 (file)
@@ -52,6 +52,7 @@
 #include "JSBoundFunction.h"
 #include "JSCInlines.h"
 #include "JSFixedArray.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSModuleEnvironment.h"
 #include "JSString.h"
@@ -199,6 +200,9 @@ unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVa
     case JSFixedArrayType:
         length = jsCast<JSFixedArray*>(cell)->size();
         break;
+    case JSImmutableButterflyType:
+        length = jsCast<JSImmutableButterfly*>(cell)->length();
+        break;
     case StringType:
     case SymbolType:
     case BigIntType:
@@ -270,6 +274,10 @@ void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue
         scope.release();
         jsCast<JSFixedArray*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length);
         return;
+    case JSImmutableButterflyType:
+        scope.release();
+        jsCast<JSImmutableButterfly*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length);
+        return; 
     default: {
         ASSERT(arguments.isObject());
         JSObject* object = jsCast<JSObject*>(cell);
index 29ad53e..8529dda 100644 (file)
@@ -405,7 +405,7 @@ void AssemblyHelpers::emitStoreStructureWithTypeInfo(AssemblyHelpers& jit, Trust
         jit.abortWithReason(AHStructureIDIsValid);
         correctStructure.link(&jit);
 
-        Jump correctIndexingType = jit.branch8(Equal, MacroAssembler::Address(dest, JSCell::indexingTypeAndMiscOffset()), TrustedImm32(structurePtr->indexingTypeIncludingHistory()));
+        Jump correctIndexingType = jit.branch8(Equal, MacroAssembler::Address(dest, JSCell::indexingTypeAndMiscOffset()), TrustedImm32(structurePtr->indexingModeIncludingHistory()));
         jit.abortWithReason(AHIndexingTypeIsValid);
         correctIndexingType.link(&jit);
 
index a8bf34d..1e387f6 100644 (file)
@@ -1483,7 +1483,7 @@ public:
         store64(scratch, MacroAssembler::Address(dest, JSCell::structureIDOffset()));
 #else
         // Store all the info flags using a single 32-bit wide load and store.
-        load32(MacroAssembler::Address(structure, Structure::indexingTypeIncludingHistoryOffset()), scratch);
+        load32(MacroAssembler::Address(structure, Structure::indexingModeIncludingHistoryOffset()), scratch);
         store32(scratch, MacroAssembler::Address(dest, JSCell::indexingTypeAndMiscOffset()));
 
         // Store the StructureID
index fa456fa..4741e9a 100644 (file)
@@ -689,6 +689,7 @@ static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue s
     VM& vm = callFrame->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
     bool isStrictMode = callFrame->codeBlock()->isStrictMode();
+
     if (LIKELY(subscript.isUInt32())) {
         // Despite its name, JSValue::isUInt32 will return true only for positive boxed int32_t; all those values are valid array indices.
         byValInfo->tookSlowPath = true;
@@ -757,6 +758,9 @@ static OptimizationResult tryPutByValOptimize(ExecState* exec, JSValue baseValue
 
     VM& vm = exec->vm();
 
+    if (baseValue.isObject() && isCopyOnWrite(baseValue.getObject()->indexingMode()))
+        return OptimizationResult::GiveUp;
+
     if (baseValue.isObject() && subscript.isInt32()) {
         JSObject* object = asObject(baseValue);
 
index e636f71..ec792f4 100644 (file)
@@ -302,11 +302,14 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction)
         zeroExtend32ToPtr(regT1, regT1);
     }
     emitArrayProfilingSiteWithCell(regT0, regT2, profile);
-    and32(TrustedImm32(IndexingShapeMask), regT2);
-    
+
     PatchableJump badType;
     JumpList slowCases;
-    
+
+    // TODO: Maybe we should do this inline?
+    addSlowCase(branchTest32(NonZero, regT2, TrustedImm32(CopyOnWrite)));
+    and32(TrustedImm32(IndexingShapeMask), regT2);
+
     JITArrayMode mode = chooseArrayMode(profile);
     switch (mode) {
     case JITInt32:
@@ -462,25 +465,9 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas
     int base = currentInstruction[1].u.operand;
     int property = currentInstruction[2].u.operand;
     int value = currentInstruction[3].u.operand;
-    JITArrayMode mode = m_byValCompilationInfo[m_byValInstructionIndex].arrayMode;
     ByValInfo* byValInfo = m_byValCompilationInfo[m_byValInstructionIndex].byValInfo;
 
-    linkSlowCaseIfNotJSCell(iter, base); // base cell check
-    if (!isOperandConstantInt(property))
-        linkSlowCase(iter); // property int32 check
-    linkSlowCase(iter); // base not array check
-    
-    linkSlowCase(iter); // out of bounds
-    
-    switch (mode) {
-    case JITInt32:
-    case JITDouble:
-        linkSlowCase(iter); // value type check
-        break;
-    default:
-        break;
-    }
-    
+    linkAllSlowCases(iter);
     Label slowPath = label();
 
     emitGetVirtualRegister(base, regT0);
index 85ec0b2..8f19fa3 100644 (file)
@@ -441,6 +441,12 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
         if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
             return GiveUpOnCache;
 
+        // FIXME: We should try to do something smarter here...
+        if (isCopyOnWrite(structure->indexingMode()))
+            return GiveUpOnCache;
+        // We can't end up storing to a CoW on the prototype since it shouldn't own properties.
+        ASSERT(!isCopyOnWrite(slot.base()->indexingMode()));
+
         if (!structure->propertyAccessesAreCacheable())
             return GiveUpOnCache;
 
index 7648705..549149f 100644 (file)
@@ -376,6 +376,7 @@ const DoubleShape              = constexpr DoubleShape
 const ContiguousShape          = constexpr ContiguousShape
 const ArrayStorageShape        = constexpr ArrayStorageShape
 const SlowPutArrayStorageShape = constexpr SlowPutArrayStorageShape
+const CopyOnWrite              = constexpr CopyOnWrite
 
 # Type constants.
 const StringType = constexpr StringType
index d51b6d1..80f41d8 100644 (file)
@@ -1703,6 +1703,7 @@ macro putByVal(slowPath)
     loadi 8[PC], t0
     loadConstantOrVariablePayload(t0, Int32Tag, t3, .opPutByValSlow)
     loadp JSObject::m_butterfly[t1], t0
+    btinz t2, CopyOnWrite, .opPutByValSlow
     andi IndexingShapeMask, t2
     bineq t2, Int32Shape, .opPutByValNotInt32
     contiguousPutByVal(
index 3eea54d..8fc47a1 100644 (file)
@@ -1741,6 +1741,7 @@ macro putByVal(slowPath)
     loadConstantOrVariableInt32(t0, t3, .opPutByValSlow)
     sxi2q t3, t3
     loadCaged(_g_gigacageBasePtrs + Gigacage::BasePtrs::jsValue, constexpr JSVALUE_GIGACAGE_MASK, JSObject::m_butterfly[t1], t0, t5)
+    btinz t2, CopyOnWrite, .opPutByValSlow
     andi IndexingShapeMask, t2
     bineq t2, Int32Shape, .opPutByValNotInt32
     contiguousPutByVal(
index 71fe75b..6485890 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "IndexingHeader.h"
+#include "IndexingType.h"
 #include "PropertyStorage.h"
 #include <wtf/Gigacage.h>
 #include <wtf/Noncopyable.h>
@@ -48,8 +49,63 @@ struct ContiguousData {
         UNUSED_PARAM(length);
     }
 
-    const T& at(size_t index) const { ASSERT(index < m_length); return m_data[index]; }
-    T& at(size_t index) { ASSERT(index < m_length);  return m_data[index]; }
+    struct Data {
+        Data(T& location, IndexingType indexingMode)
+            : m_data(location)
+#if !ASSERT_DISABLED
+            , m_isWritable(!isCopyOnWrite(indexingMode))
+#endif
+        {
+            UNUSED_PARAM(indexingMode);
+        }
+
+        explicit operator bool() const { return !!m_data.get(); }
+
+        const T& operator=(const T& value)
+        {
+            ASSERT(m_isWritable);
+            m_data = value;
+            return value;
+        }
+
+        operator const T&() const { return m_data; }
+
+        // WriteBarrier forwarded methods.
+
+        void set(VM& vm, const JSCell* owner, const JSValue& value)
+        {
+            ASSERT(m_isWritable);
+            m_data.set(vm, owner, value);
+        }
+
+        void setWithoutWriteBarrier(const JSValue& value)
+        {
+            ASSERT(m_isWritable);
+            m_data.setWithoutWriteBarrier(value);
+        }
+
+        void clear()
+        {
+            ASSERT(m_isWritable);
+            m_data.clear();
+        }
+
+        JSValue get() const
+        {
+            return m_data.get();
+        }
+
+
+        T& m_data;
+#if !ASSERT_DISABLED
+        bool m_isWritable;
+#endif
+    };
+
+    const Data at(const JSCell* owner, size_t index) const;
+    Data at(const JSCell* owner, size_t index);
+
+    T& atUnsafe(size_t index) { ASSERT(index < m_length); return m_data[index]; }
 
     T* data() const { return m_data; }
 #if !ASSERT_DISABLED
index 59a662b..6a89a0d 100644 (file)
 
 namespace JSC {
 
+template<typename T>
+const typename ContiguousData<T>::Data ContiguousData<T>::at(const JSCell* owner, size_t index) const
+{
+    ASSERT(index < m_length);
+    return Data(m_data[index], owner->indexingMode());
+}
+
+template<typename T>
+typename ContiguousData<T>::Data ContiguousData<T>::at(const JSCell* owner, size_t index)
+{
+    ASSERT(index < m_length);
+    return Data(m_data[index], owner->indexingMode());
+}
+
 ALWAYS_INLINE unsigned Butterfly::availableContiguousVectorLength(size_t propertyCapacity, unsigned vectorLength)
 {
     size_t cellSize = totalSize(0, propertyCapacity, true, sizeof(EncodedJSValue) * vectorLength);
index 3ad6a76..8f461d0 100644 (file)
@@ -61,7 +61,7 @@ ClonedArguments* ClonedArguments::createEmpty(
             return 0;
 
         for (unsigned i = length; i < vectorLength; ++i)
-            butterfly->contiguous().at(i).clear();
+            butterfly->contiguous().atUnsafe(i).clear();
     }
     
     ClonedArguments* result =
index 253db5e..5e842fb 100644 (file)
@@ -51,6 +51,7 @@
 #include "JSCJSValue.h"
 #include "JSFixedArray.h"
 #include "JSGlobalObjectFunctions.h"
+#include "JSImmutableButterfly.h"
 #include "JSLexicalEnvironment.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
@@ -96,6 +97,7 @@ namespace JSC {
 #define OP_C(index) (exec->r(pc[index].u.operand))
 
 #define GET(operand) (exec->uncheckedR(operand))
+#define GET_C(operand) (exec->r(operand))
 
 #define RETURN_TWO(first, second) do {       \
         return encodeResult(first, second);        \
@@ -1093,8 +1095,34 @@ SLOW_PATH_DECL(slow_path_new_array_with_spread)
 SLOW_PATH_DECL(slow_path_new_array_buffer)
 {
     BEGIN();
-    auto* fixedArray = jsCast<JSFixedArray*>(OP_C(2).jsValue());
-    RETURN(constructArray(exec, pc[3].u.arrayAllocationProfile, fixedArray->values(), fixedArray->length()));
+    auto* newArrayBuffer = bitwise_cast<OpNewArrayBuffer*>(pc);
+    ASSERT(exec->codeBlock()->isConstantRegisterIndex(newArrayBuffer->immutableButterfly()));
+    JSImmutableButterfly* immutableButterfly = bitwise_cast<JSImmutableButterfly*>(GET_C(newArrayBuffer->immutableButterfly()).jsValue().asCell());
+    auto* profile = newArrayBuffer->profile();
+
+    IndexingType indexingMode = profile->selectIndexingType();
+    Structure* structure = exec->lexicalGlobalObject()->arrayStructureForIndexingTypeDuringAllocation(indexingMode);
+    ASSERT(isCopyOnWrite(indexingMode));
+    ASSERT(!structure->outOfLineCapacity());
+
+    if (UNLIKELY(immutableButterfly->indexingMode() != indexingMode)) {
+        auto* newButterfly = JSImmutableButterfly::create(vm, indexingMode, immutableButterfly->length());
+        for (unsigned i = 0; i < immutableButterfly->length(); ++i)
+            newButterfly->setIndex(vm, i, immutableButterfly->get(i));
+        immutableButterfly = newButterfly;
+        CodeBlock* codeBlock = exec->codeBlock();
+
+        // FIXME: This is kinda gross and only works because we can't inline new_array_bufffer in the baseline.
+        // We also cannot allocate a new butterfly from compilation threads since it's invalid to allocate cells from
+        // a compilation thread.
+        WTF::storeStoreFence();
+        codeBlock->constantRegister(newArrayBuffer->immutableButterfly()).set(vm, codeBlock, immutableButterfly);
+        WTF::storeStoreFence();
+    }
+
+    JSArray* result = CommonSlowPaths::allocateNewArrayBuffer(vm, structure, immutableButterfly);
+    ASSERT(isCopyOnWrite(result->indexingMode()) || exec->lexicalGlobalObject()->isHavingABadTime());
+    RETURN(result);
 }
 
 SLOW_PATH_DECL(slow_path_spread)
index 406cda3..e7e36a6 100644 (file)
@@ -30,6 +30,7 @@
 #include "DirectArguments.h"
 #include "ExceptionHelpers.h"
 #include "FunctionCodeBlock.h"
+#include "JSImmutableButterfly.h"
 #include "ScopedArguments.h"
 #include "SlowPathReturnType.h"
 #include "StackAlignment.h"
@@ -251,6 +252,29 @@ static ALWAYS_INLINE void putDirectAccessorWithReify(VM& vm, ExecState* exec, JS
     baseObject->putDirectAccessor(exec, propertyName, accessor, attribute);
 }
 
+inline JSArray* allocateNewArrayBuffer(VM& vm, Structure* structure, JSImmutableButterfly* immutableButterfly)
+{
+    JSGlobalObject* globalObject = structure->globalObject();
+    Structure* originalStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
+    ASSERT(originalStructure->indexingMode() == immutableButterfly->indexingMode());
+    ASSERT(isCopyOnWrite(immutableButterfly->indexingMode()));
+    ASSERT(!structure->outOfLineCapacity());
+
+    JSArray* result = JSArray::createWithButterfly(vm, nullptr, originalStructure, immutableButterfly->toButterfly());
+    // FIXME: This works but it's slow. If we cared enough about the perf when having a bad time then we could fix it.
+    if (UNLIKELY(originalStructure != structure)) {
+        ASSERT(hasSlowPutArrayStorage(structure->indexingMode()));
+        ASSERT(globalObject->isHavingABadTime());
+
+        result->switchToSlowPutArrayStorage(vm);
+        ASSERT(result->butterfly() != immutableButterfly->toButterfly());
+        ASSERT(!result->butterfly()->arrayStorage()->m_sparseMap.get());
+        ASSERT(result->structureID() == structure->id());
+    }
+
+    return result;
+}
+
 } // namespace CommonSlowPaths
 
 class ExecState;
index 8b5ab3c..dfecefc 100644 (file)
@@ -47,10 +47,12 @@ IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType indexingType, Spe
     case ALL_INT32_INDEXING_TYPES:
         if (isInt32Speculation(type))
             return (indexingType & ~IndexingShapeMask) | Int32Shape;
+        // FIXME: Should this really say that it wants a double for NaNs.
         if (isFullNumberSpeculation(type))
             return (indexingType & ~IndexingShapeMask) | DoubleShape;
         return (indexingType & ~IndexingShapeMask) | ContiguousShape;
     case ALL_DOUBLE_INDEXING_TYPES:
+        // FIXME: Should this really say that it wants a double for NaNs.
         if (isFullNumberSpeculation(type))
             return indexingType;
         return (indexingType & ~IndexingShapeMask) | ContiguousShape;
@@ -65,7 +67,7 @@ IndexingType leastUpperBoundOfIndexingTypeAndType(IndexingType indexingType, Spe
 
 IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType indexingType, JSValue value)
 {
-    return leastUpperBoundOfIndexingTypeAndType(indexingType, speculationFromValue(value));
+    return leastUpperBoundOfIndexingTypes(indexingType, indexingTypeForValue(value) | (indexingType & IsArray));
 }
 
 void dumpIndexingType(PrintStream& out, IndexingType indexingType)
@@ -111,6 +113,15 @@ void dumpIndexingType(PrintStream& out, IndexingType indexingType)
     case ArrayWithSlowPutArrayStorage:
         basicName = "ArrayWithSlowPutArrayStorage";
         break;
+    case CopyOnWriteArrayWithInt32:
+        basicName = "CopyOnWriteArrayWithInt32";
+        break;
+    case CopyOnWriteArrayWithDouble:
+        basicName = "CopyOnWriteArrayWithDouble";
+        break;
+    case CopyOnWriteArrayWithContiguous:
+        basicName = "CopyOnWriteArrayWithContiguous";
+        break;
     default:
         basicName = "Unknown!";
         break;
index 689fae2..2ad4ee6 100644 (file)
@@ -34,17 +34,27 @@ namespace JSC {
 /*
     Structure of the IndexingType
     =============================
-    Conceptually, the IndexingType looks like this:
-
-    struct IndexingType {
-        uint8_t isArray:1;                    // bit 0
-        uint8_t shape:4;                      // bit 1 - 3
-        uint8_t mayHaveIndexedAccessors:1;    // bit 4
+    Conceptually, the IndexingTypeAndMisc looks like this:
+
+    struct IndexingTypeAndMisc {
+        struct IndexingModeIncludingHistory {
+            struct IndexingMode {
+                struct IndexingType {
+                    uint8_t isArray:1;          // bit 0
+                    uint8_t shape:3;            // bit 1 - 3
+                };
+                uint8_t copyOnWrite:1;          // bit 4
+            };
+            uint8_t mayHaveIndexedAccessors:1;  // bit 5
+        };
+        uint8_t cellLockBits:2;                 // bit 6 - 7
     };
 
     The shape values (e.g. Int32Shape, ContiguousShape, etc) are an enumeration of
     various shapes (though not necessarily sequential in terms of their values).
     Hence, shape values are not bitwise exclusive with respect to each other.
+
+    It's also common to refer to shape + copyOnWrite as IndexingShapeWithWritability.
 */
 
 typedef uint8_t IndexingType;
@@ -53,7 +63,6 @@ typedef uint8_t IndexingType;
 static const IndexingType IsArray                  = 0x01;
 
 // The shape of the indexed property storage.
-static const IndexingType IndexingShapeMask               = 0x0E;
 static const IndexingType NoIndexingShape                 = 0x00;
 static const IndexingType UndecidedShape                  = 0x02; // Only useful for arrays.
 static const IndexingType Int32Shape                      = 0x04;
@@ -62,17 +71,25 @@ static const IndexingType ContiguousShape                 = 0x08;
 static const IndexingType ArrayStorageShape               = 0x0A;
 static const IndexingType SlowPutArrayStorageShape        = 0x0C;
 
+static const IndexingType IndexingShapeMask               = 0x0E;
 static const IndexingType IndexingShapeShift              = 1;
 static const IndexingType NumberOfIndexingShapes          = 7;
+static const IndexingType IndexingTypeMask                = IndexingShapeMask | IsArray;
+
+// Whether or not the butterfly is copy on write. If it is copy on write then the butterfly is actually a JSImmutableButterfly. This should only ever be set if there are no named properties.
+static const IndexingType CopyOnWrite                      = 0x10;
+static const IndexingType IndexingShapeAndWritabilityMask  = CopyOnWrite | IndexingShapeMask;
+static const IndexingType NumberOfCopyOnWriteIndexingModes = 3; // We only have copy on write for int32, double, and contiguous shapes.
+static const IndexingType NumberOfArrayIndexingModes       = NumberOfIndexingShapes + NumberOfCopyOnWriteIndexingModes;
 
 // Additional flags for tracking the history of the type. These are usually
 // masked off unless you ask for them directly.
-static const IndexingType MayHaveIndexedAccessors         = 0x10;
+static const IndexingType MayHaveIndexedAccessors         = 0x20;
 
 // The IndexingType field of JSCells is stolen for locks and remembering if the object has been a
 // prototype.
-static const IndexingType IndexingTypeLockIsHeld          = 0x20;
-static const IndexingType IndexingTypeLockHasParked       = 0x40;
+static const IndexingType IndexingTypeLockIsHeld          = 0x40;
+static const IndexingType IndexingTypeLockHasParked       = 0x80;
 
 // List of acceptable array types.
 static const IndexingType NonArray                        = 0x0;
@@ -88,6 +105,9 @@ static const IndexingType ArrayWithDouble                 = IsArray | DoubleShap
 static const IndexingType ArrayWithContiguous             = IsArray | ContiguousShape;
 static const IndexingType ArrayWithArrayStorage           = IsArray | ArrayStorageShape;
 static const IndexingType ArrayWithSlowPutArrayStorage    = IsArray | SlowPutArrayStorageShape;
+static const IndexingType CopyOnWriteArrayWithInt32       = IsArray | Int32Shape | CopyOnWrite;
+static const IndexingType CopyOnWriteArrayWithDouble      = IsArray | DoubleShape | CopyOnWrite;
+static const IndexingType CopyOnWriteArrayWithContiguous  = IsArray | ContiguousShape | CopyOnWrite;
 
 #define ALL_BLANK_INDEXING_TYPES \
     NonArray:                    \
@@ -96,17 +116,29 @@ static const IndexingType ArrayWithSlowPutArrayStorage    = IsArray | SlowPutArr
 #define ALL_UNDECIDED_INDEXING_TYPES \
     ArrayWithUndecided
 
-#define ALL_INT32_INDEXING_TYPES      \
-    NonArrayWithInt32:                \
+#define ALL_WRITABLE_INT32_INDEXING_TYPES      \
+    NonArrayWithInt32:                         \
     case ArrayWithInt32
 
-#define ALL_DOUBLE_INDEXING_TYPES     \
-    NonArrayWithDouble:               \
-    case ArrayWithDouble
+#define ALL_INT32_INDEXING_TYPES        \
+    ALL_WRITABLE_INT32_INDEXING_TYPES:  \
+    case CopyOnWriteArrayWithInt32
+
+#define ALL_WRITABLE_DOUBLE_INDEXING_TYPES     \
+    NonArrayWithDouble:                        \
+    case ArrayWithDouble                       \
+
+#define ALL_DOUBLE_INDEXING_TYPES       \
+    ALL_WRITABLE_DOUBLE_INDEXING_TYPES: \
+    case CopyOnWriteArrayWithDouble
+
+#define ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES    \
+    NonArrayWithContiguous:                       \
+    case ArrayWithContiguous                      \
 
-#define ALL_CONTIGUOUS_INDEXING_TYPES \
-    NonArrayWithContiguous:           \
-    case ArrayWithContiguous
+#define ALL_CONTIGUOUS_INDEXING_TYPES        \
+    ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:  \
+    case CopyOnWriteArrayWithContiguous
 
 #define ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES \
     ArrayWithArrayStorage:                      \
@@ -117,51 +149,63 @@ static const IndexingType ArrayWithSlowPutArrayStorage    = IsArray | SlowPutArr
     case NonArrayWithSlowPutArrayStorage:               \
     case ARRAY_WITH_ARRAY_STORAGE_INDEXING_TYPES
 
-static inline bool hasIndexedProperties(IndexingType indexingType)
+inline bool hasIndexedProperties(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) != NoIndexingShape;
 }
 
-static inline bool hasUndecided(IndexingType indexingType)
+inline bool hasUndecided(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == UndecidedShape;
 }
 
-static inline bool hasInt32(IndexingType indexingType)
+inline bool hasInt32(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == Int32Shape;
 }
 
-static inline bool hasDouble(IndexingType indexingType)
+inline bool hasDouble(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == DoubleShape;
 }
 
-static inline bool hasContiguous(IndexingType indexingType)
+inline bool hasContiguous(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == ContiguousShape;
 }
 
-static inline bool hasArrayStorage(IndexingType indexingType)
+inline bool hasArrayStorage(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == ArrayStorageShape;
 }
 
-static inline bool hasAnyArrayStorage(IndexingType indexingType)
+inline bool hasAnyArrayStorage(IndexingType indexingType)
 {
     return static_cast<uint8_t>(indexingType & IndexingShapeMask) >= ArrayStorageShape;
 }
 
-static inline bool hasSlowPutArrayStorage(IndexingType indexingType)
+inline bool hasSlowPutArrayStorage(IndexingType indexingType)
 {
     return (indexingType & IndexingShapeMask) == SlowPutArrayStorageShape;
 }
 
-static inline bool shouldUseSlowPut(IndexingType indexingType)
+inline bool shouldUseSlowPut(IndexingType indexingType)
 {
     return hasSlowPutArrayStorage(indexingType);
 }
 
+inline constexpr bool isCopyOnWrite(IndexingType indexingMode)
+{
+    return indexingMode & CopyOnWrite;
+}
+
+inline unsigned arrayIndexFromIndexingType(IndexingType indexingType)
+{
+    if (isCopyOnWrite(indexingType))
+        return ((indexingType & IndexingShapeMask) - UndecidedShape + SlowPutArrayStorageShape) >> IndexingShapeShift;
+    return (indexingType & IndexingShapeMask) >> IndexingShapeShift;
+}
+
 inline IndexingType indexingTypeForValue(JSValue value)
 {
     if (value.isInt32())
@@ -182,10 +226,9 @@ IndexingType leastUpperBoundOfIndexingTypeAndValue(IndexingType, JSValue);
 void dumpIndexingType(PrintStream&, IndexingType);
 MAKE_PRINT_ADAPTOR(IndexingTypeDump, IndexingType, dumpIndexingType);
 
-// Mask of all possible types.
-static const IndexingType AllArrayTypes            = IndexingShapeMask | IsArray;
-
-// Mask of all possible types including the history.
+static const IndexingType AllWritableArrayTypes    = IndexingShapeMask | IsArray;
+static const IndexingType AllArrayTypes            = AllWritableArrayTypes | CopyOnWrite;
+static const IndexingType AllWritableArrayTypesAndHistory = AllWritableArrayTypes | MayHaveIndexedAccessors;
 static const IndexingType AllArrayTypesAndHistory  = AllArrayTypes | MayHaveIndexedAccessors;
 
 typedef LockAlgorithm<IndexingType, IndexingTypeLockIsHeld, IndexingTypeLockHasParked> IndexingTypeLockAlgorithm;
index 6fd86e7..ba1819f 100644 (file)
@@ -90,10 +90,10 @@ JSArray* JSArray::tryCreateUninitializedRestricted(ObjectInitializationScope& sc
         unsigned i = (createUninitialized ? initialLength : 0);
         if (hasDouble(indexingType)) {
             for (; i < vectorLength; ++i)
-                butterfly->contiguousDouble().at(i) = PNaN;
+                butterfly->contiguousDouble().atUnsafe(i) = PNaN;
         } else {
             for (; i < vectorLength; ++i)
-                butterfly->contiguous().at(i).clear();
+                butterfly->contiguous().atUnsafe(i).clear();
         }
     } else {
         static const unsigned indexBias = 0;
@@ -273,6 +273,9 @@ bool JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSVa
         return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
     }
 
+    if (isCopyOnWrite(thisObject->indexingMode()))
+        thisObject->convertFromCopyOnWrite(vm);
+
     if (propertyName == vm.propertyNames->length) {
         if (!thisObject->isLengthWritable())
             return false;
@@ -544,10 +547,10 @@ bool JSArray::appendMemcpy(ExecState* exec, VM& vm, unsigned startIndex, JSC::JS
         auto* butterfly = this->butterfly();
         if (type == ArrayWithDouble) {
             for (unsigned i = startIndex; i < newLength; ++i)
-                butterfly->contiguousDouble().at(i) = PNaN;
+                butterfly->contiguousDouble().at(this, i) = PNaN;
         } else {
             for (unsigned i = startIndex; i < newLength; ++i)
-                butterfly->contiguousInt32().at(i).setWithoutWriteBarrier(JSValue());
+                butterfly->contiguousInt32().at(this, i).setWithoutWriteBarrier(JSValue());
         }
     } else if (type == ArrayWithDouble)
         memcpy(butterfly()->contiguousDouble().data() + startIndex, otherArray->butterfly()->contiguousDouble().data(), sizeof(JSValue) * otherLength);
@@ -577,7 +580,7 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
         }
         createInitialUndecided(vm, newLength);
         return true;
-        
+
     case ArrayWithUndecided:
     case ArrayWithInt32:
     case ArrayWithDouble:
@@ -609,10 +612,10 @@ bool JSArray::setLength(ExecState* exec, unsigned newLength, bool throwException
 
         if (indexingType() == ArrayWithDouble) {
             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
-                butterfly->contiguousDouble().at(i) = PNaN;
+                butterfly->contiguousDouble().at(this, i) = PNaN;
         } else {
             for (unsigned i = butterfly->publicLength(); i-- > newLength;)
-                butterfly->contiguous().at(i).clear();
+                butterfly->contiguous().at(this, i).clear();
         }
         butterfly->setPublicLength(newLength);
         return true;
@@ -634,8 +637,11 @@ JSValue JSArray::pop(ExecState* exec)
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     Butterfly* butterfly = this->butterfly();
-    
+
     switch (indexingType()) {
     case ArrayClass:
         return jsUndefined();
@@ -654,9 +660,9 @@ JSValue JSArray::pop(ExecState* exec)
             return jsUndefined();
         
         RELEASE_ASSERT(length < butterfly->vectorLength());
-        JSValue value = butterfly->contiguous().at(length).get();
+        JSValue value = butterfly->contiguous().at(this, length).get();
         if (value) {
-            butterfly->contiguous().at(length).clear();
+            butterfly->contiguous().at(this, length).clear();
             butterfly->setPublicLength(length);
             return value;
         }
@@ -670,9 +676,9 @@ JSValue JSArray::pop(ExecState* exec)
             return jsUndefined();
         
         RELEASE_ASSERT(length < butterfly->vectorLength());
-        double value = butterfly->contiguousDouble().at(length);
+        double value = butterfly->contiguousDouble().at(this, length);
         if (value == value) {
-            butterfly->contiguousDouble().at(length) = PNaN;
+            butterfly->contiguousDouble().at(this, length) = PNaN;
             butterfly->setPublicLength(length);
             return JSValue(JSValue::EncodeAsDouble, value);
         }
@@ -738,12 +744,18 @@ NEVER_INLINE void JSArray::push(ExecState* exec, JSValue value)
 
 JSArray* JSArray::fastSlice(ExecState& exec, unsigned startIndex, unsigned count)
 {
-    auto arrayType = indexingType();
+    VM& vm = exec.vm();
+    auto arrayType = indexingMode();
     switch (arrayType) {
+    case CopyOnWriteArrayWithInt32:
+    case CopyOnWriteArrayWithDouble:
+    case CopyOnWriteArrayWithContiguous:
+        convertFromCopyOnWrite(vm);
+        arrayType = indexingMode();
+        FALLTHROUGH;
     case ArrayWithDouble:
     case ArrayWithInt32:
     case ArrayWithContiguous: {
-        VM& vm = exec.vm();
         if (count >= MIN_SPARSE_ARRAY_INDEX || structure(vm)->holesMustForwardToPrototype(vm, this))
             return nullptr;
 
@@ -885,6 +897,9 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startInde
     VM& vm = exec->vm();
     RELEASE_ASSERT(count > 0);
 
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     Butterfly* butterfly = this->butterfly();
     
     switch (indexingType()) {
@@ -912,12 +927,12 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startInde
         unsigned end = oldLength - count;
         if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
             for (unsigned i = startIndex; i < end; ++i) {
-                JSValue v = butterfly->contiguous().at(i + count).get();
+                JSValue v = butterfly->contiguous().at(this, i + count).get();
                 if (UNLIKELY(!v)) {
                     startIndex = i;
                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
                 }
-                butterfly->contiguous().at(i).setWithoutWriteBarrier(v);
+                butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
             }
         } else {
             memmove(butterfly->contiguous().data() + startIndex, 
@@ -926,7 +941,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startInde
         }
 
         for (unsigned i = end; i < oldLength; ++i)
-            butterfly->contiguous().at(i).clear();
+            butterfly->contiguous().at(this, i).clear();
         
         butterfly->setPublicLength(oldLength - count);
 
@@ -953,12 +968,12 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startInde
         unsigned end = oldLength - count;
         if (this->structure(vm)->holesMustForwardToPrototype(vm, this)) {
             for (unsigned i = startIndex; i < end; ++i) {
-                double v = butterfly->contiguousDouble().at(i + count);
+                double v = butterfly->contiguousDouble().at(this, i + count);
                 if (UNLIKELY(v != v)) {
                     startIndex = i;
                     return shiftCountWithArrayStorage(vm, startIndex, count, ensureArrayStorage(vm));
                 }
-                butterfly->contiguousDouble().at(i) = v;
+                butterfly->contiguousDouble().at(this, i) = v;
             }
         } else {
             memmove(butterfly->contiguousDouble().data() + startIndex,
@@ -966,7 +981,7 @@ bool JSArray::shiftCountWithAnyIndexingType(ExecState* exec, unsigned& startInde
                 sizeof(JSValue) * (end - startIndex));
         }
         for (unsigned i = end; i < oldLength; ++i)
-            butterfly->contiguousDouble().at(i) = PNaN;
+            butterfly->contiguousDouble().at(this, i) = PNaN;
         
         butterfly->setPublicLength(oldLength - count);
         return true;
@@ -1041,6 +1056,9 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     Butterfly* butterfly = this->butterfly();
     
     switch (indexingType()) {
@@ -1078,7 +1096,7 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
-            JSValue v = butterfly->contiguous().at(i).get();
+            JSValue v = butterfly->contiguous().at(this, i).get();
             if (UNLIKELY(!v)) {
                 scope.release();
                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
@@ -1086,9 +1104,9 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         }
 
         for (unsigned i = oldLength; i-- > startIndex;) {
-            JSValue v = butterfly->contiguous().at(i).get();
+            JSValue v = butterfly->contiguous().at(this, i).get();
             ASSERT(v);
-            butterfly->contiguous().at(i + count).setWithoutWriteBarrier(v);
+            butterfly->contiguous().at(this, i + count).setWithoutWriteBarrier(v);
         }
         
         // Our memmoving of values around in the array could have concealed some of them from
@@ -1131,7 +1149,7 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         // We have to check for holes before we start moving things around so that we don't get halfway 
         // through shifting and then realize we should have been in ArrayStorage mode.
         for (unsigned i = oldLength; i-- > startIndex;) {
-            double v = butterfly->contiguousDouble().at(i);
+            double v = butterfly->contiguousDouble().at(this, i);
             if (UNLIKELY(v != v)) {
                 scope.release();
                 return unshiftCountWithArrayStorage(exec, startIndex, count, ensureArrayStorage(vm));
@@ -1139,9 +1157,9 @@ bool JSArray::unshiftCountWithAnyIndexingType(ExecState* exec, unsigned startInd
         }
 
         for (unsigned i = oldLength; i-- > startIndex;) {
-            double v = butterfly->contiguousDouble().at(i);
+            double v = butterfly->contiguousDouble().at(this, i);
             ASSERT(v == v);
-            butterfly->contiguousDouble().at(i + count) = v;
+            butterfly->contiguousDouble().at(this, i + count) = v;
         }
         
         // NOTE: we're leaving being garbage in the part of the array that we shifted out
@@ -1192,7 +1210,7 @@ void JSArray::fillArgList(ExecState* exec, MarkedArgumentBuffer& args)
         vector = 0;
         vectorEnd = 0;
         for (; i < butterfly->publicLength(); ++i) {
-            double v = butterfly->contiguousDouble().at(i);
+            double v = butterfly->contiguousDouble().at(this, i);
             if (v != v)
                 break;
             args.append(JSValue(JSValue::EncodeAsDouble, v));
@@ -1265,7 +1283,7 @@ void JSArray::copyToArguments(ExecState* exec, VirtualRegister firstElementDest,
         vectorEnd = 0;
         for (; i < butterfly->publicLength(); ++i) {
             ASSERT(i < butterfly->vectorLength());
-            double v = butterfly->contiguousDouble().at(i);
+            double v = butterfly->contiguousDouble().at(this, i);
             if (v != v)
                 break;
             exec->r(firstElementDest + i - offset) = JSValue(JSValue::EncodeAsDouble, v);
index 032579e..cfe73ba 100644 (file)
@@ -83,14 +83,14 @@ ALWAYS_INLINE double toLength(ExecState* exec, JSObject* obj)
     return lengthValue.toLength(exec);
 }
 
-ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
+inline void JSArray::pushInline(ExecState* exec, JSValue value)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     Butterfly* butterfly = this->butterfly();
 
-    switch (indexingType()) {
+    switch (indexingMode()) {
     case ArrayClass: {
         createInitialUndecided(vm, 0);
         FALLTHROUGH;
@@ -114,7 +114,7 @@ ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
         unsigned length = butterfly->publicLength();
         ASSERT(length <= butterfly->vectorLength());
         if (length < butterfly->vectorLength()) {
-            butterfly->contiguousInt32().at(length).setWithoutWriteBarrier(value);
+            butterfly->contiguousInt32().at(this, length).setWithoutWriteBarrier(value);
             butterfly->setPublicLength(length + 1);
             return;
         }
@@ -135,7 +135,7 @@ ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
         unsigned length = butterfly->publicLength();
         ASSERT(length <= butterfly->vectorLength());
         if (length < butterfly->vectorLength()) {
-            butterfly->contiguous().at(length).set(vm, this, value);
+            butterfly->contiguous().at(this, length).set(vm, this, value);
             butterfly->setPublicLength(length + 1);
             return;
         }
@@ -170,7 +170,7 @@ ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
         unsigned length = butterfly->publicLength();
         ASSERT(length <= butterfly->vectorLength());
         if (length < butterfly->vectorLength()) {
-            butterfly->contiguousDouble().at(length) = valueAsDouble;
+            butterfly->contiguousDouble().at(this, length) = valueAsDouble;
             butterfly->setPublicLength(length + 1);
             return;
         }
@@ -227,8 +227,13 @@ ALWAYS_INLINE void JSArray::pushInline(ExecState* exec, JSValue value)
         return;
     }
 
-    default:
-        RELEASE_ASSERT_NOT_REACHED();
+    default: {
+        RELEASE_ASSERT(isCopyOnWrite(indexingMode()));
+        convertFromCopyOnWrite(vm);
+        scope.release();
+        // Reloop.
+        return pushInline(exec, value);
+    }
     }
 }
 
index 8808cb5..7c1f019 100644 (file)
@@ -126,6 +126,7 @@ public:
     
     JSType type() const;
     IndexingType indexingTypeAndMisc() const;
+    IndexingType indexingMode() const;
     IndexingType indexingType() const;
     StructureID structureID() const { return m_structureID; }
     Structure* structure() const;
index 6523694..e1839b9 100644 (file)
@@ -48,7 +48,7 @@ inline JSCell::JSCell(CreatingEarlyCellTag)
 
 inline JSCell::JSCell(VM&, Structure* structure)
     : m_structureID(structure->id())
-    , m_indexingTypeAndMisc(structure->indexingTypeIncludingHistory())
+    , m_indexingTypeAndMisc(structure->indexingModeIncludingHistory())
     , m_type(structure->typeInfo().type())
     , m_flags(structure->typeInfo().inlineTypeFlags())
     , m_cellState(CellState::DefinitelyWhite)
@@ -78,7 +78,7 @@ inline void JSCell::finishCreation(VM& vm, Structure* structure, CreatingEarlyCe
     if (structure) {
 #endif
         m_structureID = structure->id();
-        m_indexingTypeAndMisc = structure->indexingTypeIncludingHistory();
+        m_indexingTypeAndMisc = structure->indexingModeIncludingHistory();
         m_type = structure->typeInfo().type();
         m_flags = structure->typeInfo().inlineTypeFlags();
 #if ENABLE(GC_VALIDATION)
@@ -102,6 +102,11 @@ inline IndexingType JSCell::indexingTypeAndMisc() const
 
 inline IndexingType JSCell::indexingType() const
 {
+    return indexingTypeAndMisc() & AllWritableArrayTypes;
+}
+
+inline IndexingType JSCell::indexingMode() const
+{
     return indexingTypeAndMisc() & AllArrayTypes;
 }
 
@@ -251,12 +256,12 @@ ALWAYS_INLINE void JSCell::setStructure(VM& vm, Structure* structure)
     m_structureID = structure->id();
     m_flags = TypeInfo::mergeInlineTypeFlags(structure->typeInfo().inlineTypeFlags(), m_flags);
     m_type = structure->typeInfo().type();
-    IndexingType newIndexingType = structure->indexingTypeIncludingHistory();
+    IndexingType newIndexingType = structure->indexingModeIncludingHistory();
     if (m_indexingTypeAndMisc != newIndexingType) {
         ASSERT(!(newIndexingType & ~AllArrayTypesAndHistory));
         for (;;) {
             IndexingType oldValue = m_indexingTypeAndMisc;
-            IndexingType newValue = (oldValue & ~AllArrayTypesAndHistory) | structure->indexingTypeIncludingHistory();
+            IndexingType newValue = (oldValue & ~AllArrayTypesAndHistory) | structure->indexingModeIncludingHistory();
             if (WTF::atomicCompareExchangeWeakRelaxed(&m_indexingTypeAndMisc, oldValue, newValue))
                 break;
         }
index c36f856..1586133 100644 (file)
@@ -81,7 +81,7 @@ public:
 
         if (indexingType == ContiguousShape || indexingType == Int32Shape) {
             for (unsigned i = 0; i < length; i++) {
-                JSValue value = array->butterfly()->contiguous().at(i).get();
+                JSValue value = array->butterfly()->contiguous().at(array, i).get();
                 value = !!value ? value : jsUndefined();
                 result->buffer()[i].set(vm, result, value);
             }
@@ -90,7 +90,7 @@ public:
 
         if (indexingType == DoubleShape) {
             for (unsigned i = 0; i < length; i++) {
-                double d = array->butterfly()->contiguousDouble().at(i);
+                double d = array->butterfly()->contiguousDouble().at(array, i);
                 JSValue value = std::isnan(d) ? jsUndefined() : JSValue(JSValue::EncodeAsDouble, d);
                 result->buffer()[i].set(vm, result, value);
             }
index 69b537e..75a252c 100644 (file)
@@ -585,13 +585,16 @@ void JSGlobalObject::init(VM& vm)
 #endif
     m_arrayPrototype.set(vm, this, ArrayPrototype::create(vm, this, ArrayPrototype::createStructure(vm, this, m_objectPrototype.get())));
     
-    m_originalArrayStructureForIndexingShape[UndecidedShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithUndecided));
-    m_originalArrayStructureForIndexingShape[Int32Shape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithInt32));
-    m_originalArrayStructureForIndexingShape[DoubleShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithDouble));
-    m_originalArrayStructureForIndexingShape[ContiguousShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithContiguous));
-    m_originalArrayStructureForIndexingShape[ArrayStorageShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithArrayStorage));
-    m_originalArrayStructureForIndexingShape[SlowPutArrayStorageShape >> IndexingShapeShift].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage));
-    for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(UndecidedShape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithUndecided));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(Int32Shape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithInt32));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(DoubleShape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithDouble));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(ContiguousShape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithContiguous));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(ArrayStorageShape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithArrayStorage));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(SlowPutArrayStorageShape)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), ArrayWithSlowPutArrayStorage));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(CopyOnWriteArrayWithInt32)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), CopyOnWriteArrayWithInt32));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(CopyOnWriteArrayWithDouble)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), CopyOnWriteArrayWithDouble));
+    m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous)].set(vm, this, JSArray::createStructure(vm, this, m_arrayPrototype.get(), CopyOnWriteArrayWithContiguous));
+    for (unsigned i = 0; i < NumberOfArrayIndexingModes; ++i)
         m_arrayStructureForIndexingShapeDuringAllocation[i] = m_originalArrayStructureForIndexingShape[i];
 
     m_regExpPrototype.set(vm, this, RegExpPrototype::create(vm, this, RegExpPrototype::createStructure(vm, this, m_objectPrototype.get())));
@@ -1280,7 +1283,7 @@ void JSGlobalObject::haveABadTime(VM& vm)
     
     // Make sure that all JSArray allocations that load the appropriate structure from
     // this object now load a structure that uses SlowPut.
-    for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
+    for (unsigned i = 0; i < NumberOfArrayIndexingModes; ++i)
         m_arrayStructureForIndexingShapeDuringAllocation[i].set(vm, this, originalArrayStructureForIndexingType(ArrayWithSlowPutArrayStorage));
 
     // Same for any special array structures.
@@ -1390,9 +1393,9 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(thisObject->m_scopedArgumentsStructure);
     visitor.append(thisObject->m_clonedArgumentsStructure);
     visitor.append(thisObject->m_objectStructureForObjectConstructor);
-    for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
+    for (unsigned i = 0; i < NumberOfArrayIndexingModes; ++i)
         visitor.append(thisObject->m_originalArrayStructureForIndexingShape[i]);
-    for (unsigned i = 0; i < NumberOfIndexingShapes; ++i)
+    for (unsigned i = 0; i < NumberOfArrayIndexingModes; ++i)
         visitor.append(thisObject->m_arrayStructureForIndexingShapeDuringAllocation[i]);
     thisObject->m_callbackConstructorStructure.visit(visitor);
     thisObject->m_callbackFunctionStructure.visit(visitor);
index 353733e..69b6c8f 100644 (file)
@@ -322,10 +322,10 @@ public:
     WriteBarrier<Structure> m_objectStructureForObjectConstructor;
         
     // Lists the actual structures used for having these particular indexing shapes.
-    WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfIndexingShapes];
+    WriteBarrier<Structure> m_originalArrayStructureForIndexingShape[NumberOfArrayIndexingModes];
     // Lists the structures we should use during allocation for these particular indexing shapes.
     // These structures will differ from the originals list above when we are having a bad time.
-    WriteBarrier<Structure> m_arrayStructureForIndexingShapeDuringAllocation[NumberOfIndexingShapes];
+    WriteBarrier<Structure> m_arrayStructureForIndexingShapeDuringAllocation[NumberOfArrayIndexingModes];
 
     LazyProperty<JSGlobalObject, Structure> m_callbackConstructorStructure;
     LazyProperty<JSGlobalObject, Structure> m_callbackFunctionStructure;
@@ -620,12 +620,12 @@ public:
     Structure* originalArrayStructureForIndexingType(IndexingType indexingType) const
     {
         ASSERT(indexingType & IsArray);
-        return m_originalArrayStructureForIndexingShape[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get();
+        return m_originalArrayStructureForIndexingShape[arrayIndexFromIndexingType(indexingType)].get();
     }
     Structure* arrayStructureForIndexingTypeDuringAllocation(IndexingType indexingType) const
     {
         ASSERT(indexingType & IsArray);
-        return m_arrayStructureForIndexingShapeDuringAllocation[(indexingType & IndexingShapeMask) >> IndexingShapeShift].get();
+        return m_arrayStructureForIndexingShapeDuringAllocation[arrayIndexFromIndexingType(indexingType)].get();
     }
     Structure* arrayStructureForIndexingTypeDuringAllocation(ExecState* exec, IndexingType indexingType, JSValue newTarget) const
     {
@@ -638,7 +638,7 @@ public:
         
     bool isOriginalArrayStructure(Structure* structure)
     {
-        return originalArrayStructureForIndexingType(structure->indexingType() | IsArray) == structure;
+        return originalArrayStructureForIndexingType(structure->indexingMode() | IsArray) == structure;
     }
         
     Structure* booleanObjectStructure() const { return m_booleanObjectStructure.get(); }
diff --git a/Source/JavaScriptCore/runtime/JSImmutableButterfly.cpp b/Source/JavaScriptCore/runtime/JSImmutableButterfly.cpp
new file mode 100644 (file)
index 0000000..6b0c9e1
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSImmutableButterfly.h"
+
+namespace JSC {
+
+const ClassInfo JSImmutableButterfly::s_info = { "Immutable Butterfly", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSImmutableButterfly) };
+
+void JSImmutableButterfly::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    Base::visitChildren(cell, visitor);
+    if (!hasContiguous(cell->indexingType())) {
+        ASSERT(hasDouble(cell->indexingType()) || hasInt32(cell->indexingType()));
+        return;
+    }
+
+    Butterfly* butterfly = jsCast<JSImmutableButterfly*>(cell)->toButterfly();
+    visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
+}
+
+void JSImmutableButterfly::copyToArguments(ExecState* exec, VirtualRegister firstElementDest, unsigned offset, unsigned length)
+{
+    for (unsigned i = 0; i < length; ++i) {
+        if ((i + offset) < publicLength())
+            exec->r(firstElementDest + i) = get(i + offset);
+        else
+            exec->r(firstElementDest + i) = jsUndefined();
+    }
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSImmutableButterfly.h b/Source/JavaScriptCore/runtime/JSImmutableButterfly.h
new file mode 100644 (file)
index 0000000..04f5714
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "IndexingHeader.h"
+#include "JSCell.h"
+
+namespace JSC {
+
+class JSImmutableButterfly : public JSCell {
+    using Base = JSCell;
+
+public:
+    static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+
+    DECLARE_INFO;
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, IndexingType indexingType)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(JSImmutableButterflyType, StructureFlags), info(), indexingType);
+    }
+
+    ALWAYS_INLINE static JSImmutableButterfly* tryCreate(VM& vm, Structure* structure, unsigned size)
+    {
+        Checked<size_t, RecordOverflow> checkedAllocationSize = allocationSize(size);
+        if (UNLIKELY(checkedAllocationSize.hasOverflowed()))
+            return nullptr;
+
+        void* buffer = tryAllocateCell<JSImmutableButterfly>(vm.heap, checkedAllocationSize.unsafeGet());
+        if (UNLIKELY(!buffer))
+            return nullptr;
+        JSImmutableButterfly* result = new (NotNull, buffer) JSImmutableButterfly(vm, structure, size);
+        result->finishCreation(vm);
+        return result;
+    }
+
+    static JSImmutableButterfly* create(VM& vm, IndexingType indexingType, unsigned length)
+    {
+        auto* array = tryCreate(vm, vm.immutableButterflyStructures[arrayIndexFromIndexingType(indexingType) - NumberOfIndexingShapes].get(), length);
+        RELEASE_ASSERT(array);
+        return array;
+    }
+
+    unsigned publicLength() const { return m_header.publicLength(); }
+    unsigned vectorLength() const { return m_header.vectorLength(); }
+    unsigned length() const { return m_header.publicLength(); }
+
+    Butterfly* toButterfly() const { return reinterpret_cast<Butterfly*>(bitwise_cast<char*>(this) + sizeof(JSImmutableButterfly)); }
+    static JSImmutableButterfly* fromButterfly(Butterfly* butterfly) { return reinterpret_cast<JSImmutableButterfly*>(reinterpret_cast<char*>(butterfly) - sizeof(JSImmutableButterfly)); }
+
+    JSValue get(unsigned index) const
+    {
+        if (!hasDouble(indexingMode()))
+            return toButterfly()->contiguous().at(this, index).get();
+        double value = toButterfly()->contiguousDouble().at(this, index);
+        // Holes are not supported yet.
+        ASSERT(!std::isnan(value));
+        return jsNumber(value);
+    }
+
+    static void visitChildren(JSCell*, SlotVisitor&);
+
+    void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length);
+
+    template<typename>
+    static CompleteSubspace* subspaceFor(VM& vm)
+    {
+        // We allocate out of the JSValue gigacage as other code expects all butterflies to live there.
+        return &vm.jsValueGigacageAuxiliarySpace;
+    }
+
+    // Only call this if you just allocated this butterfly.
+    void setIndex(VM& vm, unsigned index, JSValue value)
+    {
+        if (hasDouble(indexingType()))
+            toButterfly()->contiguousDouble().atUnsafe(index) = value.asNumber();
+        else
+            toButterfly()->contiguous().atUnsafe(index).set(vm, this, value);
+    }
+
+private:
+
+    static Checked<size_t, RecordOverflow> allocationSize(Checked<size_t, RecordOverflow> numItems)
+    {
+        return sizeof(JSImmutableButterfly) + numItems * sizeof(WriteBarrier<Unknown>);
+    }
+
+    JSImmutableButterfly(VM& vm, Structure* structure, unsigned length)
+        : Base(vm, structure)
+    {
+        m_header.setVectorLength(length);
+        m_header.setPublicLength(length);
+    }
+
+    IndexingHeader m_header;
+};
+
+} // namespace JSC
index d84f993..0bf868b 100644 (file)
@@ -37,6 +37,7 @@
 #include "JSCustomGetterSetterFunction.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
+#include "JSImmutableButterfly.h"
 #include "Lookup.h"
 #include "NativeErrorConstructor.h"
 #include "Nodes.h"
@@ -97,7 +98,12 @@ ALWAYS_INLINE void JSObject::markAuxiliaryAndVisitOutOfLineProperties(SlotVisito
     
     if (!butterfly)
         return;
-    
+
+    if (isCopyOnWrite(structure->indexingMode())) {
+        visitor.append(bitwise_cast<WriteBarrier<JSCell>>(JSImmutableButterfly::fromButterfly(butterfly)));
+        return;
+    }
+
     bool hasIndexingHeader = structure->hasIndexingHeader(this);
     size_t preCapacity;
     if (hasIndexingHeader)
@@ -132,16 +138,11 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
     Butterfly* butterfly;
     Structure* structure;
     PropertyOffset lastOffset;
-    
-    if (visitor.mutatorIsStopped()) {
-        butterfly = this->butterfly();
-        structure = this->structure(vm);
-        lastOffset = structure->lastOffset();
-        
-        markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
-        
-        switch (structure->indexingType()) {
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
+
+    auto visitElements = [&] (IndexingType indexingMode) {
+        switch (indexingMode) {
+        // We don't need to visit the elements for CopyOnWrite butterflies since they we marked the JSImmutableButterfly acting as out butterfly.
+        case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
             visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
             break;
         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
@@ -152,6 +153,16 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         default:
             break;
         }
+    };
+
+    if (visitor.mutatorIsStopped()) {
+        butterfly = this->butterfly();
+        structure = this->structure(vm);
+        lastOffset = structure->lastOffset();
+        
+        markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
+        visitElements(structure->indexingMode());
+
         return structure;
     }
     
@@ -381,10 +392,10 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         return nullptr;
     structure = vm.getStructure(structureID);
     lastOffset = structure->lastOffset();
-    IndexingType indexingType = structure->indexingType();
-    Dependency indexingTypeDependency = Dependency::fence(indexingType);
+    IndexingType indexingMode = structure->indexingMode();
+    Dependency indexingModeDependency = Dependency::fence(indexingMode);
     Locker<JSCellLock> locker(NoLockingNecessary);
-    switch (indexingType) {
+    switch (indexingMode) {
     case ALL_CONTIGUOUS_INDEXING_TYPES:
     case ALL_ARRAY_STORAGE_INDEXING_TYPES:
         // We need to hold this lock to protect against changes to the innards of the butterfly
@@ -396,7 +407,7 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
     default:
         break;
     }
-    butterfly = indexingTypeDependency.consume(this)->butterfly();
+    butterfly = indexingModeDependency.consume(this)->butterfly();
     Dependency butterflyDependency = Dependency::fence(butterfly);
     if (!butterfly)
         return structure;
@@ -406,21 +417,8 @@ ALWAYS_INLINE Structure* JSObject::visitButterflyImpl(SlotVisitor& visitor)
         return nullptr;
     
     markAuxiliaryAndVisitOutOfLineProperties(visitor, butterfly, structure, lastOffset);
-    
-    ASSERT(indexingType == structure->indexingType());
-    
-    switch (indexingType) {
-    case ALL_CONTIGUOUS_INDEXING_TYPES:
-        visitor.appendValuesHidden(butterfly->contiguous().data(), butterfly->publicLength());
-        break;
-    case ALL_ARRAY_STORAGE_INDEXING_TYPES:
-        visitor.appendValuesHidden(butterfly->arrayStorage()->m_vector, butterfly->arrayStorage()->vectorLength());
-        if (butterfly->arrayStorage()->m_sparseMap)
-            visitor.append(butterfly->arrayStorage()->m_sparseMap);
-        break;
-    default:
-        break;
-    }
+    ASSERT(indexingMode == structure->indexingMode());
+    visitElements(indexingMode);
     
     return structure;
 }
@@ -589,7 +587,7 @@ bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec,
         if (i >= butterfly->vectorLength())
             return false;
         
-        JSValue value = butterfly->contiguous().at(i).get();
+        JSValue value = butterfly->contiguous().at(thisObject, i).get();
         if (value) {
             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), value);
             return true;
@@ -603,7 +601,7 @@ bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec,
         if (i >= butterfly->vectorLength())
             return false;
         
-        double value = butterfly->contiguousDouble().at(i);
+        double value = butterfly->contiguousDouble().at(thisObject, i);
         if (value == value) {
             slot.setValue(thisObject, static_cast<unsigned>(PropertyAttribute::None), JSValue(JSValue::EncodeAsDouble, value));
             return true;
@@ -833,12 +831,15 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
 {
     VM& vm = exec->vm();
     JSObject* thisObject = jsCast<JSObject*>(cell);
-    
+
     if (propertyName > MAX_ARRAY_INDEX) {
         PutPropertySlot slot(cell, shouldThrow);
         return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
     }
-    
+
+    if (isCopyOnWrite(thisObject->indexingMode()))
+        thisObject->convertFromCopyOnWrite(vm);
+
     switch (thisObject->indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
         break;
@@ -850,7 +851,7 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
     }
         
     case ALL_INT32_INDEXING_TYPES: {
-        if (!value.isInt32()) {
+        if (!value.isInt32() || isCopyOnWrite(thisObject->indexingMode())) {
             thisObject->convertInt32ForValue(vm, value);
             return putByIndex(cell, exec, propertyName, value, shouldThrow);
         }
@@ -861,7 +862,7 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
         Butterfly* butterfly = thisObject->butterfly();
         if (propertyName >= butterfly->vectorLength())
             break;
-        butterfly->contiguous().at(propertyName).set(vm, thisObject, value);
+        butterfly->contiguous().at(thisObject, propertyName).set(vm, thisObject, value);
         if (propertyName >= butterfly->publicLength())
             butterfly->setPublicLength(propertyName + 1);
         return true;
@@ -873,6 +874,7 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
             // Reloop.
             return putByIndex(cell, exec, propertyName, value, shouldThrow);
         }
+
         double valueAsDouble = value.asNumber();
         if (valueAsDouble != valueAsDouble) {
             thisObject->convertDoubleToContiguous(vm);
@@ -882,7 +884,7 @@ bool JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName,
         Butterfly* butterfly = thisObject->butterfly();
         if (propertyName >= butterfly->vectorLength())
             break;
-        butterfly->contiguousDouble().at(propertyName) = valueAsDouble;
+        butterfly->contiguousDouble().at(thisObject, propertyName) = valueAsDouble;
         if (propertyName >= butterfly->publicLength())
             butterfly->setPublicLength(propertyName + 1);
         return true;
@@ -1050,7 +1052,7 @@ ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length)
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguous().at(i).setWithoutWriteBarrier(JSValue());
+        newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateInt32);
@@ -1064,7 +1066,7 @@ ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length)
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguousDouble().at(i) = PNaN;
+        newButterfly->contiguousDouble().at(this, i) = PNaN;
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateDouble);
@@ -1078,7 +1080,7 @@ ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length)
     DeferGC deferGC(vm.heap);
     Butterfly* newButterfly = createInitialIndexedStorage(vm, length);
     for (unsigned i = newButterfly->vectorLength(); i--;)
-        newButterfly->contiguous().at(i).setWithoutWriteBarrier(JSValue());
+        newButterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
     StructureID oldStructureID = this->structureID();
     Structure* oldStructure = vm.getStructure(oldStructureID);
     Structure* newStructure = Structure::nonPropertyTransition(vm, oldStructure, NonPropertyTransition::AllocateContiguous);
@@ -1134,7 +1136,7 @@ ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm)
 
     Butterfly* butterfly = this->butterfly();
     for (unsigned i = butterfly->vectorLength(); i--;)
-        butterfly->contiguous().at(i).setWithoutWriteBarrier(JSValue());
+        butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
 
     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32));
     return m_butterfly->contiguousInt32();
@@ -1146,7 +1148,7 @@ ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm)
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;)
-        butterfly->contiguousDouble().at(i) = PNaN;
+        butterfly->contiguousDouble().at(this, i) = PNaN;
     
     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble));
     return m_butterfly->contiguousDouble();
@@ -1158,7 +1160,7 @@ ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm)
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;)
-        butterfly->contiguous().at(i).setWithoutWriteBarrier(JSValue());
+        butterfly->contiguous().at(this, i).setWithoutWriteBarrier(JSValue());
 
     WTF::storeStoreFence();
     setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous));
@@ -1217,10 +1219,11 @@ ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm)
 ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm)
 {
     ASSERT(hasInt32(indexingType()));
+    ASSERT(!isCopyOnWrite(indexingMode()));
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;) {
-        WriteBarrier<Unknown>* current = &butterfly->contiguous().at(i);
+        WriteBarrier<Unknown>* current = &butterfly->contiguous().atUnsafe(i);
         double* currentAsDouble = bitwise_cast<double*>(current);
         JSValue v = current->get();
         // NOTE: Since this may be used during initialization, v could be garbage. If it's garbage,
@@ -1253,7 +1256,7 @@ ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition
     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = 0; i < vectorLength; i++) {
-        JSValue v = butterfly->contiguous().at(i).get();
+        JSValue v = butterfly->contiguous().at(this, i).get();
         newStorage->m_vector[i].setWithoutWriteBarrier(v);
         if (v)
             newStorage->m_numValuesInVector++;
@@ -1275,10 +1278,11 @@ ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
 ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
 {
     ASSERT(hasDouble(indexingType()));
+    ASSERT(!isCopyOnWrite(indexingMode()));
 
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = butterfly->vectorLength(); i--;) {
-        double* current = &butterfly->contiguousDouble().at(i);
+        double* current = &butterfly->contiguousDouble().atUnsafe(i);
         WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(current);
         double value = *current;
         if (value != value) {
@@ -1303,7 +1307,7 @@ ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransitio
     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = 0; i < vectorLength; i++) {
-        double value = butterfly->contiguousDouble().at(i);
+        double value = butterfly->contiguousDouble().at(this, i);
         if (value != value) {
             newStorage->m_vector[i].clear();
             continue;
@@ -1334,7 +1338,7 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTrans
     ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
     Butterfly* butterfly = m_butterfly.get();
     for (unsigned i = 0; i < vectorLength; i++) {
-        JSValue v = butterfly->contiguous().at(i).get();
+        JSValue v = butterfly->contiguous().at(this, i).get();
         newStorage->m_vector[i].setWithoutWriteBarrier(v);
         if (v)
             newStorage->m_numValuesInVector++;
@@ -1394,19 +1398,19 @@ void JSObject::convertUndecidedForValue(VM& vm, JSValue value)
 void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value)
 {
     if (value.isInt32()) {
-        createInitialInt32(vm, index + 1).at(index).set(vm, this, value);
+        createInitialInt32(vm, index + 1).at(this, index).set(vm, this, value);
         return;
     }
     
     if (value.isDouble()) {
         double doubleValue = value.asNumber();
         if (doubleValue == doubleValue) {
-            createInitialDouble(vm, index + 1).at(index) = doubleValue;
+            createInitialDouble(vm, index + 1).at(this, index) = doubleValue;
             return;
         }
     }
     
-    createInitialContiguous(vm, index + 1).at(index).set(vm, this, value);
+    createInitialContiguous(vm, index + 1).at(this, index).set(vm, this, value);
 }
 
 void JSObject::convertInt32ForValue(VM& vm, JSValue value)
@@ -1417,10 +1421,43 @@ void JSObject::convertInt32ForValue(VM& vm, JSValue value)
         convertInt32ToDouble(vm);
         return;
     }
-    
+
     convertInt32ToContiguous(vm);
 }
 
+void JSObject::convertFromCopyOnWrite(VM& vm)
+{
+    ASSERT(isCopyOnWrite(indexingMode()));
+    ASSERT(structure()->indexingMode() == indexingMode());
+
+    const bool hasIndexingHeader = true;
+    Butterfly* oldButterfly = butterfly();
+    size_t propertyCapacity = 0;
+    unsigned newVectorLength = Butterfly::optimalContiguousVectorLength(propertyCapacity, std::min(oldButterfly->vectorLength() * 2, MAX_STORAGE_VECTOR_LENGTH));
+    Butterfly* newButterfly = Butterfly::createUninitialized(vm, this, 0, propertyCapacity, hasIndexingHeader, newVectorLength * sizeof(JSValue));
+
+    memcpy(newButterfly->propertyStorage(), oldButterfly->propertyStorage(), oldButterfly->vectorLength() * sizeof(JSValue) + sizeof(IndexingHeader));
+
+    WTF::storeStoreFence();
+    NonPropertyTransition transition = ([&] () {
+        switch (indexingType()) {
+        case ArrayWithInt32:
+            return NonPropertyTransition::AllocateInt32;
+        case ArrayWithDouble:
+            return NonPropertyTransition::AllocateDouble;
+        case ArrayWithContiguous:
+            return NonPropertyTransition::AllocateContiguous;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            return NonPropertyTransition::AllocateContiguous;
+        }
+    })();
+    StructureID oldStructureID = structureID();
+    Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
+    nukeStructureAndSetButterfly(vm, oldStructureID, newButterfly);
+    setStructure(vm, newStructure);
+}
+
 void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value)
 {
     ASSERT(index < m_butterfly->publicLength());
@@ -1443,10 +1480,15 @@ void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned
     setIndexQuickly(vm, index, value);
 }
 
-ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
+ContiguousJSValues JSObject::ensureWritableInt32Slow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode()) && hasInt32(indexingMode())) {
+        convertFromCopyOnWrite(vm);
+        return butterfly()->contiguousInt32();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousJSValues();
     
@@ -1470,10 +1512,16 @@ ContiguousJSValues JSObject::ensureInt32Slow(VM& vm)
     }
 }
 
-ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
+ContiguousDoubles JSObject::ensureWritableDoubleSlow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode())) {
+        convertFromCopyOnWrite(vm);
+        if (hasDouble(indexingMode()))
+            return butterfly()->contiguousDouble();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousDoubles();
     
@@ -1499,10 +1547,16 @@ ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm)
     }
 }
 
-ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
+ContiguousJSValues JSObject::ensureWritableContiguousSlow(VM& vm)
 {
     ASSERT(inherits(vm, info()));
-    
+
+    if (isCopyOnWrite(indexingMode())) {
+        convertFromCopyOnWrite(vm);
+        if (hasContiguous(indexingMode()))
+            return butterfly()->contiguous();
+    }
+
     if (structure(vm)->hijacksIndexingHeader())
         return ContiguousJSValues();
     
@@ -1536,6 +1590,9 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
 
     if (structure(vm)->hijacksIndexingHeader())
         return nullptr;
+
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
     
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES:
@@ -1571,6 +1628,9 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm)
 
 ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm)
 {
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     switch (indexingType()) {
     case ALL_BLANK_INDEXING_TYPES: {
         createArrayStorage(vm, 0, 0);
@@ -1602,6 +1662,9 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(V
 
 void JSObject::switchToSlowPutArrayStorage(VM& vm)
 {
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     switch (indexingType()) {
     case ArrayClass:
         ensureArrayStorage(vm);
@@ -1861,30 +1924,48 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper
 
 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
 {
+    VM& vm = exec->vm();
     JSObject* thisObject = jsCast<JSObject*>(cell);
     
     if (i > MAX_ARRAY_INDEX)
-        return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i));
+        return thisObject->methodTable(vm)->deleteProperty(thisObject, exec, Identifier::from(exec, i));
     
-    switch (thisObject->indexingType()) {
+    switch (thisObject->indexingMode()) {
     case ALL_BLANK_INDEXING_TYPES:
     case ALL_UNDECIDED_INDEXING_TYPES:
         return true;
-        
-    case ALL_INT32_INDEXING_TYPES:
-    case ALL_CONTIGUOUS_INDEXING_TYPES: {
+
+    case CopyOnWriteArrayWithInt32:
+    case CopyOnWriteArrayWithContiguous: {
+        Butterfly* butterfly = thisObject->butterfly();
+        if (i >= butterfly->vectorLength())
+            return true;
+        thisObject->convertFromCopyOnWrite(vm);
+        FALLTHROUGH;
+    }
+
+    case ALL_WRITABLE_INT32_INDEXING_TYPES:
+    case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES: {
         Butterfly* butterfly = thisObject->butterfly();
         if (i >= butterfly->vectorLength())
             return true;
-        butterfly->contiguous().at(i).clear();
+        butterfly->contiguous().at(thisObject, i).clear();
         return true;
     }
-        
-    case ALL_DOUBLE_INDEXING_TYPES: {
+
+    case CopyOnWriteArrayWithDouble: {
         Butterfly* butterfly = thisObject->butterfly();
         if (i >= butterfly->vectorLength())
             return true;
-        butterfly->contiguousDouble().at(i) = PNaN;
+        thisObject->convertFromCopyOnWrite(vm);
+        FALLTHROUGH;
+    }
+
+    case ALL_WRITABLE_DOUBLE_INDEXING_TYPES: {
+        Butterfly* butterfly = thisObject->butterfly();
+        if (i >= butterfly->vectorLength())
+            return true;
+        butterfly->contiguousDouble().at(thisObject, i) = PNaN;
         return true;
     }
         
@@ -2185,7 +2266,7 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa
             Butterfly* butterfly = object->butterfly();
             unsigned usedLength = butterfly->publicLength();
             for (unsigned i = 0; i < usedLength; ++i) {
-                if (!butterfly->contiguous().at(i))
+                if (!butterfly->contiguous().at(object, i))
                     continue;
                 propertyNames.add(i);
             }
@@ -2196,7 +2277,7 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa
             Butterfly* butterfly = object->butterfly();
             unsigned usedLength = butterfly->publicLength();
             for (unsigned i = 0; i < usedLength; ++i) {
-                double value = butterfly->contiguousDouble().at(i);
+                double value = butterfly->contiguousDouble().at(object, i);
                 if (value != value)
                     continue;
                 propertyNames.add(i);
@@ -2404,7 +2485,7 @@ bool JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa
 
 ALWAYS_INLINE static bool canDoFastPutDirectIndex(VM& vm, JSObject* object)
 {
-    return isJSArray(object)
+    return (isJSArray(object) && !isCopyOnWrite(object->indexingMode()))
         || jsDynamicCast<JSFinalObject*>(vm, object)
         || TypeInfo::isArgumentsType(object->type());
 }
@@ -2417,6 +2498,9 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P
 
     ASSERT(index <= MAX_ARRAY_INDEX);
 
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     if (!inSparseIndexingMode()) {
         // Fast case: we're putting a regular property to a regular array
         // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false
@@ -2598,6 +2682,7 @@ bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
     ASSERT((indexingType() & IndexingShapeMask) == indexingShape);
     ASSERT(!indexingShouldBeSparse());
 
@@ -2630,19 +2715,19 @@ bool JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un
     switch (indexingShape) {
     case Int32Shape:
         ASSERT(value.isInt32());
-        butterfly->contiguous().at(i).setWithoutWriteBarrier(value);
+        butterfly->contiguous().at(this, i).setWithoutWriteBarrier(value);
         return true;
         
     case DoubleShape: {
         ASSERT(value.isNumber());
         double valueAsDouble = value.asNumber();
         ASSERT(valueAsDouble == valueAsDouble);
-        butterfly->contiguousDouble().at(i) = valueAsDouble;
+        butterfly->contiguousDouble().at(this, i) = valueAsDouble;
         return true;
     }
         
     case ContiguousShape:
-        butterfly->contiguous().at(i).set(vm, this, value);
+        butterfly->contiguous().at(this, i).set(vm, this, value);
         return true;
         
     default:
@@ -2661,6 +2746,7 @@ bool JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, uns
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
+    ASSERT(!isCopyOnWrite(indexingMode()));
     // i should be a valid array index that is outside of the current vector.
     ASSERT(i <= MAX_ARRAY_INDEX);
     ASSERT(i >= storage->vectorLength());
@@ -2733,6 +2819,8 @@ bool JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue
 {
     VM& vm = exec->vm();
 
+    RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(!isCopyOnWrite(indexingMode()));
+
     // i should be a valid array index that is outside of the current vector.
     ASSERT(i <= MAX_ARRAY_INDEX);
     
@@ -3071,12 +3159,12 @@ unsigned JSObject::countElements(Butterfly* butterfly)
         switch (indexingShape) {
         case Int32Shape:
         case ContiguousShape:
-            if (butterfly->contiguous().at(i))
+            if (butterfly->contiguous().at(this, i))
                 numValues++;
             break;
             
         case DoubleShape: {
-            double value = butterfly->contiguousDouble().at(i);
+            double value = butterfly->contiguousDouble().at(this, i);
             if (value == value)
                 numValues++;
             break;
@@ -3173,12 +3261,15 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength)
 
 bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
 {
+    if (isCopyOnWrite(indexingMode()))
+        convertFromCopyOnWrite(vm);
+
     Butterfly* butterfly = this->butterfly();
     
     ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
     ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
     ASSERT(length > butterfly->vectorLength());
-    
+
     unsigned oldVectorLength = butterfly->vectorLength();
     unsigned newVectorLength;
     
@@ -3195,7 +3286,7 @@ bool JSObject::ensureLengthSlow(VM& vm, unsigned length)
         newVectorLength = availableOldLength;
     } else {
         newVectorLength = Butterfly::optimalContiguousVectorLength(
-            propertyCapacity, std::min(length << 1, MAX_STORAGE_VECTOR_LENGTH));
+            propertyCapacity, std::min(length * 2, MAX_STORAGE_VECTOR_LENGTH));
         butterfly = butterfly->growArrayRight(
             vm, this, structure, propertyCapacity, true,
             oldVectorLength * sizeof(EncodedJSValue),
@@ -3584,7 +3675,7 @@ uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
         Butterfly* butterfly = object->butterfly();
         unsigned usedLength = butterfly->publicLength();
         for (unsigned i = 0; i < usedLength; ++i) {
-            if (!butterfly->contiguous().at(i))
+            if (!butterfly->contiguous().at(object, i))
                 return 0;
         }
         return usedLength;
@@ -3594,7 +3685,7 @@ uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object)
         Butterfly* butterfly = object->butterfly();
         unsigned usedLength = butterfly->publicLength();
         for (unsigned i = 0; i < usedLength; ++i) {
-            double value = butterfly->contiguousDouble().at(i);
+            double value = butterfly->contiguousDouble().at(object, i);
             if (value != value)
                 return 0;
         }
index c811235..4a6128a 100644 (file)
@@ -220,16 +220,18 @@ public:
     bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
     {
         auto canSetIndexQuicklyForPutDirect = [&] () -> bool {
-            switch (indexingType()) {
+            switch (indexingMode()) {
             case ALL_BLANK_INDEXING_TYPES:
             case ALL_UNDECIDED_INDEXING_TYPES:
                 return false;
-            case ALL_INT32_INDEXING_TYPES:
-            case ALL_DOUBLE_INDEXING_TYPES:
-            case ALL_CONTIGUOUS_INDEXING_TYPES:
+            case ALL_WRITABLE_INT32_INDEXING_TYPES:
+            case ALL_WRITABLE_DOUBLE_INDEXING_TYPES:
+            case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
                 return propertyName < m_butterfly->vectorLength();
             default:
+                if (isCopyOnWrite(indexingMode()))
+                    return false;
                 RELEASE_ASSERT_NOT_REACHED();
                 return false;
             }
@@ -269,11 +271,11 @@ public:
             return false;
         case ALL_INT32_INDEXING_TYPES:
         case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return i < butterfly->vectorLength() && butterfly->contiguous().at(i);
+            return i < butterfly->vectorLength() && butterfly->contiguous().at(this, i);
         case ALL_DOUBLE_INDEXING_TYPES: {
             if (i >= butterfly->vectorLength())
                 return false;
-            double value = butterfly->contiguousDouble().at(i);
+            double value = butterfly->contiguousDouble().at(this, i);
             if (value != value)
                 return false;
             return true;
@@ -291,11 +293,11 @@ public:
         Butterfly* butterfly = this->butterfly();
         switch (indexingType()) {
         case ALL_INT32_INDEXING_TYPES:
-            return jsNumber(butterfly->contiguous().at(i).get().asInt32());
+            return jsNumber(butterfly->contiguous().at(this, i).get().asInt32());
         case ALL_CONTIGUOUS_INDEXING_TYPES:
-            return butterfly->contiguous().at(i).get();
+            return butterfly->contiguous().at(this, i).get();
         case ALL_DOUBLE_INDEXING_TYPES:
-            return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble().at(i));
+            return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble().at(this, i));
         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
             return butterfly->arrayStorage()->m_vector[i].get();
         default:
@@ -313,19 +315,19 @@ public:
             break;
         case ALL_INT32_INDEXING_TYPES:
             if (i < butterfly->publicLength()) {
-                JSValue result = butterfly->contiguous().at(i).get();
+                JSValue result = butterfly->contiguous().at(this, i).get();
                 ASSERT(result.isInt32() || !result);
                 return result;
             }
             break;
         case ALL_CONTIGUOUS_INDEXING_TYPES:
             if (i < butterfly->publicLength())
-                return butterfly->contiguous().at(i).get();
+                return butterfly->contiguous().at(this, i).get();
             break;
         case ALL_DOUBLE_INDEXING_TYPES: {
             if (i >= butterfly->publicLength())
                 break;
-            double result = butterfly->contiguousDouble().at(i);
+            double result = butterfly->contiguousDouble().at(this, i);
             if (result != result)
                 break;
             return JSValue(JSValue::EncodeAsDouble, result);
@@ -361,13 +363,13 @@ public:
     bool canSetIndexQuickly(unsigned i)
     {
         Butterfly* butterfly = this->butterfly();
-        switch (indexingType()) {
+        switch (indexingMode()) {
         case ALL_BLANK_INDEXING_TYPES:
         case ALL_UNDECIDED_INDEXING_TYPES:
             return false;
-        case ALL_INT32_INDEXING_TYPES:
-        case ALL_DOUBLE_INDEXING_TYPES:
-        case ALL_CONTIGUOUS_INDEXING_TYPES:
+        case ALL_WRITABLE_INT32_INDEXING_TYPES:
+        case ALL_WRITABLE_DOUBLE_INDEXING_TYPES:
+        case ALL_WRITABLE_CONTIGUOUS_INDEXING_TYPES:
         case NonArrayWithArrayStorage:
         case ArrayWithArrayStorage:
             return i < butterfly->vectorLength();
@@ -376,6 +378,8 @@ public:
             return i < butterfly->arrayStorage()->vectorLength()
                 && !!butterfly->arrayStorage()->m_vector[i];
         default:
+            if (isCopyOnWrite(indexingMode()))
+                return false;
             RELEASE_ASSERT_NOT_REACHED();
             return false;
         }
@@ -384,6 +388,7 @@ public:
     void setIndexQuickly(VM& vm, unsigned i, JSValue v)
     {
         Butterfly* butterfly = m_butterfly.get();
+        ASSERT(!isCopyOnWrite(indexingMode()));
         switch (indexingType()) {
         case ALL_INT32_INDEXING_TYPES: {
             ASSERT(i < butterfly->vectorLength());
@@ -395,7 +400,7 @@ public:
         }
         case ALL_CONTIGUOUS_INDEXING_TYPES: {
             ASSERT(i < butterfly->vectorLength());
-            butterfly->contiguous().at(i).set(vm, this, v);
+            butterfly->contiguous().at(this, i).set(vm, this, v);
             if (i >= butterfly->publicLength())
                 butterfly->setPublicLength(i + 1);
             break;
@@ -411,7 +416,7 @@ public:
                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
                 return;
             }
-            butterfly->contiguousDouble().at(i) = value;
+            butterfly->contiguousDouble().at(this, i) = value;
             if (i >= butterfly->publicLength())
                 butterfly->setPublicLength(i + 1);
             break;
@@ -461,7 +466,7 @@ public:
         case ALL_CONTIGUOUS_INDEXING_TYPES: {
             ASSERT(i < butterfly->publicLength());
             ASSERT(i < butterfly->vectorLength());
-            butterfly->contiguous().at(i).set(vm, this, v);
+            butterfly->contiguous().at(this, i).set(vm, this, v);
             break;
         }
         case ALL_DOUBLE_INDEXING_TYPES: {
@@ -476,7 +481,7 @@ public:
                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
                 return;
             }
-            butterfly->contiguousDouble().at(i) = value;
+            butterfly->contiguousDouble().at(this, i) = value;
             break;
         }
         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
@@ -515,7 +520,7 @@ public:
         case ALL_CONTIGUOUS_INDEXING_TYPES: {
             ASSERT(i < butterfly->publicLength());
             ASSERT(i < butterfly->vectorLength());
-            butterfly->contiguous().at(i).setWithoutWriteBarrier(v);
+            butterfly->contiguous().at(this, i).setWithoutWriteBarrier(v);
             break;
         }
         case ALL_DOUBLE_INDEXING_TYPES: {
@@ -524,7 +529,7 @@ public:
             RELEASE_ASSERT(v.isNumber());
             double value = v.asNumber();
             RELEASE_ASSERT(value == value);
-            butterfly->contiguousDouble().at(i) = value;
+            butterfly->contiguousDouble().at(this, i) = value;
             break;
         }
         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
@@ -817,34 +822,34 @@ public:
     // indexing should be sparse, we're having a bad time, or because
     // we already have a more general form of storage (double,
     // contiguous, array storage).
-    ContiguousJSValues ensureInt32(VM& vm)
+    ContiguousJSValues ensureWritableInt32(VM& vm)
     {
-        if (LIKELY(hasInt32(indexingType())))
+        if (LIKELY(hasInt32(indexingType()) && !isCopyOnWrite(indexingMode())))
             return m_butterfly->contiguousInt32();
             
-        return ensureInt32Slow(vm);
+        return ensureWritableInt32Slow(vm);
     }
         
     // Returns 0 if double storage cannot be created - either because
     // indexing should be sparse, we're having a bad time, or because
     // we already have a more general form of storage (contiguous,
     // or array storage).
-    ContiguousDoubles ensureDouble(VM& vm)
+    ContiguousDoubles ensureWritableDouble(VM& vm)
     {
-        if (LIKELY(hasDouble(indexingType())))
+        if (LIKELY(hasDouble(indexingType()) && !isCopyOnWrite(indexingMode())))
             return m_butterfly->contiguousDouble();
             
-        return ensureDoubleSlow(vm);
+        return ensureWritableDoubleSlow(vm);
     }
         
     // Returns 0 if contiguous storage cannot be created - either because
     // indexing should be sparse or because we're having a bad time.
-    ContiguousJSValues ensureContiguous(VM& vm)
+    ContiguousJSValues ensureWritableContiguous(VM& vm)
     {
-        if (LIKELY(hasContiguous(indexingType())))
+        if (LIKELY(hasContiguous(indexingType()) && !isCopyOnWrite(indexingMode())))
             return m_butterfly->contiguous();
             
-        return ensureContiguousSlow(vm);
+        return ensureWritableContiguousSlow(vm);
     }
 
     // Ensure that the object is in a mode where it has array storage. Use
@@ -931,11 +936,13 @@ protected:
     ContiguousJSValues createInitialInt32(VM&, unsigned length);
     ContiguousDoubles createInitialDouble(VM&, unsigned length);
     ContiguousJSValues createInitialContiguous(VM&, unsigned length);
-        
+
     void convertUndecidedForValue(VM&, JSValue);
     void createInitialForValueAndSet(VM&, unsigned index, JSValue);
     void convertInt32ForValue(VM&, JSValue);
-        
+    void convertDoubleForValue(VM&, JSValue);
+    void convertFromCopyOnWrite(VM&);
+
     static Butterfly* createArrayStorageButterfly(VM&, JSCell* intendedOwner, Structure*, unsigned length, unsigned vectorLength, Butterfly* oldButterfly = nullptr);
     ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength);
     ArrayStorage* createInitialArrayStorage(VM&);
@@ -950,7 +957,7 @@ protected:
     ContiguousJSValues convertInt32ToContiguous(VM&);
     ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition);
     ArrayStorage* convertInt32ToArrayStorage(VM&);
-    
+
     ContiguousJSValues convertDoubleToContiguous(VM&);
     ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition);
     ArrayStorage* convertDoubleToArrayStorage(VM&);
@@ -983,7 +990,7 @@ protected:
         RELEASE_ASSERT(length <= MAX_STORAGE_VECTOR_LENGTH);
         ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
 
-        if (m_butterfly->vectorLength() < length) {
+        if (m_butterfly->vectorLength() < length || isCopyOnWrite(indexingMode())) {
             if (!ensureLengthSlow(vm, length))
                 return false;
         }
@@ -1052,9 +1059,9 @@ private:
         
     bool ensureLengthSlow(VM&, unsigned length);
         
-    ContiguousJSValues ensureInt32Slow(VM&);
-    ContiguousDoubles ensureDoubleSlow(VM&);
-    ContiguousJSValues ensureContiguousSlow(VM&);
+    ContiguousJSValues ensureWritableInt32Slow(VM&);
+    ContiguousDoubles ensureWritableDoubleSlow(VM&);
+    ContiguousJSValues ensureWritableContiguousSlow(VM&);
     JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&);
 
     PropertyOffset prepareToPutDirectWithoutTransition(VM&, PropertyName, unsigned attributes, StructureID, Structure*);
index 4b0c864..527fc22 100644 (file)
@@ -275,6 +275,7 @@ ALWAYS_INLINE bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName
     StructureID structureID = this->structureID();
     Structure* structure = vm.heap.structureIDTable().get(structureID);
     if (structure->isDictionary()) {
+        ASSERT(!isCopyOnWrite(indexingMode()));
         ASSERT(!structure->hasInferredTypes());
         
         unsigned currentAttributes;
index d86dc3b..afa7dc2 100644 (file)
@@ -47,6 +47,7 @@ enum JSType : uint8_t {
     CodeBlockType,
 
     JSFixedArrayType,
+    JSImmutableButterflyType,
     JSSourceCodeType,
     JSScriptFetcherType,
     JSScriptFetchParametersType,
index 940c2e5..d1a1cbe 100644 (file)
@@ -51,7 +51,7 @@ ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(ObjectInitializa
 
     unsigned i = (createUninitialized ? initialLength : 0);
     for (; i < vectorLength; ++i)
-        butterfly->contiguous().at(i).clear();
+        butterfly->contiguous().atUnsafe(i).clear();
 
     JSArray* result = JSArray::createWithButterfly(vm, deferralContext, structure, butterfly);
     scope.notifyAllocated(result, createUninitialized);
index 00f87b0..c5959b2 100644 (file)
@@ -273,7 +273,7 @@ Structure::Structure(VM& vm, Structure* previous, DeferredStructureTransitionWat
     setIsAddingPropertyForTransition(false);
  
     TypeInfo typeInfo = previous->typeInfo();
-    m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingTypeIncludingHistory(), typeInfo);
+    m_blob = StructureIDBlob(vm.heap.structureIDTable().allocateID(this), previous->indexingModeIncludingHistory(), typeInfo);
     m_outOfLineTypeFlags = typeInfo.outOfLineTypeFlags();
 
     ASSERT(!previous->typeInfo().structureIsImmortal());
@@ -473,6 +473,7 @@ Structure* Structure::addNewPropertyTransition(VM& vm, Structure* structure, Pro
     else
         maxTransitionLength = s_maxTransitionLength;
     if (structure->transitionCount() > maxTransitionLength) {
+        ASSERT(!isCopyOnWrite(structure->indexingMode()));
         Structure* transition = toCacheableDictionaryTransition(vm, structure, deferred);
         ASSERT(structure != transition);
         offset = transition->add(vm, propertyName, attributes);
@@ -496,7 +497,8 @@ Structure* Structure::addNewPropertyTransition(VM& vm, Structure* structure, Pro
         ConcurrentJSLocker locker(transition->m_lock);
         transition->setIsAddingPropertyForTransition(true);
     }
-    
+
+    transition->m_blob.setIndexingModeIncludingHistory(structure->indexingModeIncludingHistory() & ~CopyOnWrite);
     transition->m_nameInPrevious = propertyName.uid();
     transition->setAttributesInPrevious(attributes);
     transition->setPropertyTable(vm, structure->takePropertyTableOrCloneIfPinned(vm));
@@ -647,13 +649,13 @@ PropertyTable* Structure::takePropertyTableOrCloneIfPinned(VM& vm)
 Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind)
 {
     unsigned attributes = toAttributes(transitionKind);
-    IndexingType indexingTypeIncludingHistory = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind);
+    IndexingType indexingModeIncludingHistory = newIndexingType(structure->indexingModeIncludingHistory(), transitionKind);
     
     if (changesIndexingType(transitionKind)) {
         if (JSGlobalObject* globalObject = structure->m_globalObject.get()) {
             if (globalObject->isOriginalArrayStructure(structure)) {
-                Structure* result = globalObject->originalArrayStructureForIndexingType(indexingTypeIncludingHistory);
-                if (result->indexingTypeIncludingHistory() == indexingTypeIncludingHistory) {
+                Structure* result = globalObject->originalArrayStructureForIndexingType(indexingModeIncludingHistory);
+                if (result->indexingModeIncludingHistory() == indexingModeIncludingHistory) {
                     structure->didTransitionFromThisStructure();
                     return result;
                 }
@@ -664,7 +666,7 @@ Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPro
     Structure* existingTransition;
     if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) {
         ASSERT(existingTransition->attributesInPrevious() == attributes);
-        ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingTypeIncludingHistory);
+        ASSERT(existingTransition->indexingModeIncludingHistory() == indexingModeIncludingHistory);
         return existingTransition;
     }
     
@@ -672,7 +674,7 @@ Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPro
     
     Structure* transition = create(vm, structure);
     transition->setAttributesInPrevious(attributes);
-    transition->m_blob.setIndexingTypeIncludingHistory(indexingTypeIncludingHistory);
+    transition->m_blob.setIndexingModeIncludingHistory(indexingModeIncludingHistory);
     
     if (preventsExtensions(transitionKind))
         transition->setDidPreventExtensions(true);
index 6bf4cec..503ce46 100644 (file)
@@ -258,12 +258,13 @@ public:
     TypeInfo typeInfo() const { return m_blob.typeInfo(m_outOfLineTypeFlags); }
     bool isObject() const { return typeInfo().isObject(); }
 
-    IndexingType indexingType() const { return m_blob.indexingTypeIncludingHistory() & AllArrayTypes; }
-    IndexingType indexingTypeIncludingHistory() const { return m_blob.indexingTypeIncludingHistory(); }
+    IndexingType indexingType() const { return m_blob.indexingModeIncludingHistory() & AllWritableArrayTypes; }
+    IndexingType indexingMode() const  { return m_blob.indexingModeIncludingHistory() & AllArrayTypes; }
+    IndexingType indexingModeIncludingHistory() const { return m_blob.indexingModeIncludingHistory(); }
         
     bool mayInterceptIndexedAccesses() const
     {
-        return !!(indexingTypeIncludingHistory() & MayHaveIndexedAccessors);
+        return !!(indexingModeIncludingHistory() & MayHaveIndexedAccessors);
     }
         
     bool holesMustForwardToPrototype(VM&, JSObject*) const;
@@ -506,9 +507,9 @@ public:
         return OBJECT_OFFSETOF(Structure, m_classInfo);
     }
         
-    static ptrdiff_t indexingTypeIncludingHistoryOffset()
+    static ptrdiff_t indexingModeIncludingHistoryOffset()
     {
-        return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeIncludingHistoryOffset();
+        return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingModeIncludingHistoryOffset();
     }
     
     static ptrdiff_t propertyTableUnsafeOffset()
index 51564ca..066c8ad 100644 (file)
@@ -40,10 +40,10 @@ public:
         u.doubleWord = 0xbbadbeef;
     }
 
-    StructureIDBlob(StructureID structureID, IndexingType indexingTypeIncludingHistory, const TypeInfo& typeInfo)
+    StructureIDBlob(StructureID structureID, IndexingType indexingModeIncludingHistory, const TypeInfo& typeInfo)
     {
         u.fields.structureID = structureID;
-        u.fields.indexingTypeIncludingHistory = indexingTypeIncludingHistory;
+        u.fields.indexingModeIncludingHistory = indexingModeIncludingHistory;
         u.fields.type = typeInfo.type();
         u.fields.inlineTypeFlags = typeInfo.inlineTypeFlags();
         u.fields.defaultCellState = CellState::DefinitelyWhite;
@@ -52,8 +52,8 @@ public:
     void operator=(const StructureIDBlob& other) { u.doubleWord = other.u.doubleWord; }
     
     StructureID structureID() const { return u.fields.structureID; }
-    IndexingType indexingTypeIncludingHistory() const { return u.fields.indexingTypeIncludingHistory; }
-    void setIndexingTypeIncludingHistory(IndexingType indexingTypeIncludingHistory) { u.fields.indexingTypeIncludingHistory = indexingTypeIncludingHistory; }
+    IndexingType indexingModeIncludingHistory() const { return u.fields.indexingModeIncludingHistory; }
+    void setIndexingModeIncludingHistory(IndexingType indexingModeIncludingHistory) { u.fields.indexingModeIncludingHistory = indexingModeIncludingHistory; }
     JSType type() const { return u.fields.type; }
     TypeInfo::InlineTypeFlags inlineTypeFlags() const { return u.fields.inlineTypeFlags; }
     
@@ -67,16 +67,16 @@ public:
         return OBJECT_OFFSETOF(StructureIDBlob, u.fields.structureID);
     }
 
-    static ptrdiff_t indexingTypeIncludingHistoryOffset()
+    static ptrdiff_t indexingModeIncludingHistoryOffset()
     {
-        return OBJECT_OFFSETOF(StructureIDBlob, u.fields.indexingTypeIncludingHistory);
+        return OBJECT_OFFSETOF(StructureIDBlob, u.fields.indexingModeIncludingHistory);
     }
 
 private:
     union {
         struct {
             StructureID structureID;
-            IndexingType indexingTypeIncludingHistory;
+            IndexingType indexingModeIncludingHistory;
             JSType type;
             TypeInfo::InlineTypeFlags inlineTypeFlags;
             CellState defaultCellState;
index 68a4689..437641e 100644 (file)
@@ -83,23 +83,23 @@ inline IndexingType newIndexingType(IndexingType oldType, NonPropertyTransition
         ASSERT(!hasIndexedProperties(oldType));
         return oldType | UndecidedShape;
     case NonPropertyTransition::AllocateInt32:
-        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType));
-        return (oldType & ~IndexingShapeMask) | Int32Shape;
+        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || oldType == CopyOnWriteArrayWithInt32);
+        return (oldType & ~IndexingShapeAndWritabilityMask) | Int32Shape;
     case NonPropertyTransition::AllocateDouble:
-        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType));
-        return (oldType & ~IndexingShapeMask) | DoubleShape;
+        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || oldType == CopyOnWriteArrayWithDouble);
+        return (oldType & ~IndexingShapeAndWritabilityMask) | DoubleShape;
     case NonPropertyTransition::AllocateContiguous:
-        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType));
-        return (oldType & ~IndexingShapeMask) | ContiguousShape;
+        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || oldType == CopyOnWriteArrayWithContiguous);
+        return (oldType & ~IndexingShapeAndWritabilityMask) | ContiguousShape;
     case NonPropertyTransition::AllocateArrayStorage:
         ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType));
-        return (oldType & ~IndexingShapeMask) | ArrayStorageShape;
+        return (oldType & ~IndexingShapeAndWritabilityMask) | ArrayStorageShape;
     case NonPropertyTransition::AllocateSlowPutArrayStorage:
-        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType));
-        return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
+        ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType));
+        return (oldType & ~IndexingShapeAndWritabilityMask) | SlowPutArrayStorageShape;
     case NonPropertyTransition::SwitchToSlowPutArrayStorage:
         ASSERT(hasArrayStorage(oldType));
-        return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape;
+        return (oldType & ~IndexingShapeAndWritabilityMask) | SlowPutArrayStorageShape;
     case NonPropertyTransition::AddIndexedAccessors:
         return oldType | MayHaveIndexedAccessors;
     default:
index d69b1e1..89d0f30 100644 (file)
@@ -85,6 +85,7 @@
 #include "JSFixedArray.h"
 #include "JSFunction.h"
 #include "JSGlobalObjectFunctions.h"
+#include "JSImmutableButterfly.h"
 #include "JSInternalPromiseDeferred.h"
 #include "JSLock.h"
 #include "JSMap.h"
@@ -382,6 +383,11 @@ VM::VM(VMType vmType, HeapType heapType)
     symbolStructure.set(*this, Symbol::createStructure(*this, 0, jsNull()));
     symbolTableStructure.set(*this, SymbolTable::createStructure(*this, 0, jsNull()));
     fixedArrayStructure.set(*this, JSFixedArray::createStructure(*this, 0, jsNull()));
+
+    immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithInt32) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, 0, jsNull(), CopyOnWriteArrayWithInt32));
+    immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithDouble) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, 0, jsNull(), CopyOnWriteArrayWithDouble));
+    immutableButterflyStructures[arrayIndexFromIndexingType(CopyOnWriteArrayWithContiguous) - NumberOfIndexingShapes].set(*this, JSImmutableButterfly::createStructure(*this, 0, jsNull(), CopyOnWriteArrayWithContiguous));
+
     sourceCodeStructure.set(*this, JSSourceCode::createStructure(*this, 0, jsNull()));
     scriptFetcherStructure.set(*this, JSScriptFetcher::createStructure(*this, 0, jsNull()));
     scriptFetchParametersStructure.set(*this, JSScriptFetchParameters::createStructure(*this, 0, jsNull()));
index 24a91cd..cb52270 100644 (file)
@@ -465,6 +465,7 @@ public:
     Strong<Structure> symbolStructure;
     Strong<Structure> symbolTableStructure;
     Strong<Structure> fixedArrayStructure;
+    Strong<Structure> immutableButterflyStructures[NumberOfCopyOnWriteIndexingModes];
     Strong<Structure> sourceCodeStructure;
     Strong<Structure> scriptFetcherStructure;
     Strong<Structure> scriptFetchParametersStructure;
index c4a835c..6cac44d 100644 (file)
@@ -1,3 +1,14 @@
+2018-05-22  Keith Miller  <keith_miller@apple.com>
+
+        We should have a CoW storage for NewArrayBuffer arrays.
+        https://bugs.webkit.org/show_bug.cgi?id=185003
+
+        Reviewed by Filip Pizlo.
+
+        * bindings/js/JSDOMConvertSequences.h:
+        (WebCore::Detail::NumericSequenceConverter::convertArray):
+        (WebCore::Detail::SequenceConverter::convertArray):
+
 2018-05-22  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r232052.
index b071802..3ae939e 100644 (file)
@@ -92,7 +92,7 @@ struct NumericSequenceConverter {
     {
         if (indexingType == JSC::Int32Shape) {
             for (unsigned i = 0; i < length; i++) {
-                auto indexValue = array->butterfly()->contiguousInt32().at(i).get();
+                auto indexValue = array->butterfly()->contiguousInt32().at(array, i).get();
                 ASSERT(!indexValue || indexValue.isInt32());
                 if (!indexValue)
                     result.uncheckedAppend(0);
@@ -104,7 +104,7 @@ struct NumericSequenceConverter {
 
         ASSERT(indexingType == JSC::DoubleShape);
         for (unsigned i = 0; i < length; i++) {
-            auto doubleValue = array->butterfly()->contiguousDouble().at(i);
+            double doubleValue = array->butterfly()->contiguousDouble().at(array, i);
             if (std::isnan(doubleValue))
                 result.uncheckedAppend(0);
             else {
@@ -210,7 +210,7 @@ struct SequenceConverter {
 
         if (indexingType == JSC::ContiguousShape) {
             for (unsigned i = 0; i < length; i++) {
-                auto indexValue = array->butterfly()->contiguous().at(i).get();
+                auto indexValue = array->butterfly()->contiguous().at(array, i).get();
                 if (!indexValue)
                     indexValue = JSC::jsUndefined();